Учебный проект "15 puzzle" является частью материалов лаборатории создания сайтов.


"15 puzzle" — скрипт на JavaScript, разрабатываемый в рамках лаборатории создания сайтов как учебный (не обучающий) проект.

Цель: разработать скрипт на JavaScript (с применением HTML), реализующий функциональность игры Пятнашки.

Скачать скрипт

Примечание: Прошу не причислять скрипт к индусскому коду или говнокоду. Исходный код сохраняется с целью показать, какие ошибки допускают начинающие программисты, как их исправить и не допускать в дальнейшем.

Разработка

править

Создадим файл 15puzzle.html. Этот файл будет содержать и HTML-представление и сам скрипт.

Ошибка: Нужно разделять логику и представление, используя отдельно код HTML, CSS и JavaScript. Для исправления ошибки нужно создать также файлы 15puzzle.css и 15puzzle.js.
 
рисунок 1

Сначала создадим простейшее визуальное представление квадратной коробки и блоков (как на рисунке 1). Здесь будем использовать элементы table, tr, td.


Исходный код:

<table>
<tr><td>1</td><td>2</td><td>3</td><td>4</td>
</tr>
<tr>
<td>5</td><td>6</td><td>7</td><td>8</td>
</tr>
<tr>
<td>9</td><td>10</td><td>11</td><td>12</td>
</tr>
<tr>
<td>13</td><td>14</td><td>15</td><td> </td>
</tr>
</table>


Представление:

1234
5678
9101112
131415
Ошибка: Нужно соблюдать стандарты кодирования HTML. Для исправления ошибки вставим информацию о версии HTML (!DOCTYPE) и шапку веб-страницы. Теорию смотрите в HTML/Введение и в HTML/Структура веб-страницы.
 
рисунок 3

Теперь добавим в тег table дополнительные атрибуты: align, bgcolor, border, bordercolor, height, width.


Исходный код:

<table align="center" bgcolor="#FAEBD7" border="9" bordercolor="#DEB887" height="300px" width="300px">
<tr><td>1</td><td>2</td><td>3</td><td>4</td>
</tr>
<tr>
<td>5</td><td>6</td><td>7</td><td>8</td>
</tr>
<tr>
<td>9</td><td>10</td><td>11</td><td>12</td>
</tr>
<tr>
<td>13</td><td>14</td><td>15</td><td> </td>
</tr>
</table>
 
рисунок 4

Теперь добавим в теги tr дополнительные атрибуты: align, bgcolor.


Исходный код:

<table align="center" bgcolor="#FAEBD7" border="9" bordercolor="#DEB887" height="300px" width="300px">
<tr align="center" bgcolor="#F0F8FF">
<td>1</td><td>2</td><td>3</td><td>4</td>
</tr>
<tr align="center" bgcolor="#F0F8FF">
<td>5</td><td>6</td><td>7</td><td>8</td>
</tr>
<tr align="center" bgcolor="#F0F8FF">
<td>9</td><td>10</td><td>11</td><td>12</td>
</tr>
<tr align="center" bgcolor="#F0F8FF">
<td>13</td><td>14</td><td>15</td><td> </td>
</tr>
</table>
 
рисунок 5

Как видим, нам нужно сделать цвет каждой ячейки по-отдельности, чтобы цвет пустой ячейки был отличный от цвета блоков. Добавим в теги td дополнительный атрибут bgcolor, а из тегов tr удаляем этот атрибут.

Исходный код:

<table align="center" bgcolor="#FAEBD7" border="9" bordercolor="#DEB887" height="300px" width="300px">
<tr align="center">
<td bgcolor="#F0F8FF">1</td><td bgcolor="#F0F8FF">2</td><td bgcolor="#F0F8FF">3</td><td bgcolor="#F0F8FF">4</td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF">5</td><td bgcolor="#F0F8FF">6</td><td bgcolor="#F0F8FF">7</td><td bgcolor="#F0F8FF">8</td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF">9</td><td bgcolor="#F0F8FF">10</td><td bgcolor="#F0F8FF">11</td><td bgcolor="#F0F8FF">12</td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF">13</td><td bgcolor="#F0F8FF">14</td><td bgcolor="#F0F8FF">15</td><td> </td>
</tr>
</table>
 
