Pull to refresh

Canvas: пятнадцать минут на пятнашки

JavaScript *HTML *Canvas *
Tutorial
CANVAS шаг за шагом:
  1. Основы
  2. Изображения
  3. Понг
  4. Пятнашки

В детстве у меня были пятнашки, я думаю все знают эту головоломку. Двигать пятнашки в пластиковой коробочке до получения заветного порядка цифр было очень интересным занятием. Вот и совсем недавно, в порядке спортивного интереса, я написал для себя пятнашки в которые бы можно было играть не только из окна браузера, но и с смартфона под управлением ОС Андроид или iOS.
Наша игра будет состоять из двух файлов лежащих в одной папочке, первый обзовём index.htm, а второй puzzle15.js
Содержимое html файла в течении всего поста меняться не будет, и собственно он должен выглядеть приблизительно так:
<html>
	<head>
		<meta charset="utf-8">
		<title>Пятнашки</title>
		<script src="puzzle15.js"></script>
	</head>
	<body>
		<canvas id="puzzle15">Пора уже в этой жизни что-то менять</canvas>
		<script>init();</script>
	</body>
</html>

Написанием второго файла мы и будем заняты. Итак изначально мы напишем функцию init, которая изменит размер нашего холста и закрасит его однородым цветом, т.е. содержимое файла puzzle15.js должно быть таким:
function init() {
	var canvas = document.getElementById("puzzle15");
	    canvas.width = 320; // задаём размеры холста
	    canvas.height = 320;
	var context = canvas.getContext("2d");
	    context.fillStyle = "#222"; // цвет "заливки"
	    context.fillRect(0, 0, canvas.width, canvas.height); // закрашиваем холст        
}

Если открыть браузером файл index.htm, то можно увидеть тёмно-серый прямоугольник. Для пятнашек я заготовил один класс в который вынесена вся игровая логика, этот класс собственно надо поместить в файл:
function game15() {
	var cellView = null;
	var numView = null;
	var arr = [[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,0]];
	var clicks = 0;
	function getNull() { // функция возвращает координату пустой клетки
		for (var i = 0; i < 4; i++) {
			for (var j = 0; j < 4; j++) {
				if (arr[j][i] === 0) {
					return{"x":i,"y":j};
				}
			}
		}
	};
	// функция возвращает произвольное логическое значение
	function getRandomBool() {
		if (Math.floor(Math.random() * 2) === 0) {
			return true;
		}
	}
	// метод возвращает число касаний
	this.getClicks = function() {
		return clicks;
	};
	// метод перемещает "пятнашку" в пустую клутку 
	this.move = function(x, y) {
		var nullX = getNull().x;
		var nullY = getNull().y;
		if (((x - 1 == nullX || x + 1 == nullX) && y == nullY) || ((y - 1 == nullY || y + 1 == nullY) && x == nullX)) {
			arr[nullY][nullX] = arr[y][x];
			arr[y][x] = 0;
			clicks++;
		}
	};
	// проверка условия победы
	this.victory = function() {
		var e = [[1,2,3,4], [5,6,7,8], [9,10,11,12], [13,14,15,0]];
		var res = true;
		for (var i = 0; i < 4; i++) {
			for (var j = 0; j < 4; j++) {
				if (e[i][j] != arr[i][j]) {
					res = false;
				}
			}
		}
		return res;
	};
	// метод "перемешивает" пятнашки
	this.mix = function(stepCount) {
		var x,y;
		for (var i = 0; i < stepCount; i++) {
			var nullX = getNull().x;
			var nullY = getNull().y;
			var hMove = getRandomBool();
			var upLeft = getRandomBool();
			if (!hMove && !upLeft) { y = nullY; x = nullX - 1;}
			if (hMove && !upLeft)  { x = nullX; y = nullY + 1;}
			if (!hMove && upLeft)  { y = nullY; x = nullX + 1;}
			if (hMove && upLeft)   { x = nullX; y = nullY - 1;}
			if (0 <= x && x <= 3 && 0 <= y && y <= 3) {
				this.move(x, y);
			}
		}
		clicks = 0;
	};
	// внешний вид пятнашки
	this.setCellView = function(func) {
		cellView = func;
	};
	// параметры шрифта цифр
	this.setNumView = function(func) {
		numView = func;
	};
	// Метод рисующий наши пятнашки на холсте
	this.draw = function(context, size) {
		for (var i = 0; i < 4; i++) {
			for (var j = 0; j < 4; j++) {
				if (arr[i][j] > 0) {
					if (cellView !== null) {
						cellView(j * size, i * size);
					}
					if (numView !== null) {
						numView();
						context.fillText(arr[i][j], j * size + size / 2, i * size + size / 2);
					}
				}
			}
		}
	};
}

Теперь нашу игру можно смело перевоплотить из статичного прямоугольника, в практически рабочие пятнашки
function init() {
	var canvas = document.getElementById("puzzle15");
	    canvas.width  = 320;
	    canvas.height = 320;
	var cellSize = canvas.width / 4;
	var context = canvas.getContext("2d");
	var field = new game15(); // создаём объект пятнашек
	    field.mix(350); // тщательно перемешиваем содердимое коробки
	    field.setCellView(function(x, y) { // задаём внешний вид пятнашек
	    	context.fillStyle = "#FFB93B";
	    	context.fillRect(x+1, y+1, cellSize-2, cellSize-2);
	    });
	    field.setNumView(function() { // параметры шрифта для цифр
	    	context.font = "bold "+(cellSize/2)+"px Sans";
	    	context.textAlign = "center";
	    	context.textBaseline = "middle";
	    	context.fillStyle = "#222";
	    });
	context.fillStyle = "#222";
	context.fillRect(0, 0, canvas.width, canvas.height);
	field.draw(context, cellSize);
}

Ну вот осталось добавить лишь обработку событий мыши и касаний, и у нас получиться вполне рабочая версия пятнашек, для этого в функцию init после последней строки field.draw(context, cellSize) добавим немного кода:
	function event(x, y) { // функция производит необходимые действие при клике(касанию)
		field.move(x, y);
		context.fillStyle = "#222";
		context.fillRect(0, 0, canvas.width, canvas.height);
		field.draw(context, cellSize);
		if (field.victory()) { // если головоломка сложена, то пятнашки заново перемешиваются
			alert("Собрано за "+field.getClicks()+" касание!"); // вывод сообщения о выигрыше!!
			field.mix(300);
			context.fillStyle = "#222";
			context.fillRect(0, 0, canvas.width, canvas.height);
			field.draw(context, cellSize);
		}
	}
	canvas.onclick = function(e) { // обрабатываем клики мышью
		var x = (e.pageX - canvas.offsetLeft) / cellSize | 0;
		var y = (e.pageY - canvas.offsetTop)  / cellSize | 0;
		event(x, y); // выхов функции действия
	};
	canvas.ontouchend = function(e) { // обрабатываем касания пальцем
		var x = (e.touches[0].pageX - canvas.offsetLeft) / cellSize | 0;
		var y = (e.touches[0].pageY - canvas.offsetTop)  / cellSize | 0;
		event(x, y);
	};

Кто хочет сразу поиграть для того ссылка.
Ничего сложного в коде нет, и всё пишется очень быстро. Спасибо пользователю hobbeat за идею и фотографию пятнашек на айПоде.
Tags:
Hubs:
Total votes 53: ↑49 and ↓4 +45
Views 45K
Comments 51
Comments Comments 51

Posts