15 puzzle
"15 puzzle" — скрипт на JavaScript, разрабатываемый в рамках лаборатории создания сайтов как учебный (не обучающий) проект.
Цель: разработать скрипт на JavaScript (с применением HTML), реализующий функциональность игры Пятнашки.
Примечание: Прошу не причислять скрипт к индусскому коду или говнокоду. Исходный код сохраняется с целью показать, какие ошибки допускают начинающие программисты, как их исправить и не допускать в дальнейшем.
Разработка
правитьЭтап 1
правитьСоздадим файл 15puzzle.html. Этот файл будет содержать и HTML-представление и сам скрипт.
- Ошибка: Нужно разделять логику и представление, используя отдельно код HTML, CSS и JavaScript. Для исправления ошибки нужно создать также файлы 15puzzle.css и 15puzzle.js.
Этап 2
правитьСначала создадим простейшее визуальное представление квадратной коробки и блоков (как на рисунке 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>
Представление:
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15
- Ошибка: Нужно соблюдать стандарты кодирования 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>
Этап 9
правитьДобавим функции перемещения блоков 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 элементов и перемешать их,
- Включить защиту от неразрешимых комбинаций.