topoffset=100;
leftoffset=100;
tileheight=41;
tilewidth=33;
layeroffset=-3;
copytopoffset=431;
copyleftoffset=561;

var move;
n=144;

function preload()
{
count=0;
image=new Array ('bg.jpg','bamb1.gif','bamb2.gif','bamb3.gif','bamb4.gif','bamb5.gif','bamb6.gif','bamb7.gif','bamb8.gif','bamb9.gif','circ1.gif','circ2.gif','circ3.gif','circ4.gif','circ5.gif','circ6.gif','circ7.gif','circ8.gif','circ9.gif','numb1.gif','numb2.gif','numb3.gif','numb4.gif','numb5.gif','numb6.gif','numb7.gif','numb8.gif','numb9.gif','wind1.gif','wind2.gif','wind3.gif','wind4.gif','drag1.gif','drag2.gif','drag3.gif','flow1.gif','flow2.gif','flow3.gif','flow4.gif','seas1.gif','seas2.gif','seas3.gif','seas4.gif','mark.gif','new.gif','restart.gif','undo.gif','moves.gif','hint.gif','help.gif','info.gif','email.gif');
picture=new Array(image.length);
for (i=0;i<image.length;i++)
{
picture[i]=new Image();
picture[i].onload=loadcheck;
picture[i].src=image[i];
}
}

function loadcheck()
{
count++;
if (count==1) 
{
display="<font style=\"font-family:Arial,sans-serif;font-size:14pt;white-space:nowrap;position:absolute;top:"+(topoffset/3)+";left:"+(leftoffset+tilewidth)+"\">Solitaire Mahjongg is loading...<\/font>";
document.getElementById("screen").innerHTML=display;
}
else if (count==image.length) setup();
}

function select(i,face)
{
i=i-120;
suggest=0;
test(i);
if (cond)
{
if (move==1)select2(i,face);
else select1(i,face);
}
else noselect();
}

function noselect()
{
message="Tile is not free!";
mark[k]="";
move=0;
show();
wait();
}

function nomatch()
{
message="Tiles are different!";
mark[k]="";
move=0;
show();
wait();
}

function equal()
{
message="No tile selected.";
mark[k]="";
move=0;
show();
wait();
}

function select1(i,face)
{
i1sto=i;
facesto=face;
message="Tile selected...";
k=i;
mark[k]="<a href=\"javascript:equal()\"><img src=\"mark.gif\" style=\"position:absolute;top:"+(offsetfact[k]*layeroffset+topoffset+tilefacty[k]*tileheight)+";left:"+(offsetfact[k]*layeroffset+leftoffset+tilefactx[k]*tilewidth)+"\" border=\"0\" alt=\"\"><\/a>";
move=1;
showhelp=0;
showinfo=0;
show();
}

function select2(i,face)
{
if (facesto!=face)nomatch();
else
{
i2sto=i;
ihistory[ihistory.length]=i1sto;
ihistory[ihistory.length]=i2sto;
tilehistory[tilehistory.length]=tile[i1sto];
tilehistory[tilehistory.length]=tile[i2sto];
tile[i1sto]=notile[i1sto];
tile[i2sto]=notile[i2sto];
message="Selected tiles match!";
mark[k]="";
move=0;
back++;
show();
counter-=2;
wait();
}
}

function shuffle()
{
randomnumber=new Array(n);
for (i=0;i<n;i++)
{
random=parseInt(Math.random()*(i+1));
randomnumber[i]=randomnumber[random];
randomnumber[random]=i;
}
}

