ChatGPTにリバーシゲームのwebアプリを作らせてみた！！！

### なぜやろうとしたのか？
人間がプログラムをせずにどこまでのものが作れるか試したかったから。

### 使用した環境
生成AI : GPT-3.5
エディタ : Visual Studio Code
言語 : HTML, CSS, JavaScript
拡張機能 : Live Server, HTML CSS Support

### 参考にしたもの
本記事を作成するにあたり、以下の動画を参考にさせていただきました。
https://www.youtube.com/watch?v=tMrlVqo6hFQ&list=PL0WiaI7Si8LkHRJtfbi38nFEOUc-HMQE7&index=2


### 制作の流れ
1. リバーシゲームに必要な機能をまとめる。
2. サンプルコードを書かせる。
3. コードを確認して足りない機能を確認する。
4. 足りない箇所を追加する。
5. バグがないか確認し、あったら修正。
6. 納得いくまで4と5を繰り返す。  


### プロンプトと回答
(一部改変しています)
#### まずサンプルコードを書かせてみる
プロンプト : JavaScriptでリバーシゲームを作りたいです。サンプルコードをお願いします。

返答 : もちろんです！以下は、JavaScriptを使用して基本的なリバーシゲームを作成するサンプルコードです。このコードは、HTML、CSS、およびJavaScriptで構成されています。

HTML
```HTML
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reversi Game</title>
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <div id="board" class="board"></div>
    <script src="script.js"></script>
</body>

</html>
```

CSS
```css
.board {
    display: grid;
    grid-template-columns: repeat(8, 50px);
    grid-template-rows: repeat(8, 50px);
}

.cell {
    width: 50px;
    height: 50px;
    border: 1px solid #000;
}

.white {
    background-color: #fff;
    border-radius: 50%;
}

.black {
    background-color: #000;
    border-radius: 50%;
}

```

JavaScript
```javascript
const board = document.getElementById('board');
const cells = [];

const player = {
    WHITE: 'white',
    BLACK: 'black'
};

let currentPlayer = player.BLACK;

function createBoard() {
    for (let i = 0; i < 8; i++) {
        cells[i] = [];
        for (let j = 0; j < 8; j++) {
            const cell = document.createElement('div');
            cell.className = 'cell';
            cell.dataset.row = i;
            cell.dataset.col = j;
            cell.addEventListener('click', handleCellClick);
            board.appendChild(cell);
            cells[i][j] = cell;
        }
    }

    cells[3][3].classList.add(player.WHITE);
    cells[3][4].classList.add(player.BLACK);
    cells[4][3].classList.add(player.BLACK);
    cells[4][4].classList.add(player.WHITE);
}

function handleCellClick(event) {
    const row = parseInt(event.target.dataset.row);
    const col = parseInt(event.target.dataset.col);

    if (isValidMove(row, col)) {
        cells[row][col].classList.add(currentPlayer);
        flipOpponentPieces(row, col);
        currentPlayer = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;
    }
}

function isValidMove(row, col) {
    if (cells[row][col].classList.contains(player.WHITE) || cells[row][col].classList.contains(player.BLACK)) {
        return false;
    }

    // Logic for validating the move, not implemented in this sample code.
    // You need to implement the logic for checking valid moves and flipping opponent pieces.
    // This can be a complex algorithm based on Othello game rules.
    // For simplicity, it's not implemented here.
    return true;
}

function flipOpponentPieces(row, col) {
    // Logic for flipping opponent pieces after a move, not implemented in this sample code.
    // You need to implement the logic for flipping opponent pieces based on Othello game rules.
    // For simplicity, it's not implemented here.
}

createBoard();

```
出力されたコードには、基本的なリバーシのゲームボードを作成し、プレイヤーが交互にマスをクリックすることで白と黒の石を配置できるようになっています。ただし、有効な移動と相手の駒をひっくり返すロジックはこのサンプルコードには含まれていません。

#### 石をひっくり返すロジックの追加
プロンプト：先ほどのコードに相手の駒をひっくり返すロジックを実装してください。

返答 : 相手の駒をひっくり返すロジックを実装するためには、指定された位置に駒を置いた後、8方向（縦、横、斜め）に対して相手の駒をひっくり返す必要があります。以下がその実装です。

