Wednesday 24 February 2016

Instant gratification and a quick Fixx

On the 8th of this month, there was an article in the Straits Times detailing a possible shortfall of graduates in the IT industry by 2017. One quote from the article stands out.

"When fresh graduates come to us, the coding languages they know have already been phased out"
- Mr Darren Lee, managing director of Fixx Digital.

Now admittedly I don't know the finer details of how Mr Lee runs his company, but this strikes me as fundamentally wrong and shockingly short-sighted. Programming languages, or as Mr Lee puts it, coding languages, are nothing more than tools. If you hire people based on their knowledge of a tool; when that tool inevitably becomes obsolete, these people become obsolete as well.

But they can learn new tools, right? 

That is precisely the point. Hire people who have the foundation to learn new tools, not people who know one particular set of tools. Arrays, iterative loops, MVC, human-computer interaction - what do they have in common? They are concepts. Concepts that have changed little since the day they were introduced. Languages evolve, but they will always go back to those basics.

When you do object-oriented programming, whether it's in Java, C++ or PHP, you are dealing with constructs, encapsulation and inheritance. The only thing that differs in syntax.

When you create a website, whether it's in Twitter Bootstrap or hand-coded HTML5, it still deals with HTTP and W3c compliance still applies.

No matter what version of Android API you're using to code that mobile app, the standard life cycle of an app has not changed since day one.

Whatever fancy new databases emerge on the market, they're more than likely to be queried via SQL.

There is a good reason why schools are not teaching students the latest, greatest breakthroughs in technology. Because it just isn't feasible. Technology evolves faster than you can establish a curriculum for. All you can do, is give students the foundation to build on. I may not be the biggest fan of Singapore's educational system, but let's be fair. If a school, any school, were to insist that their IT students are versed only in the latest technology, the students would never graduate. In a sense, that is true of the average developer - we never stop studying.

Learn to invest in people instead

You only want to hire people who know your stuff and can get cracking right away without having to learn it on the job? No problemo. But you're taking a huge risk here. If you hire a guy who knows the new fancy tools you've built your company around, all will be fine and dandy... until technology evolves. And this being technology, it will evolve. You can take that to the bank.

Don't hire tool users. Hire developers. 

Developers are constantly upgrading. It's part and parcel of this industry. Developers know the basics. It is the new emerging tools they need to familiarize themselves with. There is no such animal as a developer who already knows everything he needs to know and can get by without learning new things.

Companies that are not in the web industry will not understand this, nor are they expected to. They will hire based on the tools that their candidate should know now. Quite understandable. But Fixx Digital is a web design company. What's its excuse?

Get rid of that unhealthy Fixxation today.
T___T

Saturday 20 February 2016

Rise of the Handphone Vigilante

Last month in Parliament, on 26th of January, a statement was made, of which I have mixed feelings regarding. MP of Marine Parade GRC, Mr Seah Kian Peng, addressed an incident where an argument between a civil servant and a constituent was caught on camera and went viral.

"I have noticed a recent rise of self-administered vigilante justice among members of the public. The weapon of choice - the handphone; judge and jury - the social media public. This has to change. On the flip side, I have also seen some very bad responses by civil servants, the very few which give the Government as a whole a bad name. This is not who we are. This is not what we dream of becoming."
- Mr Seah Kian Peng

I both agree and disagree with that statement. In order to clarify why, let's first take a look at the phenomenon of Handphone Vigilantism.

What is Vigilantism?

A vigilante is generally defined as "a member of a volunteer committee organized to suppress and punish crime summarily (as when the processes of law are viewed as inadequate)", or more broadly, "a self-appointed doer of justice".

Batman, Spider-man, the Punisher and other comic book heroes fit this profile to cartoonish extremes - they fight crime outside of the boundaries of the law.

What is Cyber Vigilantism?

Wikipedia defines it as "the phenomenon of vigilante acts taken through the Internet (the communication network or its service providers) or carried out using applications (World Wide Web, e-mail) that depend on the Internet".

The Hacker Group Anonymous certainly qualifies, with its activism in counter-terrorism.

Perverted-Justice is a vigilante group that goes after pedophiles online.

Cyber Vigilantism in Singapore

On the other hand, here in sunny Singapore, this is what passes for cyber vigilantism - online lynch mobs.

