ChatGPT によるリバーシゲームの作成手順を変えてみます。
概要
前回(ChatGPT によるリバーシの作成その2)のように、チャットを続けて機能を追加すると、エラー・修正の繰り返しになったので、最初にある程度仕様を与えて開発してみます。
- 仕様に応じてコードが作成されるが、仕様追加・変更すると以前のコードと継続性が無くなり、新たな問題が発生する
- ある程度仕様を修正した後は、チャットで機能追加・変更を行う
- 盤面の位置ズレについては、表示された盤面の画像を読み込ませても、うまく修正されない
※実際の画面をChatGPTに読み込ませても位置ズレは認識できないようです - 位置を調整した CSS を直接指定すると、CSS に余計な変更を加えて結局ズレる
保守する場合は、ある程度固まったコードをベースにして、コードの一部を ChatGPT に出してもらうくらいしかなさそう
現状の問題点
- パス機能が未実装
- 「もどす」後に、別の手順を行うとNG
盤面の変更過程
いろいろ位置が変わったり、盤面の位置ズレ、画面サイズの変更によるズレなど試行錯誤の結果です。
結局、ChatGPT がコードが書けてもその結果のズレをフィードバックするのが困難で、細かな調整は手作業でやるしかなさそう。
チャットの内容
下記仕様で、リバーシゲームを作成してください
・盤面は、div と css で正方形にする
・盤面の上部に a-h の位置を枠の中央に表示
・盤面の左に 1-8 の位置を枠の中央に表示
・盤面と位置表示がずれないように
・白と黒の駒は、丸くわかりやすいように表示
・初期画面で、白2個と黒2個の駒を中央に表示
・黒先行で、どちらの順番かわかるように表示
・盤面をクリックすると、駒がおけるか判定し、おける場合は、駒を配置し、はさんだ駒を反転表示
・ゲームの履歴を右側に固定長で表示
・リセット機能を実装
・もどす・すすむの機能を実装
・リセット・もどす・すすむのボタンは横幅を統一
チャット中の問題
- 盤面と位置表示がズレて表示される
- いろいろ修正指示を行ったが、ダメで直接 CSS の変更コードを指示
- 履歴の表示が冗長
- フォーマット指定
チャットの共有がエラーになるので、共有は後程。
コード
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();