まず、isValidMove(row, col)関数を以下のように修正します。
JavaScript
```javascript
function isValidMove(row, col) {
    if (cells[row][col].classList.contains(player.WHITE) || cells[row][col].classList.contains(player.BLACK)) {
        return false;
    }

    let isValid = false;
    for (let i = -1; i <= 1; i++) {
        for (let j = -1; j <= 1; j++) {
            if (i === 0 && j === 0) continue;
            if (checkDirection(row, col, i, j)) {
                isValid = true;
            }
        }
    }

    return isValid;
}

function checkDirection(row, col, rowDir, colDir) {
    let r = row + rowDir;
    let c = col + colDir;
    let flipped = false;

    while (r >= 0 && r < 8 && c >= 0 && c < 8) {
        if (!cells[r][c].classList.contains(player.WHITE) && !cells[r][c].classList.contains(player.BLACK)) {
            break;
        }
        if (cells[r][c].classList.contains(currentPlayer)) {
            flipped = true;
            break;
        }
        r += rowDir;
        c += colDir;
    }

    if (flipped) {
        r = row + rowDir;
        c = col + colDir;
        while (r >= 0 && r < 8 && c >= 0 && c < 8) {
            if (cells[r][c].classList.contains(currentPlayer)) {
                break;
            }
            cells[r][c].classList.toggle(player.BLACK);
            cells[r][c].classList.toggle(player.WHITE);
            r += rowDir;
            c += colDir;
        }
        return true;
    }
    return false;
}

```
このコードでは、isValidMove()関数が指定された位置に駒を置けるかどうかを判断します。checkDirection()関数は指定された方向（縦、横、斜め）に対して相手の駒をひっくり返すロジックを実装しています。

この実装により、プレイヤーが駒を置いた後、相手の駒を適切にひっくり返すことができます。注意点として、このコードは非常に基本的なものであり、高度な戦略や特殊なケースには対応していません。必要に応じてルールやロジックをさらに拡張していくことができます。

#### 指定されたマス目に石を置けるかを検証する機能を追加
プロンプト : 指定されたマス目に駒を置けるかどうかを検証するためのロジックを実装してください。

返答 : 指定されたマス目に駒を置けるかどうかを検証するためのロジックを実装するには、指定されたマス目周辺の8方向（縦、横、斜め）に対して相手の駒をひっくり返せるかどうかをチェックする必要があります。以下がそのロジックの実装です。

まず、isValidMove(row, col)関数を以下のように修正します

JavaScript
```javascript
function isValidMove(row, col) {
    if (cells[row][col].classList.contains(player.WHITE) || cells[row][col].classList.contains(player.BLACK)) {
        return false;
    }

    let isValid = false;
    for (let i = -1; i <= 1; i++) {
        for (let j = -1; j <= 1; j++) {
            if (i === 0 && j === 0) continue;
            if (checkDirection(row, col, i, j)) {
                isValid = true;
            }
        }
    }

    return isValid;
}

function checkDirection(row, col, rowDir, colDir) {
    let r = row + rowDir;
    let c = col + colDir;
    let flipped = false;

    // 隣のマスが相手の駒でない場合は無効な方向
    if (!isInBoard(r, c) || !cells[r][c].classList.contains(getOpponentPlayer())) {
        return false;
    }

    while (isInBoard(r, c)) {
        if (!cells[r][c].classList.contains(player.WHITE) && !cells[r][c].classList.contains(player.BLACK)) {
            return false;
        }
        if (cells[r][c].classList.contains(currentPlayer)) {
            flipped = true;
            break;
        }
        r += rowDir;
        c += colDir;
    }

    return flipped;
}

function isInBoard(row, col) {
    return row >= 0 && row < 8 && col >= 0 && col < 8;
}

function getOpponentPlayer() {
    return currentPlayer === player.BLACK ? player.WHITE : player.BLACK;
}

```
##### 出力されたコードを実行してみると、先手は石をひっくり返すことができましたが後手だとできませんでした。これから改良していきます。

#### 出力されたコードが正しく動作しないため改良する
プロンプト : このプログラムを実行すると、先手の黒の場合は石をひっくり返す機能がありますが白の場合はそれがありません。改良したコードをお願いします。

返答 : 申し訳ありません、前回のコードには問題があり、修正が必要でした。以下が修正済みのコードです。主な変更点は、checkDirection()関数とflipOpponentPieces()関数です。特に、石をひっくり返すロジックに誤りがあったので、それを修正しました。