рисунок 6

Теперь заполним блоки числами в случайном порядке, используя функции document.write(), Math.floor() и Math.random().

Исходный код:

<script>
var a1 = Math.floor(Math.random()*15 + 1);
var a2 = Math.floor(Math.random()*15 + 1);
var a3 = Math.floor(Math.random()*15 + 1);
var a4 = Math.floor(Math.random()*15 + 1);
var a5 = Math.floor(Math.random()*15 + 1);
var a6 = Math.floor(Math.random()*15 + 1);
var a7 = Math.floor(Math.random()*15 + 1);
var a8 = Math.floor(Math.random()*15 + 1);
var a9 = Math.floor(Math.random()*15 + 1);
var a10 = Math.floor(Math.random()*15 + 1);
var a11 = Math.floor(Math.random()*15 + 1);
var a12 = Math.floor(Math.random()*15 + 1);
var a13 = Math.floor(Math.random()*15 + 1);
var a14 = Math.floor(Math.random()*15 + 1);
var a15 = Math.floor(Math.random()*15 + 1);
</script>


<table align="center" bgcolor="#FAEBD7" border="9" bordercolor="#DEB887" height="300px" width="300px">
<tr align="center">
<td bgcolor="#F0F8FF"><script>document.write(a1);</script></td><td bgcolor="#F0F8FF"><script>document.write(a2);</script></td>
<td bgcolor="#F0F8FF"><script>document.write(a3);</script></td><td bgcolor="#F0F8FF"><script>document.write(a4);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF"><script>document.write(a5);</script></td><td bgcolor="#F0F8FF"><script>document.write(a6);</script></td>
<td bgcolor="#F0F8FF"><script>document.write(a7);</script></td><td bgcolor="#F0F8FF"><script>document.write(a8);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF"><script>document.write(a9);</script></td><td bgcolor="#F0F8FF"><script>document.write(a10);</script></td>
<td bgcolor="#F0F8FF"><script>document.write(a11);</script></td><td bgcolor="#F0F8FF"><script>document.write(a12);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF"><script>document.write(a13);</script></td><td bgcolor="#F0F8FF"><script>document.write(a14);</script></td>
<td bgcolor="#F0F8FF"><script>document.write(a15);</script></td><td> </td>
</tr>
</table>
 
рисунок 7

Как видно из рисунка 6, исказились размеры блоков. Для исправления этих размеров добавим в теги td первого ряда блоков тег width.


Исходный код:

<script>
var a1 = Math.floor(Math.random()*15 + 1);
var a2 = Math.floor(Math.random()*15 + 1);
var a3 = Math.floor(Math.random()*15 + 1);
var a4 = Math.floor(Math.random()*15 + 1);
var a5 = Math.floor(Math.random()*15 + 1);
var a6 = Math.floor(Math.random()*15 + 1);
var a7 = Math.floor(Math.random()*15 + 1);
var a8 = Math.floor(Math.random()*15 + 1);
var a9 = Math.floor(Math.random()*15 + 1);
var a10 = Math.floor(Math.random()*15 + 1);
var a11 = Math.floor(Math.random()*15 + 1);
var a12 = Math.floor(Math.random()*15 + 1);
var a13 = Math.floor(Math.random()*15 + 1);
var a14 = Math.floor(Math.random()*15 + 1);
var a15 = Math.floor(Math.random()*15 + 1);
</script>