Amy Cheong became famous in October 2012 due to a racist rant on Facebook. The resulting uproar, which included death threats and other verbal abuse, resulted in her leaving the country.

An online group SMRT Feedback pranked Jover Chew for his cheating cases in the Sim Lim Square saga, which I discussed here last month, and worse - revealed his address and phone number.

Before the case above, Anton Casey's details were leaked by this very same group and he was hounded out of Singapore by enraged netizens.

There are other cases, but to list them all would be excessive at this point.

Am I alone in thinking that Singaporeans come off as silly and pathetic in comparison with their counterparts in the USA? In the USA, vigilantes take down terrorists and pedophiles. In Singapore, we take down racist ranters, crass idiots and scam artists.

Handphone Vigilantism

Handphone Vigilantism is a subset of Cyber Vigilantism. Instead of a computer, the vigilante uses a mobile phone. From this tool, the would-be executor of justice takes photos or a video of the offender and uploads it on a Social Media platform such as STOMP or Facebook, where reactions range from moral outrage to an outright witch hunt.

A few years ago in 2012, news of a spat between a young woman (known as "Huina") and an old lady on the MRT raged through the internet, due to this video. Note that midway through the video, the old lady whips out her mobile phone and threatens to take a photo of the young woman and shame her online.

More recently, a disagreement between a Ms Celine Chia and a Mr Syn Kok Meng made the news, again, in the MRT, when Mr Syn declined to give up his seat upon request by Ms Chia.