JavaScript
```javascript
const board = document.getElementById('board');
const cells = [];
const player = {
    WHITE: 'white',
    BLACK: 'black'
};
let currentPlayer = player.BLACK;

function createBoard() {
    for (let i = 0; i < 8; i++) {
        cells[i] = [];
        for (let j = 0; j < 8; j++) {
            const cell = document.createElement('div');
            cell.className = 'cell';
            cell.dataset.row = i;
            cell.dataset.col = j;
            cell.addEventListener('click', handleCellClick);
            board.appendChild(cell);
            cells[i][j] = cell;
        }
    }

    cells[3][3].classList.add(player.WHITE);
    cells[3][4].classList.add(player.BLACK);
    cells[4][3].classList.add(player.BLACK);
    cells[4][4].classList.add(player.WHITE);
}

function handleCellClick(event) {
    const row = parseInt(event.target.dataset.row);
    const col = parseInt(event.target.dataset.col);

    if (isValidMove(row, col)) {
        cells[row][col].classList.add(currentPlayer);
        flipOpponentPieces(row, col);
        currentPlayer = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;
    }
}

function isValidMove(row, col) {
    if (cells[row][col].classList.contains(player.WHITE) || cells[row][col].classList.contains(player.BLACK)) {
        return false;
    }

    let isValid = false;
    for (let i = -1; i <= 1; i++) {
        for (let j = -1; j <= 1; j++) {
            if (i === 0 && j === 0) continue;
            if (checkDirection(row, col, i, j)) {
                isValid = true;
            }
        }
    }

    return isValid;
}

function checkDirection(row, col, rowDir, colDir) {
    const opponent = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;
    let r = row + rowDir;
    let c = col + colDir;

    if (!isInBoard(r, c) || !cells[r][c].classList.contains(opponent)) {
        return false;
    }

    while (isInBoard(r, c)) {
        if (!cells[r][c].classList.contains(player.WHITE) && !cells[r][c].classList.contains(player.BLACK)) {
            return false;
        }
        if (cells[r][c].classList.contains(currentPlayer)) {
            return true;
        }
        r += rowDir;
        c += colDir;
    }

    return false;
}

function flipOpponentPieces(row, col) {
    const opponent = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;

    for (let i = -1; i <= 1; i++) {
        for (let j = -1; j <= 1; j++) {
            if (i === 0 && j === 0) continue;
            if (checkDirection(row, col, i, j)) {
                let r = row + i;
                let c = col + j;
                while (cells[r][c].classList.contains(opponent)) {
                    cells[r][c].classList.toggle(currentPlayer);
                    r += i;
                    c += j;
                }
            }
        }
    }
}

function isInBoard(row, col) {
    return row >= 0 && row < 8 && col >= 0 && col < 8;
}

createBoard();

```
##### しかし直らなかったため、質問を変えてみる。

プロンプト : このプログラムを実行したところ、先手の場合はひっくり返すことができましたが、後手の時には正しく動作しませんでした。考えられる原因を教えてください。

返答 : 指摘いただいている問題は、後手（白）のプレイヤーが正しく駒をひっくり返せていないという点です。これは、駒をひっくり返す際にひっくり返す対象の判定が適切でないことが原因です。具体的には、対象となる駒の判定が正しく行われていない可能性があります。

修正するためには、checkDirection()関数とflipOpponentPieces()関数において、相手の駒を判定するロジックを見直す必要があります。以下に、修正したコードを示します：

JavaScript
```javascript
function checkDirection(row, col, rowDir, colDir) {
    const opponent = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;
    let r = row + rowDir;
    let c = col + colDir;

    if (!isInBoard(r, c) || !cells[r][c].classList.contains(opponent)) {
        return false;
    }

    while (isInBoard(r, c)) {
        if (!cells[r][c].classList.contains(player.WHITE) && !cells[r][c].classList.contains(player.BLACK)) {
            return false; // 駒がない空白のマスがある場合は無効な方向
        }
        if (cells[r][c].classList.contains(currentPlayer)) {
            return true; // 自分の駒が見つかったらこの方向は有効
        }
        r += rowDir;
        c += colDir;
    }

    return false; // 相手の駒が挟まれていない場合は無効な方向
}

function flipOpponentPieces(row, col) {
    const opponent = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;

    for (let i = -1; i <= 1; i++) {
        for (let j = -1; j <= 1; j++) {
            if (i === 0 && j === 0) continue;
            if (checkDirection(row, col, i, j)) {
                let r = row + i;
                let c = col + j;
                while (cells[r][c].classList.contains(opponent)) {
                    cells[r][c].classList.toggle(player.BLACK);
                    cells[r][c].classList.toggle(player.WHITE);
                    r += i;
                    c += j;
                }
            }
        }
    }
}

```
修正点：