<table align="center" bgcolor="#FAEBD7" border="9" bordercolor="#DEB887" height="320px" width="320px">
<tr align="center">
<td bgcolor="#F0F8FF" width="80px"><script>document.write(a1);</script></td>
<td bgcolor="#F0F8FF" width="80px"><script>document.write(a2);</script></td>
<td bgcolor="#F0F8FF" width="80px"><script>document.write(a3);</script></td>
<td bgcolor="#F0F8FF" width="80px"><script>document.write(a4);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF"><script>document.write(a5);</script></td><td bgcolor="#F0F8FF"><script>document.write(a6);</script></td>
<td bgcolor="#F0F8FF"><script>document.write(a7);</script></td><td bgcolor="#F0F8FF"><script>document.write(a8);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF"><script>document.write(a9);</script></td><td bgcolor="#F0F8FF"><script>document.write(a10);</script></td>
<td bgcolor="#F0F8FF"><script>document.write(a11);</script></td><td bgcolor="#F0F8FF"><script>document.write(a12);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF"><script>document.write(a13);</script></td><td bgcolor="#F0F8FF"><script>document.write(a14);</script></td>
<td bgcolor="#F0F8FF"><script>document.write(a15);</script></td><td> </td>
</tr>
</table>
 
рисунок 8

Как видно из рисунка 7, совпадают числа некоторых блоков. Напишем функцию mismatch(), устраняющую эту проблему.


Исходный код:

<script>
var a1 = Math.floor(Math.random()*15 + 1);
var a2 = Math.floor(Math.random()*15 + 1);
mismatch(2);
var a3 = Math.floor(Math.random()*15 + 1);
mismatch(3);
var a4 = Math.floor(Math.random()*15 + 1);
mismatch(4);
var a5 = Math.floor(Math.random()*15 + 1);
mismatch(5);
var a6 = Math.floor(Math.random()*15 + 1);
mismatch(6);
var a7 = Math.floor(Math.random()*15 + 1);
mismatch(7);
var a8 = Math.floor(Math.random()*15 + 1);
mismatch(8);
var a9 = Math.floor(Math.random()*15 + 1);
mismatch(9);
var a10 = Math.floor(Math.random()*15 + 1);
mismatch(10);
var a11 = Math.floor(Math.random()*15 + 1);
mismatch(11);
var a12 = Math.floor(Math.random()*15 + 1);
mismatch(12);
var a13 = Math.floor(Math.random()*15 + 1);
mismatch(13);
var a14 = Math.floor(Math.random()*15 + 1);
mismatch(14);
var a15 = Math.floor(Math.random()*15 + 1);
mismatch(15);

function mismatch(n){
	for (var i=1;i<n;i++){
		if(window['a'+n]==window['a'+i]){
			window['a'+n]=Math.floor(Math.random()*15 + 1);
			i=0;
		}
	}	
}
</script>


<table align="center" bgcolor="#FAEBD7" border="9" bordercolor="#DEB887" height="320px" width="320px">
<tr align="center">
<td bgcolor="#F0F8FF" width="80px"><script>document.write(a1);</script></td>
<td bgcolor="#F0F8FF" width="80px"><script>document.write(a2);</script></td>
<td bgcolor="#F0F8FF" width="80px"><script>document.write(a3);</script></td>
<td bgcolor="#F0F8FF" width="80px"><script>document.write(a4);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF"><script>document.write(a5);</script></td><td bgcolor="#F0F8FF"><script>document.write(a6);</script></td>
<td bgcolor="#F0F8FF"><script>document.write(a7);</script></td><td bgcolor="#F0F8FF"><script>document.write(a8);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF"><script>document.write(a9);</script></td><td bgcolor="#F0F8FF"><script>document.write(a10);</script></td>
<td bgcolor="#F0F8FF"><script>document.write(a11);</script></td><td bgcolor="#F0F8FF"><script>document.write(a12);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF"><script>document.write(a13);</script></td><td bgcolor="#F0F8FF"><script>document.write(a14);</script></td>
<td bgcolor="#F0F8FF"><script>document.write(a15);</script></td><td> </td>
</tr>
</table>