What do these incidents have in common? Other than the fact that these incidents (and Anton Casey's case) happened on the MRT, you mean? To be honest, I've never quite figured this out. What is it about train commuting that brings out our inner vigilante? More food for thought, for another day.

Anyway - in these cases, the handphone was used as a tool to shame people. In the first case, the old woman who took the seat threatened to take Huina's picture and use it to shame her in the papers. In the second, Celine Chia did just that, posting Syn's picture online, along with a dramatic and invective-filled, decidedly one-sided account as to what had transpired, in order to get people on her side to join in flaming Syn.

How is this different from sharing someone's post on Facebook and criticizing it?

This has been done on occasion, when Xiaxue makes a blogpost, for example. Someone sees it, and shares it online, publicly disagreeing with that post. If it stops there, it's not cyber vigilantism.

The difference is, the original poster's intention is to share an opinion on public domain. And if one wishes to share an opinion, he or she should be prepared that there will be people who will publicly disagree. It's hard to make an argument for an ill-considered post. This is the Internet, and in order for the post to appear, one has to first type it. This requires a premeditated effort. The argument "I got caught up in the emotion" doesn't cut it. You had time to think it through; you merely chose not to.

In Syn's case, he was not looking to share his opinion - he was minding his own business and defending his consumer rights (albeit in a decidedly dickish manner) upon being bombarded with Chia's attempts to impose her moral standards on him, and didn't have time to consider his response. Chia, apparently fuelled by her righteous indignation, decided unilaterally to escalate it on Social Media.

How is this different from loan sharks publicizing details of those who owe them money?

It's not.

This is public shaming in a bid to put pressure on those unfortunate individuals. This constitutes harassment, and is against the law.

Some have expressed the opinion that if you behave badly in public, you deserve to be shamed online for it. I call bullshit. Children, stop reading so many comic books. It rots your brain. Being a vigilante only looks cool in the cartoons. I'll probably be unpopular for saying this, but - Amy Cheong, Anton Casey, et al have some things in common with us - they have rights. Yes, even big-mouthed bigots and racist shrews have rights. And by participating in the witch hunt, by even encouraging it, you are trampling on those rights.

My opinion

Many have described those two incidents as cases of "handphone vigilantism", some lauding it, some decrying it. I'd like to respectfully disagree.

Sure, it all looks similar and I can certainly see how people would get confused. But make no mistake - this was not done out of any misguided sense of justice. The old lady didn't want to shame Huina because she felt Huina needed to show more respect to the elderly. She wanted to shame Huina because Huina pissed her off. Not because Huina disrespected the elderly. Because Huina disrespected her.

Likewise, Celine Chua's holier-than-thou act is just that - an act. A thin veneer of social-mindedness on top of a very simple agenda - Syn pissed her off, and he had to pay. It was personal.

This is not vigilantism. Vigilantism on its own is repugnant enough, but this is worse. This is a personal witch hunt disguised as vigilantism.

Back to Mr Seah Kian Peng's statement...


"This is not who we are. This is not what we dream of becoming."

Why I agree with Mr Seah

This ought not to be the way.

If the person has committed a crime and you go vigilante on his ass, you are guilty of a crime. If the person has done something that is perfectly legal but contradicts your morals... who are you and why should your moral standards have any bearing on the situation? Who decides what society's moral standards are? I'll give you a clue - it won't be some jackass pursuing perceived justice with a mobile phone.

Fancy yourself one of these?

Why I disagree

Unfortunately, this is who we are. Singaporeans have grown into a timid, repressed lot who somehow come alive on the Internet due to the delusion of anonymity. We've come far as a Nation, but we, as a people, have not caught up with our blooming economy. In many ways, we're still the same kampung villagers in sarongs and stilt houses - only armed with mobile phones and the Internet instead of pitchforks and torches. And "handphone vigilantes" is exactly what some of us seem to dream of becoming. Some of us aspire to be heroes. Not the kind who run into burning buildings to save lives, but the kind who lead or incite lynch mobs to take down those they deem undesirable. Social justice warriors.

Technology's misuse

As a practitioner of technology, as someone who makes his living via Internet technology, it disturbs me profoundly to see said technology being used for things like that. Now that is disrespect. The mobile phone was not built in any one person's lifetime. It is an amalgamation of the evolved work of Alexander Graham Bell and the logical wizardry of Ada Lovelace. The phone and the computer, combined in one. How many engineers and technicians have spent their lives slaving over these technologies, refining them, and assembling them into that wondrous little piece of plastic you hold in your hand today?

Web and mobile technology have made many great advances over the years, and is still in the process of breaking new and exciting ground. And all we want to do with it is publicly shame people who piss us off? That's like buying the latest, fastest computer on the market just to surf porn.

We stand on the cusp of greatness. Please, please don't waste this on some petty garbage.

Just my train of thought,
T___T

Friday 12 February 2016

Web Tutorial: Valentine's Maze Game (Part 4/4)

Now the game works. What else can we add to it? Well, remember the ddlDifficulty drop-down list? We can spice things up a bit. The game we made was on the "Easy" setting.

How to make it more challenging?

Well, we could decrease the time allotted to 30 seconds, or something. But that's terribly unimaginative. Let's try this instead. Our lines thus far have been smooth corners or straight lines. Let's do it with jagged lines!

hard.png

Now add a new option in the ddlDifficulty drop-down list...
    <body onload="reset();">
        <div class="text_wrapper">
            Difficulty:
            <select id="ddlDifficulty">
                <option value="url(easy.png)" selected>Easy</option>
                <option value="url(hard.png)">Hard</option>
            </select>
        </div>
        <div id="pnlMessage" class="text_wrapper"></div>
        <div id="pnlTimer" class="text_wrapper">Time left: 0 seconds</div>
        <div id="board_wrapper">

        </div>
    </body>


When "Hard" is selected and "begin" is clicked, this happens!


Holy shit! Is that some serious mindfuckery, or what?!

Make it look pretty!

Now I have another image here.

vmaze.jpg

I'm going to add it to the board by altering the CSS class board_wrapper, like so.
            #board_wrapper
            {
                width:602px;
                height:402px;
                margin:50px auto 0 auto;
                border:2px solid #000000;
                background:url(vmaze.png) left top no-repeat;
                background-size:cover;
            }


Ta-daaaaaaaaa......



This adds an attractive background to your board and makes it a little less boring.

This concludes my web tutorial for Valentine's Day 2016. Have fun with the game.

Because the path of true love never did run smooth!
T___T

Tuesday 9 February 2016

Web Tutorial: Valentine's Maze Game (Part 3/4)

Still with me? Cool!

In order to win the game, the player needs to click on the tiles to rotate them till there is a clear unbroken line from the start tile to the end tile.


We'll write the code for rotating those tiles.

First, go to your resetboard() function and add these lines. Upon initialization of each tile, the rotatetile() function will be added to the onclick event of that tile.

            function resetboard()
            {
                var board=document.getElementById("board_wrapper");
                board.innerHTML="";
               
                var rowcounter=0,colcounter=0;
                var tile,tiletypeindex;

                for (var i=0;i<24;i++)
                {
                    tiletypeindex=generaterandomno(0,7);
                    objtiles[i] = {id:i, x:colcounter, y:rowcounter, n:objtiletype[tiletypeindex].n,s:objtiletype[tiletypeindex].s,e:objtiletype[tiletypeindex].e,w:objtiletype[tiletypeindex].w};

                    tile=document.createElement("div");
                    tile.id="tile"+i;

                    tile.className="tile_wrapper";
                    tile.style.marginLeft=objtiles[i].x*100+"px";
                    tile.style.marginTop=objtiles[i].y*100+"px";
                    tile.style.backgroundImage=document.getElementById("ddlDifficulty").value;
                    tile.style.backgroundPosition=objtiletype[tiletypeindex].pos;
                    tile.style.backgroundRepeat="no-repeat";
                    tile.style.transform="rotate(0deg)";
                    tile.style.webkitTransform="rotate(0deg)";
                    tile.onclick=function(){rotatetile(this,0.3);};
                    board.appendChild(tile);

                    if (colcounter % 5==0&&colcounter>0)
                    {
                        rowcounter++;
                        colcounter=0;
                    }
                    else
                    {
                        colcounter++;
                    }
                }
            }


Add this line after the objtiletype array definition. This defines the global variable started, which is false by default.

            var objtiletype=[];
            objtiletype[0] = {pos:"0px 0px", n:0, e:1, s:1, w:0};
            objtiletype[1] = {pos:"-200px 0px", n:0, e:0, s:1, w:1};
            objtiletype[2] = {pos:"-200px -200px", n:1, e:0, s:0, w:1};
            objtiletype[3] = {pos:"0px -200px", n:1, e:1, s:0, w:0};
            objtiletype[4] = {pos:"-100px 0px", n:0, e:1, s:0, w:1};
            objtiletype[5] = {pos:"0px -100px", n:1, e:0, s:1, w:0};
            objtiletype[6] = {pos:"-100px -200px", n:0, e:1, s:0, w:1};
            objtiletype[7] = {pos:"-200px -100px", n:1, e:0, s:1, w:0};
            objtiletype[8] = {pos:"-52px -100px", n:0, e:1, s:0, w:0};
            objtiletype[9] = {pos:"-148px -100px", n:0, e:0, s:0, w:1}; 

            var objtiles=[];
            var path=[];
            var started=false;


Now create the rotatefile() function. The If-else statement here gives you control over the rotation. If the game is not started, clicking only brings up a prompt asking the player to start the game.

            function rotatetile(vartile,varsec)
            {
                if (started)
                {
       
                }
                else
                {
                    alert("Please start the game first.");
                    return false;
                }
            }

            function resetboard()
            {


If the game has been started, the two arguments passed into the rotatetile() function come into play. vartile is the id of the tile that was clicked, while varsec defines the number of seconds it takes for the rotation to complete. Adjust varsec as you see fit! Now, the code gets the rotation status of the tile, adds 90 degrees to it, then uses varsec to determine the speed of the rotation.

            function rotatetile(vartile,varsec)
            {
                if (started)
                {
                    var timer=document.getElementById("pnlTimer");
                    var seconds=timer.innerHTML;
                    var degrees=vartile.style.transform;
                    degrees=degrees.replace("rotate(","");
                    degrees=degrees.replace("deg)","");
                    degrees=parseInt(degrees)+90;
                    vartile.style.transition="all "+varsec+"s";
                    vartile.style.transform="rotate("+degrees+"deg)";    
                    vartile.style.webkitTransition="all "+varsec+"s";
                    vartile.style.webkitTransform="rotate("+degrees+"deg)";   
                }
                else
                {
                    alert("Please start the game first.");
                    return false;
                }
            }


But that's just the visual rotation. We have to take care of the logical part too. The n, s, e and w values of the tile will change with each rotation.

            function rotatetile(vartile,varsec)
            {
                if (started)
                {
                    var timer=document.getElementById("pnlTimer");
                    var seconds=timer.innerHTML;
                    var degrees=vartile.style.transform;
                    degrees=degrees.replace("rotate(","");
                    degrees=degrees.replace("deg)","");
                    degrees=parseInt(degrees)+90;
                    vartile.style.transition="all "+varsec+"s";
                    vartile.style.transform="rotate("+degrees+"deg)";
                    vartile.style.webkitTransition="all "+varsec+"s";
                    vartile.style.webkitTransform="rotate("+degrees+"deg)";    

                    var id=parseInt(vartile.id.replace("tile",""));
                    var n,e,s,w;
                    n=objtiles[id].w;
                    e=objtiles[id].n;
                    s=objtiles[id].e;
                    w=objtiles[id].s;

                    objtiles[id].n=n;

                    objtiles[id].e=e;
                    objtiles[id].s=s;
                    objtiles[id].w=w;           
                }
                else
                {
                    alert("Please start the game first.");
                    return false;
                }
            }


Just a visual to help you see what I mean. Let's say this tile is rotated...




Before


After
n 0 0
s 1 1
e 1 0
w 0 1


Or this tile.




Before


After
n 1 0
s 1 0
e 0 1
w 0 1


Try it! Run your code. Click on the tiles. What happens? Bet you just kept getting prompts to start the game, eh? No sweat, there's something you're missing. Alter your reset() function. So clicking on "begin" not only resets the board, it also starts the time with the timer_start() function!

            function reset()
            {
                var start_id,end_id;
                var tiletypeindex;

                do
                {
                    resetboard();
                    start_id=generaterandomno(0,3) * 6;
                    objtiles[start_id].n=objtiletype[8].n;
                    objtiles[start_id].s=objtiletype[8].s;
                    objtiles[start_id].e=objtiletype[8].e;
                    objtiles[start_id].w=objtiletype[8].w;
                    end_id=(generaterandomno(1,4) * 6) - 1;
                    objtiles[end_id].n=objtiletype[9].n;
                    objtiles[end_id].s=objtiletype[9].s;
                    objtiles[end_id].e=objtiletype[9].e;
                    objtiles[end_id].w=objtiletype[9].w;

                    document.getElementById("tile"+start_id).style.backgroundPosition=objtiletype[8].pos;
                    document.getElementById("tile"+end_id).style.backgroundPosition=objtiletype[9].pos;

                    createpath(start_id,end_id);
                }
                while (path[path.length-1]==-1);

                //illustrate final path
                for (var i=1;i<path.length-1;i++)
                {
                    tiletypeindex=generaterandomno(0,3);

                    if (objtiles[path[i-1]].x==objtiles[path[i]].x&&objtiles[path[i+1]].x==objtiles[path[i]].x)
                    {
                        tiletypeindex=generaterandomno(4,7);
                    }

                    if (objtiles[path[i-1]].y==objtiles[path[i]].y&&objtiles[path[i+1]].y==objtiles[path[i]].y)
                    {
                        tiletypeindex=generaterandomno(4,7);
                    }

                      document.getElementById("tile"+path[i]).style.backgroundPosition=objtiletype[tiletypeindex].pos;

                    objtiles[path[i]].n=objtiletype[tiletypeindex].n;
                    objtiles[path[i]].e=objtiletype[tiletypeindex].e;
                    objtiles[path[i]].s=objtiletype[tiletypeindex].s;
                    objtiles[path[i]].w=objtiletype[tiletypeindex].w;

                    document.getElementById("tile"+path[i]).style.backgroundColor="#FF4400";
                }

                sendmessage("Click <span style='cursor:pointer' onclick='reset();timer_start();'><b>here</b></span> to begin");
            }


Now add the global variable gametime.

            var objtiletype=[];
            objtiletype[0] = {pos:"0px 0px", n:0, e:1, s:1, w:0};
            objtiletype[1] = {pos:"-200px 0px", n:0, e:0, s:1, w:1};
            objtiletype[2] = {pos:"-200px -200px", n:1, e:0, s:0, w:1};
            objtiletype[3] = {pos:"0px -200px", n:1, e:1, s:0, w:0};
            objtiletype[4] = {pos:"-100px 0px", n:0, e:1, s:0, w:1};
            objtiletype[5] = {pos:"0px -100px", n:1, e:0, s:1, w:0};
            objtiletype[6] = {pos:"-100px -200px", n:0, e:1, s:0, w:1};
            objtiletype[7] = {pos:"-200px -100px", n:1, e:0, s:1, w:0};
            objtiletype[8] = {pos:"-52px -100px", n:0, e:1, s:0, w:0};
            objtiletype[9] = {pos:"-148px -100px", n:0, e:0, s:0, w:1}; 

            var objtiles=[];
            var path=[];
            var gametime;
            var started=false;


Next, create your timer_start() function. This basically puts an initial value of 60 seconds in the pnlTimer div, clears the gametime variable, then sets a timer function to the decrement_timer() function. And of course, sets the global variable started to true.

            function sendmessage(varmessage)
            {
                document.getElementById("pnlMessage").innerHTML=varmessage;
            }

            function timer_start()
            {
                var timer=document.getElementById("pnlTimer");
                timer.innerHTML="Time left: 60 seconds";

                clearInterval(gametime);

                gametime=setInterval(function(){timer_decrement();},1000);
                started=true;
            }


You got it, another function! Create the timer_decrement() function.

            function timer_start()
            {
                var timer=document.getElementById("pnlTimer");
                timer.innerHTML="Time left: 60 seconds";

                clearInterval(gametime);
                gametime=setInterval(function(){timer_decrement();},1000);
                started=true;
            }

            function timer_decrement()
            {

            }


Now run the code. Click on "begin". Does the timer display say "60 seconds"? Now click on the tiles. Do they rotate?

My timer isn't moving!

That's because we haven't written any code for the timer_decrement() function. Here goes...
            function timer_decrement()
            {
                var timer=document.getElementById("pnlTimer");
                var seconds=timer.innerHTML;
                seconds=seconds.replace(" seconds","");
                seconds=seconds.replace("Time left: ","");
                seconds=parseInt(seconds);

                if (seconds==0)

                {
                    clearInterval(gametime);
                    alert("Times up! Better luck next time!");
                    started=false;
                }
                else
                {
                    seconds--;
                    timer.innerHTML="Time left: "+seconds+" seconds";
                }
            }


This basically gets the value of the pnlTimer div. If the time is now 0, it resets gametime, sets the started variable to false, and prompts the player. If not, it decrements the value and puts it back in the div.

Refresh. Is your timer moving now?

How do I win the game?

Well, as you probably know by now, rotating the tiles until there is an unbroken line from the start tile to the end tile, wins the game. But how does the script know? The answer is in the rotatetile() function. Add this segment. The function findcurrentpath() checks the board, after every rotation. If it finds an unbroken path from start tile to the end tile, it returns true. And that's when you can prompt the player with a congratulatory message, clear the gametime variable, set the started variable back to false, and put a "replay" link in the pnlMessage div.
            function rotatetile(vartile,varsec)
            {
                if (started)
                {
                    var timer=document.getElementById("pnlTimer");
                    var seconds=timer.innerHTML;
                    var degrees=vartile.style.transform;
                    degrees=degrees.replace("rotate(","");
                    degrees=degrees.replace("deg)","");
                    degrees=parseInt(degrees)+90;
                    vartile.style.transition="all "+varsec+"s";
                    vartile.style.transform="rotate("+degrees+"deg)";
                    vartile.style.webkitTransition="all "+varsec+"s";
                    vartile.style.webkitTransform="rotate("+degrees+"deg)";    

                    var id=parseInt(vartile.id.replace("tile",""));
                    var n,e,s,w;
                    n=objtiles[id].w;
                    e=objtiles[id].n;
                    s=objtiles[id].e;
                    w=objtiles[id].s;

                    objtiles[id].n=n;
                    objtiles[id].e=e;
                    objtiles[id].s=s;
                    objtiles[id].w=w;

                    if (findcurrentpath())
                    {
                        clearInterval(gametime);
                        alert("Congratulations! You have found true love!");
                        sendmessage("Well done! Click <span style='cursor:pointer' onclick='reset();timer_start();'><b>here</b></span> to replay");
                        started=false;   
                    }           
                }
                else
                {
                    alert("Please start the game first.");
                    return false;
                }
            }


Phew! Got all that? Now it's time for one last function, which is findcurrentpath(). First, we declare variables next_id and current_id. current_id is set to the first element in the path array, which is the id of the start tile. At the end of it, if next_id is the same as the last element in the path array, the function returns true. Otherwise, it returns false.

            function findcurrentpath()
            {
                var next_id,current_id=path[0];   

                if (next_id==path[path.length-1])

                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            function generaterandomno(varmin,varmax)
            {
                return Math.floor((Math.random() * (varmax-varmin+1)) + varmin);
            }


Now declare the variables trynext and direction. trynext is exactly what the name suggests - a temporary variable for feeling out the next tile's id. direction is meant to hold the next possible direction.

            function findcurrentpath()
            {
                var next_id,current_id=path[0];
                var trynext;
                var direction;

                if (next_id==path[path.length-1])
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }


direction will never be the direction that the next tile came from. For example, if the next possible direction is east, then for the tile eastwards of the current tile, the next possible direction is never west.









n possible










(current)
w not possible


(next)


e possible










s possible






This is reflected in the next series of If statements. Since the start tile has only one exit point, it's easy to determine the one possible direction.

            function findcurrentpath()
            {
                var next_id,current_id=path[0];
                var trynext;
                var direction;

                if (objtiles[current_id].n==1) direction="n";

                if (objtiles[current_id].s==1) direction="s";
                if (objtiles[current_id].e==1) direction="e";
                if (objtiles[current_id].w==1) direction="w";   

                if (next_id==path[path.length-1])
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }


Another Do-while loop. next_id is set at a default of "-1". If a next tile is available, this value is changed  to the id of the next tile. This continues until no next tile is available (ie, next_id remains at a value of "-1") or the value of next_id is the same as the last element of the path array.

            function findcurrentpath()
            {
                var next_id,current_id=path[0];
                var trynext;
                var direction;

                if (objtiles[current_id].n==1) direction="n";
                if (objtiles[current_id].s==1) direction="s";
                if (objtiles[current_id].e==1) direction="e";
                if (objtiles[current_id].w==1) direction="w";   
           
                do
                {
                    next_id=-1;
                }
                while (next_id!=-1&&next_id!=path[path.length-1])

                if (next_id==path[path.length-1])
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }


Here, let's create the If statement for "n", ie. direction north. First, we check if it's at the top row. If so, north is a dead end and no further action is necessary. next_id remains at "-1" and findcurrentpath() returns false. If not, we check for the s property of the tile above the current tile. If it's not "1", that means there's no connection. Again, no further action is necessary. next_id remains at "-1" and findcurrentpath() returns false. If there is a connection, trynext is set to the id of the tile above the current tile. We then check for the exit points of that tile - north, east or west. We won't check south because the previous tile was from the south!

            function findcurrentpath()
            {
                var next_id,current_id=path[0];
                var trynext;
                var direction;

                if (objtiles[current_id].n==1) direction="n";
                if (objtiles[current_id].s==1) direction="s";
                if (objtiles[current_id].e==1) direction="e";
                if (objtiles[current_id].w==1) direction="w";   
           
                do
                {
                    next_id=-1;

                    if (direction=="n")
                    {
                        if (objtiles[current_id].y>0)
                        {
                            trynext=current_id-6;

                            if (objtiles[trynext].s==1)

                            {
                                next_id=trynext;
                                current_id=next_id;
                                if (objtiles[trynext].n==1) direction="n";
                                if (objtiles[trynext].e==1) direction="e";
                                if (objtiles[trynext].w==1) direction="w";
                            }
                        }
                    }

                }
                while (next_id!=-1&&next_id!=path[path.length-1])

                if (next_id==path[path.length-1])
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }


So do the same for the other directions.
            function findcurrentpath()
            {
                var next_id,current_id=path[0];
                var trynext;
                var direction;

                if (objtiles[current_id].n==1) direction="n";
                if (objtiles[current_id].s==1) direction="s";
                if (objtiles[current_id].e==1) direction="e";
                if (objtiles[current_id].w==1) direction="w";   
           
                do
                {
                    next_id=-1;

                    if (direction=="n")
                    {
                        if (objtiles[current_id].y>0)
                        {
                            trynext=current_id-6;

                            if (objtiles[trynext].s==1)
                            {
                                next_id=trynext;
                                current_id=next_id;
                                if (objtiles[trynext].n==1) direction="n";
                                if (objtiles[trynext].e==1) direction="e";
                                if (objtiles[trynext].w==1) direction="w";
                            }
                        }
                    }

                    if (direction=="s")
                    {
                        if (objtiles[current_id].y<3)
                        {
                            trynext=current_id+6;

                            if (objtiles[trynext].n==1)
                            {
                                next_id=trynext;
                                current_id=next_id;
                                if (objtiles[trynext].s==1) direction="s";
                                if (objtiles[trynext].e==1) direction="e";
                                if (objtiles[trynext].w==1) direction="w";   
                            }
                        }
                    }

                    if (direction=="e")
                    {
                        if (objtiles[current_id].x<5)
                        {
                            trynext=current_id+1;

                            if (objtiles[trynext].w==1)
                            {
                                next_id=trynext;
                                current_id=next_id;
                                if (objtiles[trynext].n==1) direction="n";
                                if (objtiles[trynext].s==1) direction="s";
                                if (objtiles[trynext].e==1) direction="e";   
                            }
                        }
                    }

                    if (direction=="w")
                    {
                        if (objtiles[current_id].x>0)
                        {
                            trynext=current_id-1;
                           
                            if (objtiles[trynext].e==1)
                            {
                                next_id=trynext;
                                current_id=next_id;
                                if (objtiles[trynext].n==1) direction="n";
                                if (objtiles[trynext].s==1) direction="s";
                                if (objtiles[trynext].w==1) direction="w";   
                            }
                        }
                    }
                }
                while (next_id!=-1&&next_id!=path[path.length-1])

                if (next_id==path[path.length-1])
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

That's it! Time to test your game!

Run your code. I've left the path shaded in orange to help see if what you have is correct. Click on "begin" and start rotating your tiles. When there's an unbroken line between the start tile and the end tile, you should see the victory prompt.

Now remove this line or comment it out.

            function reset()
            {
                var start_id,end_id;
                var tiletypeindex;

                do
                {
                    resetboard();
                    start_id=generaterandomno(0,3) * 6;
                    objtiles[start_id].n=objtiletype[8].n;
                    objtiles[start_id].s=objtiletype[8].s;
                    objtiles[start_id].e=objtiletype[8].e;
                    objtiles[start_id].w=objtiletype[8].w;
                    end_id=(generaterandomno(1,4) * 6) - 1;
                    objtiles[end_id].n=objtiletype[9].n;
                    objtiles[end_id].s=objtiletype[9].s;
                    objtiles[end_id].e=objtiletype[9].e;
                    objtiles[end_id].w=objtiletype[9].w;

                    document.getElementById("tile"+start_id).style.backgroundPosition=objtiletype[8].pos;
                    document.getElementById("tile"+end_id).style.backgroundPosition=objtiletype[9].pos;

                    createpath(start_id,end_id);
                }
                while (path[path.length-1]==-1);

                for (var i=1;i<path.length-1;i++)
                {
                    tiletypeindex=generaterandomno(0,3);

                    if (objtiles[path[i-1]].x==objtiles[path[i]].x&&objtiles[path[i+1]].x==objtiles[path[i]].x)
                    {
                        tiletypeindex=generaterandomno(4,7);
                    }

                    if (objtiles[path[i-1]].y==objtiles[path[i]].y&&objtiles[path[i+1]].y==objtiles[path[i]].y)
                    {
                        tiletypeindex=generaterandomno(4,7);
                    }

                    document.getElementById("tile"+path[i]).style.backgroundPosition=objtiletype[tiletypeindex].pos;

                    objtiles[path[i]].n=objtiletype[tiletypeindex].n;
                    objtiles[path[i]].e=objtiletype[tiletypeindex].e;
                    objtiles[path[i]].s=objtiletype[tiletypeindex].s;
                    objtiles[path[i]].w=objtiletype[tiletypeindex].w;

                    //document.getElementById("tile"+path[i]).style.backgroundColor=="#FF4400";
                }

                sendmessage("Click <span style='cursor:pointer' onclick='reset();timer_start();'><b>here</b></span> to

begin");
            }


And you have a game!

Next

We'll put the finishing touches on this game. Up the difficulty level a bit. It will be short, I promise.