という方、初学者向けの解説記事です。
この記事を読んでできること
・オセロの作り方を理解することができる
・簡単なCPUを実装することができる
対象読者様
②コードの意味・作り方を理解したい方
上記のような読者様を想定しております。
当記事では javascriptで初めて何かを作りたい方 を想定している為
4つのパートに分けて解説をしていきます。
今回は第3回(前回はこちら:リバーシの作り方を初学者向けに1行づつ解説!②)
クリックすると石が置けて、挟めたら石をひっくり返すまで解説していきます。
概要説明
今回はオセロを作るうえで必須の石をひっくり返す判定です。
正直、難しいですが要点に分けての解説に努めます。
一緒に頑張りましょうm(__)m
◆ポイント
②canReverceMasu() → ひっくり返せる可能性のあるマスの座標を返す
③canReverceHoukou() → ②のマスがひっくり返せるか判定して配列を返す
3つの関数は連動しており、中心となるのは②の関数です。
②⇒③⇒①の順に解説していきます。
canReverceMasu() を理解しよう!
function canReverceMasu(i, j, color) {
if (data[i][j] === KURO || data[i][j] === SIRO) {
return []; // タッチしたところに石があれば何もしない
}
let touchedMawari = [
[-1, -1],
[0, -1],
[1, -1],
[-1, 0],
[1, 0],
[-1, 1],
[0, 1],
[1, 1]
];
let result = [];
for (let p = 0; p < touchedMawari.length; p++) {
let canReverseMasu = canReverceHoukou(
i,
j,
touchedMawari[p][0],
touchedMawari[p][1],
color
);
result = result.concat(canReverseMasu);
}
return result;
}
この関数でしていることは配列でひっくり返せる可能性のあるマスの座標を返すです。
座標…? 配列… ? となると思います。部分毎に分割してみましょう。
2次元配列に慣れよう!
まず、配列についてです。こちらをご覧ください。
let touchedMawari = [
[-1, -1],
[0, -1],
[1, -1],
[-1, 0],
[1, 0],
[-1, 1],
[0, 1],
[1, 1]
];
初学者の大きな壁ともいえる配列の中の配列、いわゆる2次元配列です。
初学者の皆様にはその「わかった」を是非体感いただきたいのです。
配列の中身をピンポイントで指定しよう
2次元配列の最大の利点は情報量をたくさん入れることができるです。
変数を使う時、『letで宣言して…変数に3を入れて…ここで代入して…』としますが
いっぱい数字や文字を宣言するのは面倒ですし、ミスの元です。
たくさんの数字の配列に見えますが、下記の方法で簡単に数字を取り出すことができます。
是非ご参考下さい。
let touchedMawari = [ [●,●] [●,●] … ] をイメージしよう!
let touchedMawari = [
[-1, -1],
[0, -1],
[1, -1],
[-1, 0],
[1, 0],
[-1, 1],
[0, 1],
[1, 1]
];
本題です。
まず、この配列はクリックした座標に周りの8マスを探すことが目的です。
絵で示すとこのようになります。
クリックした座標の周りに相手の色があるかを調べてその座標を格納します。
タッチした座標にすでに白か黒が入っている場合は空の配列を返すという処理になります。空の配列を返される=ひっくり返せないマスをクリックしたことを意味します。
では、空ではない配列とはどのようなものでしょうか。
配列を返そう
空ではない、つまり配列が入ります。
例えばこんな感じです。
canReverseMasu = [
[3, 3],
[2, 2]
]
この配列はひっくり返せる可能性のあるマスの座標
まとめると、下記の部分は最後に配列をreturn
で返すためのものです。
result return で配列を返そう
let result = [];
for (let p = 0; p < touchedMawari.length; p++) {
let canReverseMasu = canReverceHoukou(
i,
j,
touchedMawari[p][0],
touchedMawari[p][1],
color
);
result = result.concat(canReverseMasu);
} return result;
result
を宣言②
canReverseMasu
に配列を格納 ※canReverceHoukou
で特定しますが、それは後述します③
result.concat(〇〇)
で変数result
に〇〇
を合体return result
で配列を返す■ p は
touchedMawari
の配列の数。つまり0~8です■
touchedMawari[p][0] / touchedMawari[p][1]
・p = 0: [0][0] → -1 / [0][1] → -1
・p = 1: [1][0] → 0 / [1][1] → -1
・p = 2: [2][0] → 1 / [2][1] → -1
canReverceHoukou() を理解しよう!
function canReverceHoukou(i, j, Xhoukou, Yhoukou, color) {
let checkedX = i + Xhoukou;
let checkedY = j + Yhoukou;
if (
checkedX < 0 ||
checkedY < 0 ||
checkedX > 7 ||
checkedY > 7 ||
data[checkedX][checkedY] === color ||
data[checkedX][checkedY] === 0
) {
return []; // 盤外、同色、空ならfalse(挟めない)
}
// 挟める候補の座標 = クリックした座標から見て違う色の石を2次元配列で格納
let canReverseMasu = [];
canReverseMasu.push([checkedX, checkedY]);
while (true) {
checkedX += Xhoukou;
checkedY += Yhoukou;
if (
checkedX < 0 ||
checkedY < 0 ||
checkedX > 7 ||
checkedY > 7 ||
data[checkedX][checkedY] === 0
) {
return []; // 盤外・空・false ⇒ 挟むことができない
}
if (data[checkedX][checkedY] === color) {
return canReverseMasu;
} else {
canReverseMasu.push([checkedX, checkedY]);
}
}
}
この関数の目的は canReverseMasu
に渡した配列がtrueかfalseか判定するです。
ブロック毎に分けてますので、引数が何にあたるかだけを記載します。
■
Xhoukou
・Yhoukou
i,j 周りの座標のセットcheckedX・checkedY とは?
let checkedX = i + Xhoukou
let checkedY = j + Yhoukou
まず、こちらの画像を思い出してください。
これはタッチした周辺の8マスを調べていますが、オセロのひっくり返す時のルールを思い出すと…そう、挟まないといけないのです。
つまり、ここでは周囲の8マスを調べるというより、8方向を調べると理解いただくとよいです。すると、この部分もすぐに意味が分かります。
checkedX += Xhoukou
checkedY += Yhoukou
『周囲8マスの座標checkedX/Y
にもう一度X/Yhoukou
を加える』
これで、隣接したマスの方向を調べることができるのです。
挟めない条件
「挟めない条件を全て満たさない時、石をひっくり返しなさい」
という条件文がここにはいくつかでてきます。
if (
checkedX < 0 ||
checkedY < 0 ||
checkedX > 7 ||
checkedY > 7 ||
data[checkedX][checkedY] === color ||
data[checkedX][checkedY] === 0
) {
return []; // 盤外、同色、空ならfalse(挟めない)
}
if (
checkedX < 0 ||
checkedY < 0 ||
checkedX > 7 ||
checkedY > 7 ||
data[checkedX][checkedY] === 0
) {
return []; // 盤外・空・false ⇒ 挟むことができない
}
checkedX < 0
はXが-1以下、つまり盤の外
checkedY > 7
もXが8以上、も同じく盤の外
data[checkedX][checkedY] === color
は同じ色
data[checkedX][checkedY] === 0
は配列が空っぽ、何もない
日本語におこすとこのようになるでしょうか。
これに該当する場合は return []
で空っぽの配列が返る、ひっくり返す処理は行われないことなります。
canReverseMasu に仮の配列を格納しよう!
ーーーーーーーーーー
data[checkedX][checkedY] === color
ーーーーーーーーーー
let canReverseMasu = [];
canReverseMasu.push([checkedX, checkedY]);
ーーーーーーーーーー
if (data[checkedX][checkedY] === color) {
return canReverseMasu;
} else {
canReverseMasu.push([checkedX, checkedY]);
}
}
ーーーーーーーーーー
↑は解説用にーーーーーーーーーー
で一部のコードを省略しています。
緑の部分は挟めない条件を満たさない = 挟める条件を満たした座標を
canReverseMasuに仮の配列として格納します。
あくまで仮の配列を一度格納する都合上、仮の配列の方向に手番のプレイヤーの石の色があるか調べなければなりません。
data[checkedX][checkedY] === color
はクリックした座標に隣接する石の色が同じか
data[checkedX][checkedY] === color
は仮の座標の先に手番のプレイヤーと同じ色があるかをそれぞれ調べています。
見た目は同じですが、処理の順番で意味が異なりますので要注意です。
この青の部分は満たす場合は挟める条件を満たすことになりますので
return canReverseMasu
で格納した配列を返すことができます。
canReverceHoukou() を理解しよう!
function canReverce(color) {
for (let x = 0; x < 8; x++) {
for (let y = 0; y < 8; y++) {
let canReverseMasu= canReverceMasu(x, y, color);
if (canReverseMasu.length > 0) {
return true;
}
}
}
return false;
}
あとほんの少しですので頑張りましょう!
注目していただきたいのはcanReverseMasu
この中には下の流れでひっくり返してよい座標が入っていることをおさらしましょう。
canReverceHoukouに渡しました。
関数 canReverceHoukou では 渡された座標がひっくり返してよいか判定し、
ひっくり返してよい座標をcanReverceMasuに返しました。
返ってきた座標はどうなるの?
配列があれば
return true
でひっくり返す処理を行います。canReverseMasu
の配列の数だけ[k][0]
マスをSIRO
にするという処理が行われます。let canReverseMasu = canReverceMasu(tate, yoko, SIRO);
if (canReverseMasu.length > 0) {
for (let k = 0; k < canReverseMasu.length; k++) {
put(canReverseMasu[k][0], canReverseMasu[k][1], SIRO);
}
put(tate, yoko, SIRO);
kousin();
}
入っていない場合、ひっくり返せるものがない場合は空の配列となり
length
は0、return false
でひっくり返す処理自体行われません。
まとめ
ここまでで白黒の石をはさむ処理編終了です!
大変お疲れさまでした!
このパート、とても難しかったと思いますが多次元配列への理解が深まれば今後がとても楽になります。