flipOpponentPieces()関数内で、対象の駒をtoggle()メソッドを使用してひっくり返す際、player.BLACKとplayer.WHITEのクラスを正しく切り替えるように修正しました。
checkDirection()関数では、自分の駒が見つかったらその方向は有効と判断し、対象の駒をひっくり返す際に正しく動作するようになりました。
これによって、先手（黒）と後手（白）のプレイヤーが正しく駒をひっくり返せるようになるはずです。修正後のコードを実行してみてください。

##### 正しく動いたため、次のステップに進む。

#### ゲームの終了判定と勝者の判定を追加

プロンプト : 先ほどのコードにゲームの終了判定と勝者の判定を追加したいです。コードをお願い致します。

返答 : もちろんです。以下に、ゲームの終了判定と勝者の判定を追加したリバーシゲームのコードを示します。

JavaScript
```javascript
const board = document.getElementById('board');
const cells = [];
const player = {
    WHITE: 'white',
    BLACK: 'black'
};
let currentPlayer = player.BLACK;

function createBoard() {
    for (let i = 0; i < 8; i++) {
        cells[i] = [];
        for (let j = 0; j < 8; j++) {
            const cell = document.createElement('div');
            cell.className = 'cell';
            cell.dataset.row = i;
            cell.dataset.col = j;
            cell.addEventListener('click', handleCellClick);
            board.appendChild(cell);
            cells[i][j] = cell;
        }
    }

    cells[3][3].classList.add(player.WHITE);
    cells[3][4].classList.add(player.BLACK);
    cells[4][3].classList.add(player.BLACK);
    cells[4][4].classList.add(player.WHITE);
}

function handleCellClick(event) {
    const row = parseInt(event.target.dataset.row);
    const col = parseInt(event.target.dataset.col);

    if (isValidMove(row, col)) {
        cells[row][col].classList.add(currentPlayer);
        flipOpponentPieces(row, col);
        currentPlayer = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;

        if (!hasValidMoves()) {
            currentPlayer = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;

            if (!hasValidMoves()) {
                declareWinner();
            }
        }
    }
}

function isValidMove(row, col) {
    // ...（先ほどのコードと同じ）
}

function checkDirection(row, col, rowDir, colDir) {
    // ...（先ほどのコードと同じ）
}

function flipOpponentPieces(row, col) {
    // ...（先ほどのコードと同じ）
}

function isInBoard(row, col) {
    // ...（先ほどのコードと同じ）
}

function hasValidMoves() {
    for (let i = 0; i < 8; i++) {
        for (let j = 0; j < 8; j++) {
            if (!cells[i][j].classList.contains(player.WHITE) && !cells[i][j].classList.contains(player.BLACK) && isValidMove(i, j)) {
                return true;
            }
        }
    }
    return false;
}

function declareWinner() {
    let blackCount = 0;
    let whiteCount = 0;
    for (let i = 0; i < 8; i++) {
        for (let j = 0; j < 8; j++) {
            if (cells[i][j].classList.contains(player.BLACK)) {
                blackCount++;
            } else if (cells[i][j].classList.contains(player.WHITE)) {
                whiteCount++;
            }
        }
    }

    if (blackCount > whiteCount) {
        alert('黒の勝ちです！');
    } else if (blackCount < whiteCount) {
        alert('白の勝ちです！');
    } else {
        alert('引き分けです！');
    }
}

createBoard();
```
#### リセットボタンを追加
プロンプト : 先ほどのコードにリセットボタンを追加したいです。コードをお願い致します。

返答 : リセットボタンを追加するために、以下のようなコードを追加できます。このコードは、HTMLの <div> 要素に "reset" というIDを追加して、それに対応するJavaScriptの処理を追加したものです。

まず、HTMLの <div> 要素にリセットボタンを追加します：

HTML
```html
<div id="reset" class="button">リセット</div>
```
そして、JavaScriptのコードを以下のように追加します：

JavaScript
```javascript
const resetButton = document.getElementById('reset');
resetButton.addEventListener('click', resetGame);

function resetGame() {
    // ボードをリセット（クラスを削除）
    for (let i = 0; i < 8; i++) {
        for (let j = 0; j < 8; j++) {
            cells[i][j].classList.remove(player.WHITE, player.BLACK);
        }
    }

    // 最初の配置を再設定
    cells[3][3].classList.add(player.WHITE);
    cells[3][4].classList.add(player.BLACK);
    cells[4][3].classList.add(player.BLACK);
    cells[4][4].classList.add(player.WHITE);

    // ゲームのプレイヤーを初期化
    currentPlayer = player.BLACK;
}
```
#### GUIの工夫

