0
0

ChatGPT によるリバーシの作成その3

Last updated at Posted at 2024-09-13

ChatGPT によるリバーシゲームの作成手順を変えてみます。

概要

前回(ChatGPT によるリバーシの作成その2)のように、チャットを続けて機能を追加すると、エラー・修正の繰り返しになったので、最初にある程度仕様を与えて開発してみます。

  • 仕様に応じてコードが作成されるが、仕様追加・変更すると以前のコードと継続性が無くなり、新たな問題が発生する
  • ある程度仕様を修正した後は、チャットで機能追加・変更を行う
  • 盤面の位置ズレについては、表示された盤面の画像を読み込ませても、うまく修正されない
    ※実際の画面をChatGPTに読み込ませても位置ズレは認識できないようです
  • 位置を調整した CSS を直接指定すると、CSS に余計な変更を加えて結局ズレる
    保守する場合は、ある程度固まったコードをベースにして、コードの一部を ChatGPT に出してもらうくらいしかなさそう

2024-09-13_10h48_18.png

現状の問題点

  • パス機能が未実装
  • 「もどす」後に、別の手順を行うとNG

盤面の変更過程

いろいろ位置が変わったり、盤面の位置ズレ、画面サイズの変更によるズレなど試行錯誤の結果です。
結局、ChatGPT がコードが書けてもその結果のズレをフィードバックするのが困難で、細かな調整は手作業でやるしかなさそう。

2024-09-13_09h27_02.png

チャットの内容

下記仕様で、リバーシゲームを作成してください
・盤面は、div と css で正方形にする
・盤面の上部に a-h の位置を枠の中央に表示
・盤面の左に 1-8 の位置を枠の中央に表示
・盤面と位置表示がずれないように
・白と黒の駒は、丸くわかりやすいように表示
・初期画面で、白2個と黒2個の駒を中央に表示
・黒先行で、どちらの順番かわかるように表示
・盤面をクリックすると、駒がおけるか判定し、おける場合は、駒を配置し、はさんだ駒を反転表示
・ゲームの履歴を右側に固定長で表示
・リセット機能を実装
・もどす・すすむの機能を実装
・リセット・もどす・すすむのボタンは横幅を統一

チャット中の問題

  • 盤面と位置表示がズレて表示される
    • いろいろ修正指示を行ったが、ダメで直接 CSS の変更コードを指示
  • 履歴の表示が冗長
    • フォーマット指定

チャットの共有がエラーになるので、共有は後程。

2024-09-13_11h25_08.png

コード

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>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>リバーシゲーム</h1>
    <div class="game-container">
        <div class="board-info">
            <div id="turn">黒の番です</div>
            <button id="reset">リセット</button>
            <button id="undo">もどす</button>
            <button id="redo">すすむ</button>
            <div id="history">履歴</div>
        </div>
        <div class="board-wrapper">
            <div class="column-labels">
                <div>a</div><div>b</div><div>c</div><div>d</div><div>e</div><div>f</div><div>g</div><div>h</div>
            </div>
            <div class="board-container">
                <div class="row-labels">
                    <div>1</div><div>2</div><div>3</div><div>4</div><div>5</div><div>6</div><div>7</div><div>8</div>
                </div>
                <div id="board"></div>
            </div>
        </div>
    </div>

    <script src="script.js"></script>
</body>
</html>
style.css
body {
    font-family: Arial, sans-serif;
    text-align: center;
}

h1 {
    margin-bottom: 20px;
}

.game-container {
    display: flex;
    justify-content: center;
    gap: 20px;
}

.board-info {
    display: flex;
    flex-direction: column;
    align-items: center;
}

#turn {
    font-size: 18px;
    margin-bottom: 10px;
}

button {
    width: 100px;
    margin: 5px 0;
}

#history {
    width: 100px;
    height: 200px;
    overflow-y: scroll;
    border: 1px solid #000;
    margin-top: 10px;
}

.board-wrapper {
    display: grid;
    grid-template-columns: 50px repeat(8, 50px);
    grid-template-rows: 50px repeat(8, 50px);
    justify-content: center;
    margin-top: 20px;  /* 追加: 縦のスペースを調整 */
}

.column-labels, .row-labels {
    display: grid;
    gap: 0;
}

.column-labels {
    grid-column: 2 / span 8;
    display: grid;
    grid-template-columns: repeat(8, 50px);
    text-align: center;
}

.row-labels {
    grid-row: 2 / span 8;
    display: grid;
    grid-template-rows: repeat(8, 50px);
    text-align: center;
    justify-items: center;
}

#board {
    grid-column: 2 / span 8;
    grid-row: 2 / span 8;
    display: grid;
    grid-template-columns: repeat(8, 50px);
    grid-template-rows: repeat(8, 50px);
    gap: 1px;
    position: absolute;
    top: 140px;
    left: calc(50% - 115px); /* 画面の中央に配置 */
}

.cell {
    width: 50px;
    height: 50px;
    background-color: green;
    position: relative;
}

