Klõpsa mänguväljal; liikumine - nooleklahvid!
Reload, kui mäng on läbi (peatub)!
Labyrint
Selles loengus esitatakse:

Täiesti tühjal väljal tavaliselt seeni pole. Teeme programmi mis genereerib etteantud ridade-veergude arvuga labyrindi; hiljem muudame labyrindi seinad puude ridadeks ja saame nii metsa.

Labyrintide genereerimiseks on leiutatud palju algoritme. Kasutame siin nn "uuristamismeetodit":

Mängu ajal koll liigub. Liikumine tekib kogu kanvaa kustutamise ja siis kõigi elementide (labürint, koll, eesmärk) uues kohas joonistamisega. Kuna labürint ei tohi muutuda (see genereeritakse mängu algul) salvestatakse labürindi pikslid andmestruktuuris imageData. Selle struktuuri kaks esimest arvu on salvestatud pikselkujutise laius ja körgus (width, height) ja siis järgneb massiiv, kus iga piksel on salvestatud nelja arvuga: piksli punane, roheline, sinine ja läbipaistvuse (alpha) komponendid. Kui labürint on kanvaale joonistatud, toimub selle imageData loomine kanvaalt lugemisega käsuga getImageData(x,y,w,h), kus x,y - kanvaalt loetava ala vasak ülanurk, w,h - loetava pildiala laius ja kõrgus; salvestatud pikslite massiivi kanvaale joonistamine punkti x,y toimub käsuga putImageData(img,x,y) (sellel käsul võib olla ka rohkem parameetreid vaid pikslimassiivi osa kanvaale toomioseks).

Uue (tühja) ruumi loob funktsioon ruut:

function ruut(n, m) {
	this.r = n;
	this.v = m;
	this.doors = [];
	}

Javascriptis ei ole klasse, kuid parameetri this kasutamisel saab funktsiooni kasutada kui (klassi) konstruktorfunktsiooni: käsk new ruut(n,m) loob uue objekti, millele on paraameetrid .r (rida), .v (veerg), .doors (uste massiiv - loomisel tühi, sellepärast pole seda ka funktsiooni formaalsete parameetrite seas). Programmi algul luuakse kõik ruumid ja salvestatakse (lineaarses, s.t. ühemõõtmelises) massiivis ruudud:

for (var i = 0; i < n; i++) {
			for (var j = 0; j < n; j++) {
				ruudud.push(new ruut(i, j));
			}
		}

Labürindile uue ruumi lisamiseks otsitakse kõigi juba lisatud ruumide seas selline, millel mingis suunas pole ust, kuid selles suunas (seina taga) on veel lisamata ruum. Sellepärast on tarvis funktsiooni, mis ruumi r ja (juhuslikult valitud) suuna d1 põhjal leiab selles suunas "seina taga" oleva ruumi ja kontrollib, kas see on veel ühendamata (kuulub massiivi free):

 function next(r, d) {
	var nn1;
	var mm1;
	switch (d) {
		case 0:
			nn1 = r.r;
			mm1 = r.v + 1;
			break;
		case 1:
			nn1 = r.r + 1;
			mm1 = r.v;
			break;
		case 2:
			nn1 = r.r;
			mm1 = r.v - 1;
			break;
		case 3:
			nn1 = r.r - 1;
			mm1 = r.v;
			break;
		default:
			nn1 = r.r;
			mm1 = r.v;
	}
	
	for (var i = 0; i < free.length; i++) {
		//console.log(i+' '+n1+' '+m1);
		if ((free[i].r == nn1) && (free[i].v == mm1)) {
			return i;
			break;
		}
	}
	return -1;
}

Labürindi joonistamiseks kasutatakse abifunktisooni line:

function line(x0, y0, x1, y1) {
	ctx.strokeStyle = '#0000cc';
	ctx.beginPath();
	ctx.moveTo(x0, y0);
	ctx.lineTo(x1, y1);
	ctx.stroke();
}

Kolli ja eesmärgi joonistamiseks kasutatakse abifunktsiooni

function circle(x,y,r,fill,stroke){  //a - angle, d - direction
	ctx.fillStyle = fill;
	ctx.strokeStyle = stroke;
	ctx.beginPath();
	ctx.arc(x, y, r, 0, Math.PI*2, true);
	ctx.stroke();
	ctx.fill();
}

Kogu labyrindi, kolli ja eesmärgi joonistab neid abifunktisoone kasutades järgmine funktsioon, mis käivitatse diokumendi loomisel automaatselt (skriptis globaalsete muutujate deklaratsioonide järel):