function setup()
{
counter=n;
message="Game ready: "+n+" tiles are set up.";
move=0;
showhelp=0;
showinfo=0;
k=0;
suggest=0;
back=0;
tile=new Array(n);
notile=new Array(n);
mark=new Array(n);
value=new Array(n);
ihistory=new Array();
tilehistory=new Array();
fig=new Array(n);
tilevalue=new Array(10,10,10,10,11,11,11,11,12,12,12,12,13,13,13,13,14,14,14,14,15,15,15,15,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,22,22,22,22,23,23,23,23,24,24,24,24,25,25,25,25,26,26,26,26,27,27,27,27,28,28,28,28,29,29,29,29,30,30,30,30,31,31,31,31,32,32,32,32,33,33,33,33,34,34,34,34,35,35,35,35,36,36,36,36,37,37,37,37,38,38,38,38,39,39,39,39,40,40,40,40,41,41,41,41,42,42,42,42,43,43,43,43,44,44,44,44,45,45,45,45);
tileface=new Array('circ1','circ1','circ1','circ1','circ2','circ2','circ2','circ2','circ3','circ3','circ3','circ3','circ4','circ4','circ4','circ4','circ5','circ5','circ5','circ5','circ6','circ6','circ6','circ6','circ7','circ7','circ7','circ7','circ8','circ8','circ8','circ8','circ9','circ9','circ9','circ9','bamb1','bamb1','bamb1','bamb1','bamb2','bamb2','bamb2','bamb2','bamb3','bamb3','bamb3','bamb3','bamb4','bamb4','bamb4','bamb4','bamb5','bamb5','bamb5','bamb5','bamb6','bamb6','bamb6','bamb6','bamb7','bamb7','bamb7','bamb7','bamb8','bamb8','bamb8','bamb8','bamb9','bamb9','bamb9','bamb9','numb1','numb1','numb1','numb1','numb2','numb2','numb2','numb2','numb3','numb3','numb3','numb3','numb4','numb4','numb4','numb4','numb5','numb5','numb5','numb5','numb6','numb6','numb6','numb6','numb7','numb7','numb7','numb7','numb8','numb8','numb8','numb8','numb9','numb9','numb9','numb9','wind1','wind1','wind1','wind1','wind2','wind2','wind2','wind2','wind3','wind3','wind3','wind3','wind4','wind4','wind4','wind4','drag1','drag1','drag1','drag1','drag2','drag2','drag2','drag2','drag3','drag3','drag3','drag3','seas1','seas2','seas3','seas4','flow1','flow2','flow3','flow4');
shuffle();
for (i=0;i<n;i++)
{
tile[i]="<a href=\"javascript:select("+(randomnumber[i]+120)+","+tilevalue[i]+")\"><img src=\""+tileface[i];
notile[i]="";
mark[i]="";
value[i]=(randomnumber[i]+120)*1000+tilevalue[i];
}
tile.sort();
value.sort();
tilepos=".gif\" style=\"position:absolute;top:";
offsetfact=new Array(4,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
tilefacty=new Array(3.5,3,3,4,4,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3.5,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,3.5,3.5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7);
tilefactx=new Array(6.5,6,7,6,7,5,6,7,8,5,6,7,8,5,6,7,8,5,6,7,8,4,5,6,7,8,9,4,5,6,7,8,9,4,5,6,7,8,9,4,5,6,7,8,9,4,5,6,7,8,9,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,10,11,12,3,4,5,6,7,8,9,10,2,3,4,5,6,7,8,9,10,11,0,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,13,14,2,3,4,5,6,7,8,9,10,11,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,11,12);
for (i=0;i<n;i++)
tile[i]=tile[i]+tilepos+(offsetfact[i]*layeroffset+topoffset+tilefacty[i]*tileheight)+";left:"+(offsetfact[i]*layeroffset+leftoffset+tilefactx[i]*tilewidth)+"\" border=\"0\" alt=\"\"><\/a>";
background="<img src=\"bg.jpg\" style=\"position:absolute;top:"+(topoffset-10)+";left:"+(leftoffset-10)+"\" alt=\"\">";
copyright="<font style=\"font-family:Arial,sans-serif;font-size:7pt;white-space:nowrap;position:absolute;top:"+copytopoffset+";left:"+copyleftoffset+"\">&copy; 2002 twb<\/font>";
menu="<br><br><a href=\"javascript:setup()\"><img src=\"new.gif\" border=\"0\" alt=\"\" title=\"Sets up new game\"><\/a><a href=\"javascript:restart()\"><img src=\"restart.gif\" border=\"0\" alt=\"\" title=\"Restarts same game\"><\/a><a href=\"javascript:undo()\"><img src=\"undo.gif\" border=\"0\" alt=\"\" title=\"Withdraws last move\"><\/a><a href=\"javascript:moves()\"><img src=\"moves.gif\" border=\"0\" alt=\"\" title=\"Displays number of possible moves\"><\/a><a href=\"javascript:hint()\"><img src=\"hint.gif\" border=\"0\" alt=\"\" title=\"Shows possible moves\"><\/a><a href=\"javascript:help()\"><img src=\"help.gif\" border=\"0\" alt=\"\" title=\"Displays rules of the game\"><\/a><a href=\"javascript:info()\"><img src=\"info.gif\" border=\"0\" alt=\"\" title=\"Displays game information\"><\/a><a href=\"mailto:thomas.weibel@bluewin.ch\"><img src=\"email.gif\" border=\"0\" alt=\"\" title=\"Sends email to the author\"><\/a>";
show();
wait();
}

function show()
{
for (i=0;i<n;i++)fig[i]=tile[i]+mark[i];
display="<font style=\"font-family:Arial,sans-serif;font-size:24pt;white-space:nowrap;position:absolute;top:"+(topoffset/3)+";left:"+(leftoffset+tilewidth)+"\">Solitaire Mahjongg<\/font>"+background+copyright+fig[57]+fig[58]+fig[59]+fig[60]+fig[61]+fig[62]+fig[63]+fig[64]+fig[65]+fig[66]+fig[67]+fig[68]+fig[69]+fig[70]+fig[71]+fig[72]+fig[73]+fig[74]+fig[75]+fig[76]+fig[77]+fig[78]+fig[79]+fig[80]+fig[81]+fig[82]+fig[83]+fig[84]+fig[85]+fig[86]+fig[87]+fig[88]+fig[89]+fig[90]+fig[91]+fig[92]+fig[93]+fig[94]+fig[95]+fig[96]+fig[97]+fig[98]+fig[99]+fig[100]+fig[101]+fig[102]+fig[103]+fig[104]+fig[105]+fig[106]+fig[107]+fig[108]+fig[109]+fig[110]+fig[111]+fig[112]+fig[113]+fig[114]+fig[115]+fig[116]+fig[117]+fig[118]+fig[119]+fig[120]+fig[121]+fig[122]+fig[123]+fig[124]+fig[125]+fig[126]+fig[127]+fig[128]+fig[129]+fig[130]+fig[131]+fig[132]+fig[133]+fig[134]+fig[135]+fig[136]+fig[137]+fig[138]+fig[139]+fig[140]+fig[141]+fig[142]+fig[143]+fig[21]+fig[22]+fig[23]+fig[24]+fig[25]+fig[26]+fig[27]+fig[28]+fig[29]+fig[30]+fig[31]+fig[32]+fig[33]+fig[34]+fig[35]+fig[36]+fig[37]+fig[38]+fig[39]+fig[40]+fig[41]+fig[42]+fig[43]+fig[44]+fig[45]+fig[46]+fig[47]+fig[48]+fig[49]+fig[50]+fig[51]+fig[52]+fig[53]+fig[54]+fig[55]+fig[56]+fig[5]+fig[6]+fig[7]+fig[8]+fig[9]+fig[10]+fig[11]+fig[12]+fig[13]+fig[14]+fig[15]+fig[16]+fig[17]+fig[18]+fig[19]+fig[20]+fig[1]+fig[2]+fig[3]+fig[4]+fig[0]+"<table width=\""+(tilewidth*12)+"\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" style=\"position:absolute;top:"+(topoffset+tileheight*9)+";left:"+(leftoffset+tilewidth)+"\"><tr><td style=\"font-family:Arial,sans-serif;font-size:14pt\">"+message+"<\/td><\/tr><tr><td>"+menu+"<\/td><\/tr><\/table>";
document.getElementById("screen").innerHTML=display;
}

function wait()
{
showhelp=0;
showinfo=0;
if (counter!=0) message="Score: "+(n-counter);
else message="Score: "+(n-counter)+" &#150; congratulations! You win!";
setTimeout('show()',2000);
}

function help()
{
if (showhelp==0)
{
showhelp=1;
message="Solitaire Mahjongg, also called Taipei, Taipeh or Shanghai, is the web adaptation of an old Chinese board game. The objective is to remove all "+n+" tiles from the board; for each pair of tiles removed you will get two points.<p>Options:<p><ul><li>New: sets up a new game.<\/li><li>Restart: restarts the current game.<\/li><li>Undo: withdraws your moves one by one.<\/li><li>Moves: displays the number of currently possible moves.<\/li><li>Hints: shows currently possible moves one by one.<\/li><li>Help: displays the rules of the game.<\/li><li>Info: displays Solitaire Mahjongg game information.<\/li><li>Email: sends an email to the author.<\/li><\/ul><p>The rules are simple:<p>Starting from the sides, or from the central tile at the top of the pile, remove tiles by clicking on two equal symbols. If the tiles are identical, they will disappear, and your score will rise.<p>Selectable tiles always must have at least one empty side. If on both sides of a tile there is a neighbouring tile on the same level, it cannot be selected.<p><img src=\"seas1.gif\" alt=\"\"><img src=\"seas2.gif\" alt=\"\"><img src=\"seas3.gif\" alt=\"\"><img src=\"seas4.gif\" alt=\"\">&nbsp;&nbsp;<img src=\"flow1.gif\" alt=\"\"><img src=\"flow2.gif\" alt=\"\"><img src=\"flow3.gif\" alt=\"\"><img src=\"flow4.gif\" alt=\"\"><br>Note: The four tiles showing season symbols (above, left) match even if they are different; the four flower tiles (above, right) match respectively.";
suggest=0;
mark[k]="";
move=0;
show();
}
else
{
message="Back to the game.";
show();
wait();
}
}

function info()
{
if (showinfo==0)
{
showinfo=1;
message="In mathematical terms, Solitaire Mahjongg is easily described. There are 144!/(4!)<sup>36<\/sup> different games, the probability that you will encounter the same game twice is 1/1.1394912*10<sup>200<\/sup>. The number of games that actually can be won cannot be calculated exactly, yet experts estimate that good players win every third up to every second game.<p>This web version of Solitaire Mahjongg consists of two files coded in DHTML/CSS and Javascript. In addition, images are used for the tiles, buttons and game background. To play the game no plugins are needed, and there are no special system requirements. If the Javascript option of your browser is enabled, just surf and play.<p>The idea for this game comes from the annual www.the5k.org competition first held in 2000, the objective of which it is to encourage lean, efficient, functional, original and aesthetic web programming. A < 5kb version of Solitaire Mahjongg took part in the 2002  contest.<p>Solitaire Mahjongg v2.4<br>copyright &copy; 2002 by Thomas Weibel<br>4102 Binningen, Switzerland<p>Last update: October 22nd, 2002";
suggest=0;
mark[k]="";
move=0;
show();
}
else
{
message="Back to the game.";
show();
wait();
}
}

function undo()
{
if (ihistory.length==0)
{
message="Undo not possible!";
suggest=0;
mark[k]="";
move=0;
show();
wait();
}
else
{
restore();
message="Move undone.";
suggest=0;
mark[k]="";
move=0;
back--;
show();
counter+=2;
wait();
}
}

function restart()
{
if (ihistory.length==0)
{
message="Restart not possible!";
suggest=0;
mark[k]="";
move=0;
show();
wait();
}
else
{
for (i=0;i<back;i++) restore();
message="Game restarted.";
suggest=0;
mark[k]="";
move=0;
back=0;
show();
counter=n;
wait();
}
}

function restore()
{
tile[ihistory[ihistory.length-1]]=tilehistory[tilehistory.length-1];
tile[ihistory[ihistory.length-2]]=tilehistory[tilehistory.length-2];
ihistory.length=ihistory.length-2;
tilehistory.length=tilehistory.length-2;
}

function moves()
{
suggest=0;
remain=0;
calc=new Array();
free=new Array();
for (i=0;i<n;i++)
{
remaining(i);
if (cond)
free[free.length]=calc[i];
}
free.sort();
for (i=0;i<(free.length-1);i++)
{
if (free[i]==free[i+1]&&free[i]==free[i+2]&&free[i]==free[i+3])
{
remain+=2; i+=3;
}
if (free[i]==free[i+1]&&free[i]==free[i+2])
{
remain++; i+=2;
}
if (free[i]==free[i+1])
{
remain++; i++;
}
}
if (remain==0) message="Game over: No moves left!";
else message="Possible moves: "+remain;
mark[k]="";
move=0;
show();
wait();
}

function hint()
{
if (suggest==0)
{
calc=new Array();
free=new Array();
hints=new Array();
for (i=0;i<n;i++)
{
remaining(i);
if (cond)
free[free.length]=calc[i]*1000+i;
}
free.sort();
for (i=0;i<(free.length-1);i++)
{
if (parseInt(free[i]/1000)==parseInt(free[i+1]/1000))
{
hints[hints.length]=free[i]-parseInt(free[i]/1000)*1000;
hints[hints.length]=free[i+1]-parseInt(free[i+1]/1000)*1000;
}
}
if (hints.length==0)
{
message="Game over: No moves left!";
suggest=0;
}
else
{
remainmsg();
suggest=1;
}
}
else if (suggest==1)
{
remainmsg();
}
move=0;
show();
mark[hints[0]]="";
mark[hints[1]]="";
mark[k]="";
hints[hints.length]=hints[0];
hints[hints.length]=hints[1];
for (i=0;i<hints.length-2;i++)
hints[i]=hints[i+2];
hints.length=hints.length-2;
wait();
}

function remaining(i)
{
calc[i]=value[i]-parseInt(value[i]/1000)*1000;
test(i);
}

function remainmsg()
{
message="Possible move";
mark[hints[0]]="<img src=\"mark.gif\" style=\"position:absolute;top:"+(offsetfact[hints[0]]*layeroffset+topoffset+tilefacty[hints[0]]*tileheight)+";left:"+(offsetfact[hints[0]]*layeroffset+leftoffset+tilefactx[hints[0]]*tilewidth)+"\" alt=\"\">";
mark[hints[1]]="<img src=\"mark.gif\" style=\"position:absolute;top:"+(offsetfact[hints[1]]*layeroffset+topoffset+tilefacty[hints[1]]*tileheight)+";left:"+(offsetfact[hints[1]]*layeroffset+leftoffset+tilefactx[hints[1]]*tilewidth)+"\" alt=\"\">";
}

function test(i)
{
lr=(tile[i-1]==notile[i-1]||tile[i+1]==notile[i+1]);
loop=new Array();
vari=new Array(10,11,14,15,28,29,30,31,34,35,36,37,40,41,42,43,46,47,48,49,70,71,72,73,74,75,79,80,81,82,83,84,91,92,93,94,95,96,103,104,105,106,107,108,116,117,118,119,120,121,125,126,127,128,129,130);
for (j=1;j<57;j++) loop[j]=(i==vari[j-1]&&(lr&&tile[j]==notile[j]));
cond=((i==0||i==5||i==8||i==9||i==12||i==13||i==16||i==17||i==20||i==21||i==26||i==27||i==32||i==33||i==38||i==39||i==44||i==45||i==50||i==51||i==56||i==57||i==68||i==69||i==76||i==77||i==86||i==87||i==113||i==114||i==123||i==124||i==131||i==132||i==143||
((i>=1&&i<=4)&&tile[0]==notile[0])||
((i==6||i==7||i==18||i==19||(i>=22&&i<=25)||(i>=52&&i<=55)||(i>=58&&i<=67)||i==78||i==85||(i>=88&&i<=90)||i==97||i==98||i==101||i==102||(i>=109&&i<=111)||i==115||i==122||i==133||(i>=134&&i<=142))&&lr)||
loop[1]||loop[2]||loop[3]||loop[4]||loop[5]||loop[6]||loop[7]||loop[8]||loop[9]||loop[10]||loop[11]||loop[12]||loop[13]||loop[14]||loop[15]||loop[16]||loop[17]||loop[18]||loop[19]||loop[20]||loop[21]||loop[22]||loop[23]||loop[24]||loop[25]||loop[26]||loop[27]||loop[28]||loop[29]||loop[30]||loop[31]||loop[32]||loop[33]||loop[34]||loop[35]||loop[36]||loop[37]||loop[38]||loop[39]||loop[40]||loop[41]||loop[42]||loop[43]||loop[44]||loop[45]||loop[46]||loop[47]||loop[48]||loop[49]||loop[50]||loop[51]||loop[52]||loop[53]||loop[54]||loop[55]||loop[56]||
(i==99&&(tile[98]==notile[98]||tile[112]==notile[112]))||
(i==100&&(tile[87]==notile[87]||tile[101]==notile[101]))||
(i==112&&((tile[99]==notile[99]&&tile[111]==notile[111])||tile[113]==notile[113])))&&
tile[i]!=notile[i]);
}

