ChatGPT によるリバーシの作成

ChatGPT によるリバーシゲームを作成してみます。

# 概要

ふとChatGPT でリバーシを作ったらどうなるか試したくなりました。
- ChatGPT 4o
- JavaScript

![2024-09-06_15h24_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/6c109a74-e19d-7ef8-e32a-31e5163cd51f.png)

続き
- [ChatGPT によるリバーシの作成その２](https://qiita.com/rex0220/items/f182278937ef13e93cbc)
- [ChatGPT によるリバーシの作成その３](https://qiita.com/rex0220/items/69ea415d6d3c08717866)

# チャットの内容

簡単なやり取りで、ゲーム作成・改良が出来ました。
チャットの内容を公開リンクにしてみました。

https://chatgpt.com/share/d1bfccd4-8df2-47e1-8247-3da6004f7ac3

- JavaScript で、リバーシゲームを作成してください
    - コードを応答
    - 初期画面不正
- 初期画面に4つの駒が配置されていません
    - コードを応答
    - ゲーム画面表示・動作
- ゲーム中も白と黒の個数を表示して
    - コードを応答
    - 初期画面不正
- 初期画面に4つの駒が配置されていません
    - コードを応答
    - ゲーム画面表示・動作
- リセットボタンを追加して
    - コードを応答
    - ゲーム画面表示・動作


# 作成されたコード

```index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>リバーシゲーム</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
        }
        table {
            margin: 20px auto;
            border-collapse: collapse;
        }
        td {
            width: 50px;
            height: 50px;
            border: 1px solid black;
            text-align: center;
            vertical-align: middle;
            font-size: 24px;
            cursor: pointer;
        }
        .black {
            background-color: black;
        }
        .white {
            background-color: white;
        }
        .empty {
            background-color: green;
        }
    </style>
</head>
<body>
    <h1>リバーシゲーム</h1>
    <table id="board"></table>
    <p id="status"></p>
    <p>黒: <span id="black-count">2</span> 白: <span id="white-count">2</span></p>
    <button id="reset-btn">リセット</button> <!-- リセットボタンを追加 -->

    <script src="reversi.js"></script>
</body>
</html>
```

```.riversi.js
const SIZE = 8;
const EMPTY = 0;
const BLACK = 1;
const WHITE = 2;

let board = [];
let currentPlayer = BLACK;

// 初期化
function initBoard() {
    board = Array(SIZE).fill(null).map(() => Array(SIZE).fill(EMPTY));

    // 初期配置
    board[3][3] = WHITE;
    board[3][4] = BLACK;
    board[4][3] = BLACK;
    board[4][4] = WHITE;

    renderBoard();
    updateStatus();
}

// ボードを描画
function renderBoard() {
    const table = document.getElementById('board');
    table.innerHTML = '';

    let blackCount = 0;
    let whiteCount = 0;

    for (let y = 0; y < SIZE; y++) {
        const row = document.createElement('tr');
        for (let x = 0; x < SIZE; x++) {
            const cell = document.createElement('td');
            
            // 駒の状態に応じてクラスを追加
            if (board[y][x] === BLACK) {
                cell.classList.add('black');
                blackCount++; // 黒の数をカウント
            } else if (board[y][x] === WHITE) {
                cell.classList.add('white');
                whiteCount++; // 白の数をカウント
            } else {
                cell.classList.add('empty'); // 空のマスのみ空にする
            }

            cell.addEventListener('click', () => handleClick(x, y));
            row.appendChild(cell);
        }
        table.appendChild(row);
    }

    // 駒の数を表示
    document.getElementById('black-count').textContent = blackCount;
    document.getElementById('white-count').textContent = whiteCount;
}

// 駒を置く処理
function handleClick(x, y) {
    if (board[y][x] !== EMPTY || !isValidMove(x, y, currentPlayer)) {
        return;
    }

    placeDisk(x, y, currentPlayer);
    flipDisks(x, y, currentPlayer);

    currentPlayer = currentPlayer === BLACK ? WHITE : BLACK;
    renderBoard();
    updateStatus();

    if (!canMove(currentPlayer)) {
        currentPlayer = currentPlayer === BLACK ? WHITE : BLACK;
        if (!canMove(currentPlayer)) {
            endGame();
        }
    }
}

// 駒を置く
function placeDisk(x, y, player) {
    board[y][x] = player;
}

// 駒をひっくり返す
function flipDisks(x, y, player) {
    const directions = [
        [0, 1], [1, 0], [0, -1], [-1, 0],
        [1, 1], [1, -1], [-1, 1], [-1, -1]
    ];

    for (let [dx, dy] of directions) {
        if (canFlip(x, y, dx, dy, player)) {
            let nx = x + dx;
            let ny = y + dy;
            while (board[ny][nx] !== player) {
                board[ny][nx] = player;
                nx += dx;
                ny += dy;
            }
        }
    }
}

// 駒をひっくり返せるか確認
function canFlip(x, y, dx, dy, player) {
    let nx = x + dx;
    let ny = y + dy;
    let foundOpponent = false;

    while (nx >= 0 && nx < SIZE && ny >= 0 && ny < SIZE) {
        if (board[ny][nx] === EMPTY) return false;
        if (board[ny][nx] === player) return foundOpponent;
        foundOpponent = true;
        nx += dx;
        ny += dy;
    }

    return false;
}

// 有効な手か確認
function isValidMove(x, y, player) {
    const directions = [
        [0, 1], [1, 0], [0, -1], [-1, 0],
        [1, 1], [1, -1], [-1, 1], [-1, -1]
    ];

    return directions.some(([dx, dy]) => canFlip(x, y, dx, dy, player));
}

// 現在のプレイヤーが手を打てるか確認
function canMove(player) {
    for (let y = 0; y < SIZE; y++) {
        for (let x = 0; x < SIZE; x++) {
            if (board[y][x] === EMPTY && isValidMove(x, y, player)) {
                return true;
            }
        }
    }
    return false;
}

// ゲーム終了
function endGame() {
    const blackCount = board.flat().filter(disk => disk === BLACK).length;
    const whiteCount = board.flat().filter(disk => disk === WHITE).length;
    const status = document.getElementById('status');
    if (blackCount > whiteCount) {
        status.textContent = `黒の勝ち！ 黒: ${blackCount}, 白: ${whiteCount}`;
    } else if (whiteCount > blackCount) {
        status.textContent = `白の勝ち！ 黒: ${blackCount}, 白: ${whiteCount}`;
    } else {
        status.textContent = `引き分け！ 黒: ${blackCount}, 白: ${whiteCount}`;
    }
}

// 状態を更新
function updateStatus() {
    const status = document.getElementById('status');
    status.textContent = currentPlayer === BLACK ? '黒のターン' : '白のターン';
}

// リセットボタンの処理
document.getElementById('reset-btn').addEventListener('click', function() {
    currentPlayer = BLACK; // 黒を先攻にリセット
    initBoard(); // ボードを初期化
});

// 初期化
initBoard();
```