Добавим функции перемещения блоков an(), ap() и используем их с помощью события onClick. Теперь скрипт работоспособен и уже можно играть.


Исходный код:

<script>
var a1 = Math.floor(Math.random()*15 + 1);
var a2 = Math.floor(Math.random()*15 + 1);
mismatch(2);
var a3 = Math.floor(Math.random()*15 + 1);
mismatch(3);
var a4 = Math.floor(Math.random()*15 + 1);
mismatch(4);
var a5 = Math.floor(Math.random()*15 + 1);
mismatch(5);
var a6 = Math.floor(Math.random()*15 + 1);
mismatch(6);
var a7 = Math.floor(Math.random()*15 + 1);
mismatch(7);
var a8 = Math.floor(Math.random()*15 + 1);
mismatch(8);
var a9 = Math.floor(Math.random()*15 + 1);
mismatch(9);
var a10 = Math.floor(Math.random()*15 + 1);
mismatch(10);
var a11 = Math.floor(Math.random()*15 + 1);
mismatch(11);
var a12 = Math.floor(Math.random()*15 + 1);
mismatch(12);
var a13 = Math.floor(Math.random()*15 + 1);
mismatch(13);
var a14 = Math.floor(Math.random()*15 + 1);
mismatch(14);
var a15 = Math.floor(Math.random()*15 + 1);
mismatch(15);
var a16 = ' ';

function mismatch(n){
	for (var i=1;i<n;i++){
		if(window['a'+n]==window['a'+i]){
			window['a'+n]=Math.floor(Math.random()*15 + 1);
			i=0;
		}
	}	
}

var a;
var b;
var с = ' ';
function an(){
	document.getElementById('td'+b).innerHTML=window["a"+a];document.getElementById('td'+b).style.background = '#F0F8FF';
	window["a"+b]=window["a"+a];
	}
function ap(){
	document.getElementById('td'+a).innerHTML=с;document.getElementById('td'+a).style.background = '#FAEBD7';
	window["a"+a]=с
	}
</script>


<table align="center" bgcolor="#FAEBD7" border="9" bordercolor="#DEB887" height="320px" width="320px">
<tr align="center">
<td bgcolor="#F0F8FF" width="80px" id="td1" onclick="if(window['a5']==с){a=1;b=5;an();ap();}if(window['a2']==с){a=1;b=2;an();ap();}">
	<script>document.write(a1);</script></td>
<td bgcolor="#F0F8FF" width="80px" id="td2" onclick="if(window['a1']==с){a=2;b=1;an();ap();}if(window['a6']==с){a=2;b=6;an();ap();}
	if(window['a3']==с){a=2;b=3;an();ap();}">
	<script>document.write(a2);</script></td>
<td bgcolor="#F0F8FF" width="80px" id="td3" onclick="if(window['a2']==с){a=3;b=2;an();ap();}if(window['a4']==с){a=3;b=4;an();ap();}
	if(window['a7']==с){a=3;b=7;an();ap();}">
	<script>document.write(a3);</script></td>
<td bgcolor="#F0F8FF" width="80px" id="td4" onclick="if(window['a3']==с){a=4;b=3;an();ap();}if(window['a8']==с){a=4;b=8;an();ap();}">
	<script>document.write(a4);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF" id="td5" onclick="if(window['a1']==с){a=5;b=1;an();ap();}if(window['a6']==с){a=5;b=6;an();ap();}
	if(window['a9']==с){a=5;b=9;an();ap();}">
	<script>document.write(a5);</script></td>
<td bgcolor="#F0F8FF" id="td6" onclick="if(window['a2']==с){a=6;b=2;an();ap();}if(window['a5']==с){a=6;b=5;an();ap();}
	if(window['a7']==с){a=6;b=7;an();ap();}if(window['a10']==с){a=6;b=10;an();ap();}">
	<script>document.write(a6);</script></td>
