オセロをつくる 先読みさせてコンピューターをもっと強くするで作成したオセロをJavaScriptで作ってみました。実際にはTypeScriptで書いたものをコンパイルしてJavaScriptに変換しています。やっぱりデスクトップアプリよりもwebで遊べるもののほうが見る側としてはいいですよね。
↓クリックすれば遊べます。ためしにどうぞ。
ではコードを示します。といってもアルゴリズムはC#と同じで書き方が違うだけです。
以下はセルの場所と色を管理するクラスです。IsEmpty関数は石が置かれていないかどうかを調べます。Colorプロパティが”green”であれば石は置かれていないと判断します。Draw関数が呼び出されたら背景を緑で塗りつぶし、石を色付きの円で表示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
class Cell { constructor(x: number, y: number, color: string) { this._posX = x; this._posY = y; this._color = color; } private _posX: number; get PosX() { return this._posX; } private _posY: number; get PosY() { return this._posY; } private _color: string; set Color(value) { this._color = value; } get Color() { return this._color; } IsEmpty(): boolean { return this.Color == "green"; } Draw(): void { con.fillStyle = "green"; con.fillRect(CellSize * this.PosX, CellSize * this.PosY, CellSize, CellSize); con.fillStyle = this.Color; // 塗りつぶしの色 con.beginPath(); let centerX = CellSize * this.PosX + CellSize / 2; let centerY = CellSize * this.PosY + CellSize / 2; con.arc(centerX, centerY, CellSize/2-2, 0, 2 * Math.PI, false); con.fill(); } } |
以下はコンピュータの次の手の候補の情報を管理するクラスです。
1 2 3 4 5 6 7 8 9 |
class NextCandidate { constructor(cell: Cell, count: number){ this.HandsCount = count; this.Cell = cell; } HandsCount: number; Cell: Cell; } |
以下はグローバル変数です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
let YourColor = "black"; let EnemyColor = "white"; let CellSize = 28; let isYourTurn: boolean = false; let ColumMax = 8; let RowMax = 8; let Cells: Cell[] = []; let can: HTMLCanvasElement = null; let con: CanvasRenderingContext2D = null; Main(); |
本体部分になるMain()関数を示します。
最初に盤面を表示するためのセルをつくります。そしてクリックされたときに対応できるようにEventListenerを追加してクリックされたら着手できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function Main() { can = <HTMLCanvasElement>document.getElementById('can'); con = can.getContext("2d"); can.width = CellSize * ColumMax; can.height = CellSize * RowMax + 100; can.style.border = "1px solid #111"; CreateCells(); GameStart(); can.addEventListener('click', function (e) { let offsetX = e.offsetX; // =>要素左上からのx座標 let offsetY = e.offsetY; // =>要素左上からのy座標 OnCellClicked(Math.floor(offsetX / CellSize), Math.floor(offsetY / CellSize)); }); } |
ShowString関数は盤の下側に「そこには打てません」などの文字列を表示するためのものです。
1 2 3 4 5 6 7 8 9 10 11 12 |
function ShowString(str1: string, str2: string) { con.clearRect(0, 0, can.width, can.height); DrawCells(); con.font = "14px 'MS ゴシック'"; con.fillStyle = "black"; let y1 = CellSize * RowMax + 30; let y2 = CellSize * RowMax + 50; con.fillText(str1, 10, y1); con.fillText(str2, 10, y2); } |
CreateCells関数は最初に1回だけ実行されるセルオブジェクトの配列を生成する関数です。
1 2 3 4 5 6 7 |
function CreateCells() { for (let x = 0; x < ColumMax; x++) { for (let y = 0; y < RowMax; y++) { Cells.push(new Cell(x, y, "green")); } } } |
GetCell関数はセルの配列から指定した位置のセルを取得するためのものです。
1 2 3 |
function GetCell(cells: Cell[], x: number, y: number): Cell { return cells.find(cell => cell.PosX == x && cell.PosY == y); } |
GameStart関数は名前のとおりゲームを開始するときによばれる関数です。GetCell関数で初期配置の位置に石をセットします。そして後述のDrawCells関数を呼び出してCanvasを再描画をします。
1 2 3 4 5 6 7 8 9 10 11 |
function GameStart() { Cells.forEach(x => x.Color = "green"); GetCell(Cells, 3, 3).Color = "black"; GetCell(Cells, 4, 4).Color = "black"; GetCell(Cells, 3, 4).Color = "white"; GetCell(Cells, 4, 3).Color = "white"; DrawCells(); isYourTurn = true; } |
DrawCells関数はセルの状態が変化したときに全体を再描画するときに呼び出す関数です。セルを描画したあと境界線を描画します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function DrawCells() { Cells.forEach(x => x.Draw()); con.strokeStyle = "black"; // 縦横の線を描画する for (let i = 0; i <= RowMax; i++) { con.beginPath(); con.moveTo(0, CellSize * i); con.lineTo(CellSize * ColumMax, CellSize * i); con.stroke(); } for (let i = 0; i < ColumMax; i++) { con.beginPath(); con.moveTo(CellSize * i, 0); con.lineTo(CellSize * i, CellSize * ColumMax); con.stroke(); } } |
OnCellClicked関数は盤上をクリックしたときに呼び出されます。クリックされた部分に石を置くことができるか調べて不適切なクリックに対してメッセージを表示します。適切なクリックであれば石を配置して挟んだ石をひっくり返します。着手してから実際に石がひっくり返されるまで0.5秒の間をおいています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
function OnCellClicked(x: number, y: number) { if (!isYourTurn) { ShowString("まだあなたの番ではありません", ""); return; } if (x < 0 || x >= ColumMax || y < 0 || y >= RowMax || !GetCell(Cells, x, y).IsEmpty()) { ShowString("そこへは置けません"); return; } let revStones = GetRevarseStones(Cells, x, y, YourColor); if (revStones.length == 0) { ShowString("そこへは置けません", ""); return; } isYourTurn = false; GetCell(Cells, x, y).Color = YourColor; DrawCells(); ShowString("", ""); setTimeout(Reverse, 500, revStones); } |
GetRevarseStones関数はその位置に石を置くと相手の石を挟めるか調べるためのものです。8方向それぞれについて調べています。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function GetRevarseStones(cells: Cell[], posX: number, posY: number, color: string): Cell[] { let retPos: Cell[] = []; GetReverseUp(cells, posX, posY, color).forEach(cell => retPos.push(cell)); GetReverseDown(cells, posX, posY, color).forEach(cell => retPos.push(cell)); GetReverseLeft(cells, posX, posY, color).forEach(cell => retPos.push(cell)); GetReverseRight(cells, posX, posY, color).forEach(cell => retPos.push(cell)); GetReverseLeftUp(cells, posX, posY, color).forEach(cell => retPos.push(cell)); GetReverseLeftDown(cells, posX, posY, color).forEach(cell => retPos.push(cell)); GetReverseRightUp(cells, posX, posY, color).forEach(cell => retPos.push(cell)); GetReverseRightDown(cells, posX, posY, color).forEach(cell => retPos.push(cell)); return retPos; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
function GetReverseUp(cells: Cell[], posX: number, posY: number, color: string): Cell[] { let retPos: Cell[] = []; let enemyColor; if (color == "black") enemyColor = "white"; else enemyColor = "black"; // 盤の端なので石が存在しない if (posY - 1 < 0) return []; let cell = GetCell(cells, posX, posY - 1); // となりは盤の端ではないがそこにあるのは自分の石、または石が存在しない if (cell.Color == color || cell.IsEmpty()) return []; for (let i = 0; ; i++) { // もう片方が存在しない。実は挟めていなかったので空のリストを返す if (posY - 1 - i < 0) return []; // 連続して敵の石の場合、挟めているかもしれないのでリストに追加する let nextCell = GetCell(cells, posX, posY - 1 - i); if (nextCell.Color == enemyColor) { retPos.push(nextCell); continue; } // もう片方が自分の石であるならリストのなかにある敵の石は挟めているということなので結果を返す if (nextCell.Color == color) return retPos; // もう片方が存在しない。実は挟めていなかったので空のリストを返す if (nextCell.IsEmpty()) return []; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function GetReverseDown(cells: Cell[], posX: number, posY: number, color: string): Cell[] { let retPos: Cell[] = []; let enemyColor; if (color == "black") enemyColor = "white"; else enemyColor = "black"; if (posY + 1 >= RowMax) return []; let cell = GetCell(cells, posX, posY + 1); if (cell.Color == color || cell.IsEmpty()) return retPos; for (let i = 0; ; i++) { if (posY + 1 + i >= RowMax) return []; let nextCell = GetCell(cells, posX, posY + 1 + i); if (nextCell.Color == enemyColor) { retPos.push(nextCell); continue; } if (nextCell.Color == color) return retPos; if (nextCell.IsEmpty()) return []; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function GetReverseLeft(cells: Cell[], posX: number, posY: number, color: string): Cell[] { let retPos: Cell[] = []; let enemyColor; if (color == "black") enemyColor = "white"; else enemyColor = "black"; if (posX - 1 < 0) return []; let cell = GetCell(cells, posX - 1, posY); if (cell.Color == color || cell.IsEmpty()) return []; for (let i = 0; ; i++) { if (posX - 1 - i < 0) return []; let nextCell = GetCell(cells, posX - 1 - i, posY); if (nextCell.Color == enemyColor) { retPos.push(nextCell); continue; } if (nextCell.Color == color) return retPos; if (nextCell.IsEmpty()) return []; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function GetReverseRight(cells: Cell[], posX: number, posY: number, color: string): Cell[] { let retPos: Cell[] = []; let enemyColor; if (color == "black") enemyColor = "white"; else enemyColor = "black"; if (posX + 1 >= ColumMax) return []; let cell = GetCell(cells, posX + 1, posY); if (cell.Color == color || cell.IsEmpty()) return retPos; for (let i = 0; ; i++) { if (posX + 1 + i >= ColumMax) return []; let nextCell = GetCell(cells, posX + 1 + i, posY); if (nextCell.Color == enemyColor) { retPos.push(nextCell); continue; } if (nextCell.Color == color) return retPos; if (nextCell.IsEmpty()) return []; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function GetReverseLeftUp(cells: Cell[], posX: number, posY: number, color: string): Cell[] { let retPos: Cell[] = []; let enemyColor; if (color == "black") enemyColor = "white"; else enemyColor = "black"; if (posX - 1 < 0 || posY - 1 < 0) return []; let cell = GetCell(cells, posX - 1, posY - 1); if (cell.Color == color || cell.IsEmpty()) return []; for (let i = 0; ; i++) { if (posX - 1 - i < 0 || posY - 1 - i < 0) return []; let nextCell = GetCell(cells, posX - 1 - i, posY - 1 - i); if (nextCell.Color == enemyColor) { retPos.push(nextCell); continue; } if (nextCell.Color == color) return retPos; if (nextCell.IsEmpty()) return []; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function GetReverseRightDown(cells: Cell[], posX: number, posY: number, color: string): Cell[] { let retPos: Cell[] = []; let enemyColor; if (color == "black") enemyColor = "white"; else enemyColor = "black"; if (posX + 1 >= ColumMax || posY + 1 >= RowMax) return []; let cell = GetCell(cells, posX + 1, posY + 1); if (cell.Color == color || cell.IsEmpty()) return []; for (let i = 0; ; i++) { if (posX + 1 + i >= ColumMax || posY + 1 + i >= RowMax) return []; let nextCell = GetCell(cells, posX + 1 + i, posY + 1 + i); if (nextCell.Color == enemyColor) { retPos.push(nextCell); continue; } if (nextCell.Color == color) return retPos; if (nextCell.IsEmpty()) return []; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function GetReverseLeftDown(cells: Cell[], posX: number, posY: number, color: string): Cell[] { let retPos: Cell[] = []; let enemyColor; if (color == "black") enemyColor = "white"; else enemyColor = "black"; if (posX - 1 < 0 || posY + 1 >= RowMax) return []; let cell = GetCell(cells, posX - 1, posY + 1); if (cell.Color == color || cell.IsEmpty()) return []; for (let i = 0; ; i++) { if (posX - 1 - i < 0 || posY + 1 + i >= RowMax) return []; let nextCell = GetCell(cells, posX - 1 - i, posY + 1 + i); if (nextCell.Color == enemyColor) { retPos.push(nextCell); continue; } if (nextCell.Color == color) return retPos; if (nextCell.IsEmpty()) return []; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
function GetReverseRightUp(cells: Cell[], posX: number, posY: number, color: string): Cell[] { let retPos: Cell[] = []; let enemyColor; if (color == "black") enemyColor = "white"; else enemyColor = "black"; if (posX + 1 >= ColumMax || posY - 1 < 0) return []; let cell = GetCell(cells, posX + 1, posY - 1); if (cell.Color == color || cell.IsEmpty()) return []; for (let i = 0; ; i++) { if (posX + 1 + i >= ColumMax || posY - 1 - i < 0) return []; let nextCell = GetCell(cells, posX + 1 + i, posY - 1 - i); if (nextCell.Color == enemyColor) { retPos.push(nextCell); continue; } if (nextCell.Color == color) return retPos; if (nextCell.IsEmpty()) return []; } } |
Reverse関数は実際に相手の石をひっくり返すためのものです。1秒あいだをあけてコンピュータが着手をおこないます。
1 2 3 4 5 6 |
function Reverse(revStones:Cell[]) { revStones.forEach(x => x.Color = YourColor); DrawCells(); setTimeout(EnemyThink, 1000); } |
EnemyThink関数はコンピュータの着手に関する処理をおこなう関数です。コンピュータは角をとれるときは角をとり、そうでないときはコンピュータの着手の直後とその1ターン後にプレイヤーに角を取られない場所を探します。そのような場所が見つからないときは乱数で次の一手を決め、次の手が存在しないときはパスをします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
function EnemyThink() { let isComPassed: boolean = false; // コンピュータの次の一手 let nextEnemyHand: Cell = null; // コンピュータは角をとれるときは角をとる let nextHand = GetCanDepriveCornerMax(Cells, EnemyColor); if (nextHand != null) nextEnemyHand = nextHand; if (nextEnemyHand == null) { // 石が置かれていない場所で挟むことができる場所を探す。 let enemyHands = GetRevarsePlace(Cells, EnemyColor); // コンピュータは角を取られない場所を探す let nextCandidates = SearchEnemyHandNotDepriveCorner(enemyHands); if (nextCandidates.length > 0) { // 敵はできるだけプレイヤーの次の手が少なくなるような手を選ぶ let min = 64; nextCandidates.forEach(candidate => { if (min > candidate.HandsCount) { min = candidate.HandsCount; nextEnemyHand = candidate.Cell; } }); } else { // 候補手がない場合は、角を奪われても仕方がないので適当に選ぶしかない let count: number = enemyHands.length; if (count > 0) { let random = Math.floor(Math.random() * count); nextEnemyHand = enemyHands[random]; } else { // 次の手がまったく存在しない場合はパス isComPassed = true; } } } // コンピュータの次の手が決まったら着手する if (nextEnemyHand != null) { GetCell(Cells, nextEnemyHand.PosX, nextEnemyHand.PosY).Color = EnemyColor; DrawCells(); let reversedCells = GetRevarseStones(Cells, nextEnemyHand.PosX, nextEnemyHand.PosY, EnemyColor); // コンピュータが着手してから石をひっくり返すまでに0.5秒あける setTimeout(EnemyReverse, 500, reversedCells); } else // プレイヤーの手番になったとき、次の手は存在するのか? ChangePlayerTurn(isComPassed); } |
GetRevarsePlace関数はコンピュータが次の手を考えるときに、石が置かれていない場所でプレイヤーの石を挟むことができる場所を探すためのものです。
1 2 3 4 5 6 7 8 9 |
function GetRevarsePlace(cells: Cell[], color: string): Cell[] { let ret: Cell[] = []; cells.forEach(cell => { if (cell.IsEmpty() && GetRevarseStones(cells, cell.PosX, cell.PosY, color).length > 0) { ret.push(cell); } }); return ret; } |
PutStoneTentative関数はコンピュータが次の手を考えるときに、ためしに石を置いたときのセルの配列を取得するための関数です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function PutStoneTentative(sourceCells: Cell[], posX: number, posY: number, color: string): Cell[] { // sourceCellsのコピーをつくる let copiedCells: Cell[] = []; sourceCells.forEach(cell => { copiedCells.push(new Cell(cell.PosX, cell.PosY, cell.Color)); }); // 石を置いてみる GetCell(copiedCells, posX, posY).Color = color; // ひっくり返せる石を取得して色を変える let canReverseCells = GetRevarseStones(copiedCells, posX, posY, color); canReverseCells.forEach(cell => { cell.Color = color; }); return copiedCells; } |
CanDepriveCorner関数は自分の手番なら角を奪えるかを調べるためのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function CanDepriveCorner(cells: Cell[], color: string): boolean { let hands = GetRevarsePlace(cells, color); for (let i = 0; i < hands.length; i++) { let hand = hands[i]; if (hand.PosX == 0 && hand.PosY == 0) return true; if (hand.PosX == 0 && hand.PosY == RowMax - 1) return true; if (hand.PosX == ColumMax - 1 && hand.PosY == 0) return true; if (hand.PosX == ColumMax - 1 && hand.PosY == RowMax - 1) return true; } return false; } |
GetCanDepriveCornerMax関数は角に置いたときにひっくり返すことができる石がどれだけあるか調べるためのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
function GetCanDepriveCornerMax(cells: Cell[], color: string): Cell { let retCell = null; let max = 0; // 左上隅に石をおくと相手の石をひっくり返せるか?そのときの数は? // その他の3箇所も同様に調べる let cellLeftTop = GetCell(cells, 0, 0); if (cellLeftTop.IsEmpty()) { let reverseLen = GetRevarseStones(cells, cellLeftTop.PosX, cellLeftTop.PosY, color).length; if (reverseLen > max) { retCell = cellLeftTop; max = reverseLen; } } let cellLeftBottom = GetCell(cells, 0, RowMax - 1); if (cellLeftBottom.IsEmpty()) { let reverseLen = GetRevarseStones(cells, cellLeftBottom.PosX, cellLeftBottom.PosY, color).length; if (reverseLen > max) { retCell = cellLeftBottom; max = reverseLen; } } let cellRightBottom = GetCell(cells, ColumMax - 1, RowMax - 1); if (cellRightBottom.IsEmpty()) { let reverseLen = GetRevarseStones(cells, cellRightBottom.PosX, cellRightBottom.PosY, color).length; if (reverseLen > max) { retCell = cellRightBottom; max = reverseLen; } } let cellRightTop = GetCell(cells, ColumMax - 1, 0); if (cellRightTop.IsEmpty()) { let reverseLen = GetRevarseStones(cells, cellRightTop.PosX, cellRightTop.PosY, color).length; if (reverseLen > max) { retCell = cellRightTop; max = reverseLen; } } return retCell; } |
SearchEnemyHandNotDepriveCorner関数は、コンピュータが次の手を考えるときにプレーヤーに角を取られない場所を探すための関数です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
function SearchEnemyHandNotDepriveCorner(enemyHands: Cell[]): NextCandidate[] { let nextCandidates: NextCandidate[] = []; enemyHands.length; for (let i = 0; i < enemyHands.length; i++) { let hand = enemyHands[i]; // Cellsのコピーをつくり、そこでためしに石を置いてみる let copiedCells1 = PutStoneTentative(Cells, hand.PosX, hand.PosY, EnemyColor); // このときプレイヤーにはどのような応手があるか? let yourHands: Cell[] = GetRevarsePlace(copiedCells1, YourColor); // もしプレイヤーに角を奪われてしまうのであればその手は候補からいったん外す if (CanDepriveCorner(copiedCells1, YourColor)) continue; // 最終的にコンピュータが選択する手のひとつ let retEnemyHand: Cell = null; //プレイヤーの応手に角を奪う手がない場合、それぞれどのような進行が想定されるか? for (let j = 0; j < yourHands.length; j++) { let yourNextHand: Cell = yourHands[j]; let copiedCells2: Cell[] = PutStoneTentative(copiedCells1, yourNextHand.PosX, yourNextHand.PosY, YourColor); // その後、コンピュータの着手を考える // これで敵味方1手ずつ打ち合った盤面(copiedCells2)と // 敵の次の次の手リスト(enemyHand2s)が取得できたことになる let enemyHand2s: Cell[] = GetRevarsePlace(copiedCells2, EnemyColor); for (let k = 0; k < enemyHand2s.length; k++) { let enemyHand2 = enemyHand2s[k]; let copiedCells3 = PutStoneTentative(copiedCells2, enemyHand2.PosX, enemyHand2.PosY, EnemyColor); // このときプレイヤーは角を奪えるか? if (CanDepriveCorner(copiedCells3, YourColor)) { // 奪えるならenemyHand2は適切な手ではない continue; } else { // プレイヤーが奪えないのであればenemyHand2は不適切な候補ではないといえる // ループを抜け、handを候補手に追加する retEnemyHand = enemyHand2; break; } } } if (retEnemyHand != null) nextCandidates.push(new NextCandidate(hand, yourHands.length)); } return nextCandidates; } |
EnemyReverse関数は、実際にコンピュータが着手したときに石をひっくり返す処理をする関数です。
1 2 3 4 5 6 |
function EnemyReverse(revStones: Cell[]) { revStones.forEach(x => x.Color = EnemyColor); DrawCells(); ChangePlayerTurn(false); } |
コンピュータが着手したらプレイヤーの手番になるが、そのときにプレイヤーの次の手は存在するのかを調べて、存在しない場合はパスの処理をおこなわなければなりません。ChangePlayerTurn関数はそのための関数です。コンピュータもパスしたらゲーム終了です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
function ChangePlayerTurn(isComPassed: boolean) { let yourNextHands: Cell[] = GetRevarsePlace(Cells, YourColor); if (yourNextHands.length > 0) { isYourTurn = true; if (!isComPassed) ShowString("あなたの手番です。", ""); else ShowString("コンピュータはパスしました。", "あなたの手番です。"); } else { if (!isComPassed) { ShowString("あなたの手番ですが", "パスするしかありません。"); EnemyThink(); } else End(); } } |
ゲームが終了したら双方の石を数えて結果を表示することになりますが、そのためにEnd関数が呼び出されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
function End(): void { let black: number = 0; let white: number = 0; Cells.forEach(cell => { if (cell.Color == "black") black++; if (cell.Color == "white") white++; }); let winner: string = ""; if (black > white) winner = "黒の勝ちです。"; else if (black < white) winner = "白の勝ちです。"; else winner = "引き分けです。"; let mes: string = black + " 対 " + white + " で" + winner; ShowString("終了しました。", mes); } |
オセロ久々すぎて途中負けるんじゃないかと思いましたw
https://i.imgur.com/vV4Vx5D.png