プロンプト :  ゲームボードの背景色やセルの色、ボーダーのスタイルを変更したいです。cssのコードをお願い致します。

返答 : リバーシゲームのゲームボードの背景色、セルの色、ボーダーのスタイルを変更するためのCSSのコードを以下に示します。これらのスタイリングは、ゲームボードを視覚的に魅力的にするのに役立ちます。

CSS
```css
#board {
    display: grid;
    grid-template-columns: repeat(8, 50px);
    grid-template-rows: repeat(8, 50px);
    gap: 2px; /* セル間の隙間 */
    background-color: #2ecc71; /* ゲームボードの背景色 */
    border: 5px solid #27ae60; /* ボーダーのスタイル */
}

.cell {
    width: 50px;
    height: 50px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 24px;
    background-color: #3498db; /* セルの背景色 */
    border: 2px solid #2980b9; /* セルのボーダーのスタイル */
}

.cell.black {
    background-color: #34495e; /* 黒の駒のセルの背景色 */
    color: white; /* 黒の駒の文字色 */
}

.cell.white {
    background-color: #ecf0f1; /* 白の駒のセルの背景色 */
}

```

##### 色に違和感があるので変えてもらう

プロンプト : ゲームボードの背景色やセルの色、ボーダーのスタイルを変更したいです。cssのコードをお願い致します。

返答 : ゲームボードの背景色やセルの色、ボーダーのスタイルを変更するためのCSSのコードは以下の通りです。これらのスタイルを適用することで、リバーシゲームの外観をカスタマイズできます。

CSS
```css
#board {
    display: grid;
    grid-template-columns: repeat(8, 50px);
    grid-template-rows: repeat(8, 50px);
    gap: 2px; /* セル間の隙間 */
    background-color: #2ecc71; /* ゲームボードの背景色 */
    border: 5px solid #27ae60; /* ボーダーのスタイル */
}

.cell {
    width: 50px;
    height: 50px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 24px;
    background-color: #3498db; /* セルの背景色 */
    border: 2px solid #2980b9; /* セルのボーダーのスタイル */
}

.cell.black {
    background-color: #34495e; /* 黒の駒のセルの背景色 */
    color: white; /* 黒の駒の文字色 */
}

.cell.white {
    background-color: #ecf0f1; /* 白の駒のセルの背景色 */
}

```
### 完成したコード