<td bgcolor="#F0F8FF" id="td7" onclick="if(window['a3']==с){a=7;b=3;an();ap();}if(window['a6']==с){a=7;b=6;an();ap();}
	if(window['a8']==с){a=7;b=8;an();ap();}if(window['a11']==с){a=7;b=11;an();ap();}">
	<script>document.write(a7);</script></td>
<td bgcolor="#F0F8FF" id="td8" onclick="if(window['a4']==с){a=8;b=4;an();ap();}if(window['a7']==с){a=8;b=7;an();ap();}
	if(window['a12']==с){a=8;b=12;an();ap();}">
	<script>document.write(a8);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF" id="td9" onclick="if(window['a5']==с){a=9;b=5;an();ap();}if(window['a10']==с){a=9;b=10;an();ap();}
	if(window['a13']==с){a=9;b=13;an();ap();}">
	<script>document.write(a9);</script></td>
<td bgcolor="#F0F8FF" id="td10" onclick="if(window['a6']==с){a=10;b=6;an();ap();}if(window['a9']==с){a=10;b=9;an();ap();}
	if(window['a11']==с){a=10;b=11;an();ap();}if(window['a14']==с){a=10;b=14;an();ap();}">
	<script>document.write(a10);</script></td>
<td bgcolor="#F0F8FF" id="td11" onclick="if(window['a7']==с){a=11;b=7;an();ap();}if(window['a10']==с){a=11;b=10;an();ap();}
	if(window['a12']==с){a=11;b=12;an();ap();}if(window['a15']==с){a=11;b=15;an();ap();}">
	<script>document.write(a11);</script></td>
<td bgcolor="#F0F8FF" id="td12" onclick="if(window['a8']==с){a=12;b=8;an();ap();}if(window['a11']==с){a=12;b=11;an();ap();}
	if(window['a16']==с){a=12;b=16;an();ap();}">
	<script>document.write(a12);</script></td>
</tr>
<tr align="center">
<td bgcolor="#F0F8FF" id="td13" onclick="if(window['a9']==с){a=13;b=9;an();ap();}if(window['a14']==с){a=13;b=14;an();ap();}">
	<script>document.write(a13);</script></td>
<td bgcolor="#F0F8FF" id="td14" onclick="if(window['a10']==с){a=14;b=10;an();ap();}if(window['a13']==с){a=14;b=13;an();ap();}
	if(window['a15']==с){a=14;b=15;an();ap();}">
	<script>document.write(a14);</script></td>
<td bgcolor="#F0F8FF" id="td15" onclick="if(window['a11']==с){a=15;b=11;an();ap();}if(window['a14']==с){a=15;b=14;an();ap();}
	if(window['a16']==с){a=15;b=16;an();ap();}">
	<script>document.write(a15);</script></td>
<td bgcolor="#FAEBD7" id="td16" onclick="if(window['a12']==с){a=16;b=12;an();ap();}if(window['a15']==с){a=16;b=15;an();ap();}"></td>
</tr>
</table>

Тестирование

править

Скрипт работает в браузерах Mozilla Firefox 3.6.10 и Internet Explorer 8 версии.

Результаты

править
  • Разработана игра-скрипт "15 puzzle" версия 0.1 (см. исходный код). Размер исходного кода — 5 100 байт.
  • Проект на Sourceforge
  • Демо скрипта
  • Код представлен к обсуждению на форуме Javascript.RU, где были получены полезные замечания и советы по исправлению и улучшению скрипта:
  • Соблюдать стандарты кодирования,
  • Использовать CSS,
  • Разделять логику и представление,
  • Не использовать document.write без необходимости,
  • Использовать методы DOM,
  • Создать массив из 15 элементов и перемешать их,
  • Включить защиту от неразрешимых комбинаций.