.piece {
    border-radius: 50%;
    width: 80%;
    height: 80%;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.black {
    background-color: black;
}

.white {
    background-color: white;
}
script.js
const board = document.getElementById('board');
const turnDisplay = document.getElementById('turn');
const resetButton = document.getElementById('reset');
const undoButton = document.getElementById('undo');
const redoButton = document.getElementById('redo');
const historyDisplay = document.getElementById('history');

let boardState = Array(8).fill().map(() => Array(8).fill(null));
let history = [];
let currentHistoryIndex = -1;
let currentTurn = 'black';

const columns = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']; // 列名の定義
const directions = [
    [-1, 0], [1, 0], [0, -1], [0, 1], // 上、下、左、右
    [-1, -1], [-1, 1], [1, -1], [1, 1] // 斜め方向
];

// 初期の駒配置
function initializeBoard() {
    board.innerHTML = '';
    boardState = Array(8).fill().map(() => Array(8).fill(null));
    boardState[3][3] = 'white';
    boardState[3][4] = 'black';
    boardState[4][3] = 'black';
    boardState[4][4] = 'white';
    history = [];
    currentHistoryIndex = -1;
    renderBoard();
    updateHistory();
}

// ボードの描画
function renderBoard() {
    board.innerHTML = '';
    for (let row = 0; row < 8; row++) {
        for (let col = 0; col < 8; col++) {
            const cell = document.createElement('div');
            cell.classList.add('cell');
            cell.dataset.row = row;
            cell.dataset.col = col;
            cell.addEventListener('click', handleCellClick);
            const piece = boardState[row][col];
            if (piece) {
                const pieceElement = document.createElement('div');
                pieceElement.classList.add('piece', piece);
                cell.appendChild(pieceElement);
            }
            board.appendChild(cell);
        }
    }
}

// 駒のクリック処理
function handleCellClick(e) {
    const row = parseInt(e.target.dataset.row);
    const col = parseInt(e.target.dataset.col);
    if (boardState[row][col]) return;

    if (isValidMove(row, col, currentTurn)) {
        makeMove(row, col, currentTurn);
        history = history.slice(0, currentHistoryIndex + 1); // 履歴を更新
        const move = `${columns[col]}${row + 1}`; // 置いた場所を「列+行」で記録
        history.push({ board: JSON.parse(JSON.stringify(boardState)), move, player: currentTurn });
        currentHistoryIndex++;
        switchTurn();
        renderBoard();
        updateHistory();
    }
}

// 有効な手かどうかを判定する
function isValidMove(row, col, color) {
    // 8方向のチェックを行い、少なくとも1方向で挟める駒があればtrueを返す
    return directions.some(direction => canFlip(row, col, direction, color));
}

// 駒を挟めるかどうかを確認
function canFlip(row, col, direction, color) {
    let [dx, dy] = direction;
    let x = row + dx;
    let y = col + dy;
    let hasOpponentPiece = false;

    while (x >= 0 && x < 8 && y >= 0 && y < 8 && boardState[x][y] && boardState[x][y] !== color) {
        hasOpponentPiece = true;
        x += dx;
        y += dy;
    }

    // 最後に自分の色の駒があれば、その間にある駒を挟めると判断
    return hasOpponentPiece && x >= 0 && x < 8 && y >= 0 && y < 8 && boardState[x][y] === color;
}

// 駒を配置し、挟んだ駒を反転させる
function makeMove(row, col, color) {
    boardState[row][col] = color;
    flipPieces(row, col, color);
}

// 駒を反転させる処理
function flipPieces(row, col, color) {
    directions.forEach(direction => {
        if (canFlip(row, col, direction, color)) {
            let [dx, dy] = direction;
            let x = row + dx;
            let y = col + dy;

            while (x >= 0 && x < 8 && y >= 0 && y < 8 && boardState[x][y] && boardState[x][y] !== color) {
                boardState[x][y] = color; // 駒を反転
                x += dx;
                y += dy;
            }
        }
    });
}

// ターンの切り替え
function switchTurn() {
    currentTurn = currentTurn === 'black' ? 'white' : 'black';
    turnDisplay.textContent = currentTurn === 'black' ? '黒の番です' : '白の番です';
}

// リセット
resetButton.addEventListener('click', initializeBoard);

// 履歴を戻す
undoButton.addEventListener('click', () => {
    if (currentHistoryIndex > 0) {
        currentHistoryIndex--;
        boardState = JSON.parse(JSON.stringify(history[currentHistoryIndex].board));
        renderBoard();
        updateHistory();
    }
});

// 履歴を進める
redoButton.addEventListener('click', () => {
    if (currentHistoryIndex < history.length - 1) {
        currentHistoryIndex++;
        boardState = JSON.parse(JSON.stringify(history[currentHistoryIndex].board));
        renderBoard();
        updateHistory();
    }
});

// 履歴表示の更新
function updateHistory() {
    historyDisplay.innerHTML = history.map((entry, index) => {
        const playerName = entry.player === 'black' ? '' : ''; // プレイヤー名を日本語に
        return `<div>${index + 1}. ${playerName}: ${entry.move}</div>`;
    }).join('');
}

// ゲーム開始時の初期化
initializeBoard();
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0