今回は、書籍ゲームで学ぶJavascript入門で紹介されている
15puzzle知識定着のために自分なりに解説をしてみます。
15puzzleは数字を順番に並び替えていくゲームです。
(これは画像です)
<body onload="init()">
<table id="table"></table>
</body>
</html>
まず読み込んだら初期化のinit()が起動します。
body onload ="init()"の所です。
<body onload="init()">
<table id="table"></table>
</body>
</html>
##初期化(数タイルを表示させる)
初期化ですることは、
1~15までの数字を順番に表示をさせ、その後、順番をバラバラに表示させます。
このようなhtmlを出力できたらOKです。
<table id="table">
<tr>
<td class="tile">1</td>
<td class="tile">2</td>
<td class="tile">3</td>
<td class="tile">4</td>
</tr>
<tr>
<td class="tile">5</td>
<td class="tile">6</td>
<td class="tile">7</td>
<td class="tile">8</td>
</tr>
<tr>
<td class="tile">9</td>
<td class="tile">10</td>
<td class="tile">11</td>
<td class="tile">12</td>
</tr>
<tr>
<td class="tile">13</td>
<td class="tile">14</td>
<td class="tile">15</td>
<td class="tile">16</td>
</tr>
</table>
これをjsで段階的に書いてみると
var tr = document.createElement("tr")
~tdのfor処理4回分
最後にtrの閉じタグをつける
var table = docment.getElementById("table");
var tr = document.createElement("tr");
for (var j=0;j<4;j++){
var td = document.createElement("td");
td.textContent = j;
td.className ="tile";
tr.appendChild(td); //trの最後の所にtdを追加。閉じタグにて
}
table.appendChild(tr);
これだと0~3までしかできないので
trが4回分をつなげるとこうなります。
var table = document.getElementById("table");
//子のtrが4回分
for (var i = 0;i<4;i++){
var tr = document.createElement("tr");
//孫のtdが4回分
for (var j=0;j<4;j++){
var td = document.createElement("td");
td.textContent = i*4+j;//0-15まで
td.className ="tile";
tr.appendChild(td); //trの最後の所にtdを追加。閉じタグにて
}
//子の最後にtrの閉じタグを入れる
table.appendChild(tr);
}
見た目的にこれでOKですが
パズル動作をするために取得した要素のプロパティを入れておく必要があります。
また値が0の所は、空白にしてパズルの入れ替えポイントにします。
また数字の入れ替え処理ができるようにclick関数を、onclickに設定します。
click関数は後ほど出ます。
var table = document.getElementById("table");
//子のtrが4回分
for (var i = 0;i<4;i++){
var tr = document.createElement("tr");
//孫のtdが4回分
for (var j=0;j<4;j++){
var td = document.createElement("td");
td.className ="tile";
//値をindexとして
var index = i*4+j;
td.textContent = index;
td.index = index;
td.value = index;
td.textContent = index == 0 ? "" : index; //三項演算子
td.onclick = click; //onclickにclick関数を設定
tr.appendChild(td); //trの最後の所にtdを追加。閉じタグにて
}
//子の最後にtrの閉じタグを入れる
table.appendChild(tr);
}
三項演算子はif文の省略した書き方です。
index が0なら、""を。 0じゃないならindexを
「index == 0 ? "" :index;」
その結果を
td.textContentに「代入=」しています。
まだ未完成ですが並び替えを実行するための
click関数を考えます。
click関数は並び替えを行いますが、
並びがどうなってるかjavascript側であらかじめ覚えてくれたほうが楽です。
click関数を呼び出したときに、「えーと、現在の並びがこうだから。。。」と取得してると効率が悪いです。
(googleのスプレットシートとGASの処理で、何度もスプレットシートの値を取得するのでなく、
スプレット全体を配列で一旦全部記憶させて、配列からデータを探させるのと同じ考え方です。)
// グローバル変数(並びを入れとくやつ)
var tiles = [];
// 初期化関数
function init() {
var table = document.getElementById("table");
for (var i = 0 ; i < 4 ; i++) {
var tr = document.createElement("tr");
for (var j = 0 ; j < 4 ; j++) {
var td = document.createElement("td");
td.className = "tile";
var index = i * 4 + j;
td.index = index;
td.value = index;
td.textContent = index == 0 ? "" : index;
td.onclick = click;
tr.appendChild(td);
//並びを覚えさせる
tiles.push(td);
}
table.appendChild(tr);
}
}
これでやっと並び替えclick処理を考えられます。
##クリック処理
クリックをした時の処理は
「押した数字が隣が空白で、押した数字が空白に移動する」です。
移動の仕方は上下左右なので4パターンとなります。
後ほど記述するswap関数には、入れ替えの場所を引数で渡します。
その4パターンのうち、どの処理を行うか選別してるようにします。
画像のようにindexの一般化から値を様子を見ます。
function click(e) {
//クリックした場所を取得
var i = e.srcElement.index;
if (tiles[i - 4].value == 0) {
swap(i, i - 4);//上移動の処理
} else if (tiles[i + 4].value == 0) {
swap(i, i + 4);//下移動
} else if (tiles[i - 1].value == 0) {
swap(i, i - 1);//左移動
} else if (tiles[i + 1].value == 0) {
swap(i, i + 1);//右移動
}
}
これで良さそうに見えますが
i=1のときに、tiles[-3]の値を見ようとします。そんな値はないのでエラーが出ちゃいます。
なので、
上移動なら、まずクリックされたものが一番上でないこと。
下移動なら、まずクリックされたものが一番下でないこと。とする必要があります。
function click(e) {
var i = e.srcElement.index;
if (i - 4 >= 0 && tiles[i - 4].value == 0) {//最上位ではなく、上の値が0なら上移動
swap(i, i - 4);
} else if (i + 4 < 16 && tiles[i + 4].value == 0) {
swap(i, i + 4);
} else if (i % 4 != 0 && tiles[i - 1].value == 0) {
swap(i, i - 1);
} else if (i % 4 != 3 && tiles[i + 1].value == 0) {
swap(i, i + 1);
}
}
##並び替えswap関数
並び替える場所を引数に渡せば、並び替えをする関数です。
textContentとvalueを入れ替えています。
function swap(i, j) {
//一旦格納する
var tmp = tiles[i].value;
tiles[i].textContent = tiles[j].textContent;
tiles[i].value = tiles[j].value;
tiles[j].textContent = tmp;
tiles[j].value = tmp;
}
##バラバラに並び替える
ここまでのプログラムですと、順番に数字が並ぶだけです。
一応ゲームのため、あらかじめバラバラに並び替えておく必要があります。
今回はclickしたら、並び替えswapが発動するのでそれを使います。
適当なところをランダムに押すプログラムを置きます。
for(var i = 0 ;i<1000;i++){
click({srcElement:{index:Math.floor(Math.random()*16)}})
}
0-15のランダムにとり、その位置のsrcElementをクリックされます。
click→swapまでたまたま行ったもので、並び替えが行われます。
##最後に
全体図はサンプルコードの配布になってしまうので控えます。
詳しくはこちらの書籍を購入お願いします。