HTML
```html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Reversi Game</title>
    <link rel="stylesheet" href="styles.css">
</head>

<body>
    <div id="board" class="board"></div>
    <div id="reset" class="button">リセット</div>
    <script src="script.js"></script>
</body>

</html>

```
CSS
```css
#board {
    display: grid;
    grid-template-columns: repeat(8, 50px);
    grid-template-rows: repeat(8, 50px);
    gap: 2px; /* セル間の隙間 */
    background-color: #aed6f1; /* ゲームボードの背景色 */
    border: 5px solid #2980b9; /* ボーダーのスタイル */
}

.cell {
    width: 50px;
    height: 50px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 24px;
    background-color: #2ecc71; /* セルの背景色（緑） */
    border: 2px solid #27ae60; /* セルのボーダーのスタイル */
}

.cell.black {
    background-color: #474747; /* 黒の駒のセルの背景色（黒に近いグレー） */
    color: white; /* 黒の駒の文字色 */
}

.cell.white {
    background-color: #f2f2f2; /* 白の駒のセルの背景色（白に近いグレー） */
}

```
JavaScript
```javascript
const board = document.getElementById('board');
const cells = [];
const player = {
    WHITE: 'white',
    BLACK: 'black'
};
let currentPlayer = player.BLACK;

const resetButton = document.getElementById('reset');
resetButton.addEventListener('click', resetGame);

function resetGame() {
    // ボードをリセット（クラスを削除）
    for (let i = 0; i < 8; i++) {
        for (let j = 0; j < 8; j++) {
            cells[i][j].classList.remove(player.WHITE, player.BLACK);
        }
    }

    // 最初の配置を再設定
    cells[3][3].classList.add(player.WHITE);
    cells[3][4].classList.add(player.BLACK);
    cells[4][3].classList.add(player.BLACK);
    cells[4][4].classList.add(player.WHITE);

    // ゲームのプレイヤーを初期化
    currentPlayer = player.BLACK;
}


function createBoard() {
    for (let i = 0; i < 8; i++) {
        cells[i] = [];
        for (let j = 0; j < 8; j++) {
            const cell = document.createElement('div');
            cell.className = 'cell';
            cell.dataset.row = i;
            cell.dataset.col = j;
            cell.addEventListener('click', handleCellClick);
            board.appendChild(cell);
            cells[i][j] = cell;
        }
    }

    cells[3][3].classList.add(player.WHITE);
    cells[3][4].classList.add(player.BLACK);
    cells[4][3].classList.add(player.BLACK);
    cells[4][4].classList.add(player.WHITE);
}

function handleCellClick(event) {
    const row = parseInt(event.target.dataset.row);
    const col = parseInt(event.target.dataset.col);

    if (isValidMove(row, col)) {
        cells[row][col].classList.add(currentPlayer);
        flipOpponentPieces(row, col);
        currentPlayer = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;

        if (!hasValidMoves()) {
            currentPlayer = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;

            if (!hasValidMoves()) {
                declareWinner();
            }
        }
    }
}

function isValidMove(row, col) {
    if (cells[row][col].classList.contains(player.WHITE) || cells[row][col].classList.contains(player.BLACK)) {
        return false;
    }

    let isValid = false;
    for (let i = -1; i <= 1; i++) {
        for (let j = -1; j <= 1; j++) {
            if (i === 0 && j === 0) continue;
            if (checkDirection(row, col, i, j)) {
                isValid = true;
            }
        }
    }

    return isValid;
}

function checkDirection(row, col, rowDir, colDir) {
    const opponent = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;
    let r = row + rowDir;
    let c = col + colDir;

    if (!isInBoard(r, c) || !cells[r][c].classList.contains(opponent)) {
        return false;
    }

    while (isInBoard(r, c)) {
        if (!cells[r][c].classList.contains(player.WHITE) && !cells[r][c].classList.contains(player.BLACK)) {
            return false; // 駒がない空白のマスがある場合は無効な方向
        }
        if (cells[r][c].classList.contains(currentPlayer)) {
            return true; // 自分の駒が見つかったらこの方向は有効
        }
        r += rowDir;
        c += colDir;
    }

    return false; // 相手の駒が挟まれていない場合は無効な方向
}

function flipOpponentPieces(row, col) {
    const opponent = currentPlayer === player.BLACK ? player.WHITE : player.BLACK;

    for (let i = -1; i <= 1; i++) {
        for (let j = -1; j <= 1; j++) {
            if (i === 0 && j === 0) continue;
            if (checkDirection(row, col, i, j)) {
                let r = row + i;
                let c = col + j;
                while (cells[r][c].classList.contains(opponent)) {
                    cells[r][c].classList.toggle(player.BLACK);
                    cells[r][c].classList.toggle(player.WHITE);
                    r += i;
                    c += j;
                }
            }
        }
    }
}


function isInBoard(row, col) {
    return row >= 0 && row < 8 && col >= 0 && col < 8;
}

function hasValidMoves() {
    for (let i = 0; i < 8; i++) {
        for (let j = 0; j < 8; j++) {
            if (!cells[i][j].classList.contains(player.WHITE) && !cells[i][j].classList.contains(player.BLACK) && isValidMove(i, j)) {
                return true;
            }
        }
    }
    return false;
}

function declareWinner() {
    let blackCount = 0;
    let whiteCount = 0;
    for (let i = 0; i < 8; i++) {
        for (let j = 0; j < 8; j++) {
            if (cells[i][j].classList.contains(player.BLACK)) {
                blackCount++;
            } else if (cells[i][j].classList.contains(player.WHITE)) {
                whiteCount++;
            }
        }
    }

    if (blackCount > whiteCount) {
        alert('黒の勝ちです！');
    } else if (blackCount < whiteCount) {
        alert('白の勝ちです！');
    } else {
        alert('引き分けです！');
    }
}

createBoard();
```


### まとめ
このような流れでリバーシゲーム制作を行いました。
問題を解決させるための指示を考えるのに苦労しましたがとてもいい経験になりました。


### 今後...
現在、2人用のゲームになっているため、このプログラムに対戦相手となるAIを導入してみたり、webサイトとしての完成度を上げるためにUIを工夫したいです。