window.onload = function () {
	
		for (var i = 0; i < n; i++) {
			for (var j = 0; j < n; j++) {
				ruudud.push(new ruut(i, j));
			}
		}
		var i0 = random(0,ruudud.length);
		connected.push(ruudud[i0]);
		//esimene ruum labyrindis!
		free = ruudud.slice(0); //eemaldame 
		free.splice(i0, 1);
		//Next line starts Firebug debugger!
		//debugger;
		while (free.length > 0) {
			var found = 0;
			while (found == 0) {
				i0 = random(0, connected.length);
				var r = connected[i0];
				//kandidaat!
				var dirs = [0, 1, 2, 3];
				for (var i = 0; i < 4; i++) {
					if (r.doors.indexOf(dirs[i]) > 0)
						dirs.splice(i, 1);
				}
				while (dirs.length > 0) {
					var j0 = random(0, dirs.length);
					var d1 = dirs[j0];
					var i1 = next(r, d1);
					if (i1 >= 0) {
						var r1 = free[i1];
						found = 1;
						r.doors.push(d1);
						var d2 = reverse(d1);
						r1.doors.push(d2);
						connected.push(r1);
						free.splice(i1, 1);
						break;
					} else {
						dirs.splice(j0, 1);
					}
				}
			}
		}
		//draw labyrinth
		for (var i = 0; i < connected.length; i++) {
			var xx = connected[i].v;
			var yy = connected[i].r;
			if (connected[i].doors.indexOf(0) == -1) //ust selles suunas pole
				line((xx + 1) * d, yy * d, (xx + 1) * d, (yy + 1) * d);
			if (connected[i].doors.indexOf(1) == -1)
				line(xx * d, (yy + 1) * d, (xx + 1) * d, (yy + 1) * d);
			if (xx == 0 && connected[i].doors.indexOf(2) == -1)
				line(xx * d, yy * d, xx * d, (yy + 1) * d);
			if (yy == 0 && connected[i].doors.indexOf(3) == -1)
				line(xx * d, yy * d, (xx + 1) * d, yy * d);
		}
		labyr = ctx.getImageData(0, 0, w, h);
		
		clear(0,0,d,d); //x0-d/4,y0-d/2,d/2,d/2);	
		circle(x0,y0,d0,'#0000CC','#0000CC');
		bogy = ctx.getImageData(x0-d/4,y0-d/4,d/2,d/2);	
		
		clear(0,0,d,d); //x0-d/4,y0-d/2,d/2,d/2);	
		circle(x0,y0,d0,'#FFAA00','#FF0000');
		goal = ctx.getImageData(x0-d/4,y0-d/4,d/2,d/2);	
		x0 = d/4;
		y0 = d/4;
		xg = w-3*d/4;
		yg = h-3*d/4;
		
		window.onkeydown = move;
		return frame=window.setInterval(animate, 10);
		//return setTimeout(animate,30);
	} //if (canvas.getContext
}//drawLabyr

See funktsioon lisab aknale sündmuse keyDown kuulaja - funktsiooni move (siin saab kasutaja nooleklahvidega kolli juhtida) ja käivitab iga 10 ms järel funktsiooni animate - kanvaa puhastatakse ja joonistatkse siis uus seis:

function move(e) {
	var r = Math.floor(y0/d);
	var v = Math.floor(x0/d);
	var mx = 0;
	var my = 0;
	switch(e.keyCode) {
	case (37): 
	if (ruudud[r*n+v].doors.indexOf(2)> -1)
        mx = -d;
        break;
    case 38: 
	if (ruudud[r*n+v].doors.indexOf(3)> -1)
        my = -d;
		break;
    case 39: 
	if (ruudud[r*n+v].doors.indexOf(0)> -1)
        mx = d;
        break;
    case 40:
	if (ruudud[r*n+v].doors.indexOf(1)> -1)
        my = d;
		break;
	default:
		mx = 0;
		my = 0;
    }
	//clear(x0,y0,d/2,d/2);
	
	x0 += mx;
	y0 += my;
}
function animate(){
	ctx.putImageData(labyr, 0, 0);
	ctx.putImageData(bogy, x0, y0);
	ctx.putImageData(goal, xg, yg);
	if (collision(x0,y0,bogy.width,bogy.height,xg,yg,goal.width,goal.height))
	{window.clearInterval(frame);
	showText("Tegid ära!",16,"#FF0000");}
	//ctx.restore();
}
Animatsioonifunktsioon animate kontrollib funktsiooni collision abil, kas koll on jõudnud eesmärgini ja kui on, näidatakse mängu aknas vastavat teksti ja kaadrivahetus peatatakse:
function collision(x1,y1,w1,h1,x2,y2,w2,h2){
	return 	x1 < x2 + w2 && x2 < x1 + w1 &&
			y1 < y2 + h2 && y2 < y1 + h1
	}
function showText(txt,sz,col){
	// Score
	ctx.fillStyle = col; //"rgb(250, 100, 100)";
	ctx.font = sz+"px Arial";
	ctx.textAlign = "left";
	ctx.textBaseline = "top";
	ctx.fillText(txt, d0, d0); //text, x,y
};
Ja ongi kõik!