[JavaScript] オセロ
オセロ
今回は JavaScript でオセロを作成しました。
See the Pen RwrrmEa by ヤマカサ (@yamakasa33) on CodePen.
デザイン
主にcssを参考にしました。
アルゴリズム
オセロを実装するには、石を置けるかどうかや、ひっくり返されるマスのリストが大変だと思いますが、愚直に実装します。
IDを付与
各マスにIDを割り振って、管理します。左上から、0, 1, 2, ... と割り振ると、 座標に対応するものは、
となり、 座標は、
となります。 は縦と横のマスの数です。 から ID を求めるには、
となります。
石を置く または 石をひっくり返す
function put(x, y, color) { const td = document.getElementById('cell-' + getId(x, y)); cells[y][x] = color; if (color == BLACK) { if (td.classList.contains('white')) { td.classList.remove('white'); numWhite--; } td.classList.add('black'); numBlack++; } else if (color == WHITE) { if (td.classList.contains('black')) { td.classList.remove('black'); numBlack--; } td.classList.add('white'); numWhite++; } }
マスに石を置けるかどうか
石を置ける条件は、石を置いたときにひっくり返されるマスが存在することなので、あるマスに注目した時、8方向を探索し、同色の意思で挟むことができるかを調べれば良いです。
function canPut(x, y, color) { if (cells[y][x] != 0) return false; for (let i = 0; i < 8; i++) { let flag = false; // 変えるマスが存在するかどうか for (let j = 1; j < N; j++) { const nx = x + j * dx[i]; const ny = y + j * dy[i]; if (nx >= N || nx < 0 || ny >= N || ny < 0) break; if (cells[ny][nx] == 0) break; if (cells[ny][nx] == color) { if (flag) return true; break; } else { flag = true; } } } return false; }
ひっくり返されるマスのリスト
上記の関数を拡張する感じで実装します。
function getReverseCellsId(x, y, color) { const ret = []; for (let i = 0; i < 8; i++) { const tmp = []; let flag = false; for (let j = 1; j < N; j++) { const nx = x + j * dx[i]; const ny = y + j * dy[i]; if (nx >= N || nx < 0 || ny >= N || ny < 0) break; if (cells[ny][nx] == 0) break; if (cells[ny][nx] != color) { tmp.push(getId(nx, ny)); flag = true; } else { if (flag) { Array.prototype.push.apply(ret, tmp); break; } break; } } } return ret; }
コンピュータのルーチン
評価値を計算して、最も価値の高い手を打ちます。ここの部分は改良の余地があります。
// コンピューターの思考 function think() { if(myTurn) return; let hightScore = -1000; let px = -1; let py = -1; for (let x = 0; x < N; x++) { for (let y = 0; y < N; y++) { if (!canPut(x, y, enColor)) continue; const tmpScore = getEval(x, y, enColor); if (hightScore < tmpScore) { hightScore = tmpScore; px = x; py = y; } } } if (px == -1) { myTurn = true; return; } const aryId = getReverseCellsId(px, py, enColor); console.log(aryId); put(px, py, enColor); aryId.forEach( e=> { const x = e % N; const y = Math.floor(e / N); put(x, y, enColor); }); myTurn = true; update(); } // 評価値 function getEval(x, y, color) { const aryId = getReverseCellsId(x, y, color); let v = 0; aryId.forEach( e => { const x = e % N; const y = Math.floor(e / N); v += WEIGHT[y][x]; }); return v; }