使用AI可以完成简单程序(如:五子棋),甚至都不要调试即可以运行,但逻辑规则复杂的程序就需要反复的调整,修改运行BUG,优化运行性能。(如:中国象棋,支持提示目标落子位置,并要求使用AI算法自动对弈)。
下面是经过反复调整后(N多次),得到的中国象棋游戏的js代码。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>中国象棋</title>
<style>
.board {
display: grid;
grid-template-columns: repeat(9, 50px);
grid-template-rows: repeat(10, 50px);
gap: 1px;
background-color: #f0d9b5;
width: fit-content;
margin: auto;
}
.cell {
width: 50px;
height: 50px;
background-color: #b58863;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
cursor: pointer;
}
.piece {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
border: 2px solid;
background-color: white;
}
.red {
color: red;
border-color: red;
}
.black {
color: black;
border-color: black;
}
.possible-move {
position: relative;
z-index: 1000;
}
.possible-move::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 45px;
height: 45px;
border: 2px dashed white;
border-radius: 50%;
z-index: 1000;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.blink {
animation: blink 1s linear infinite;
}
</style>
</head>
<body>
<h1 align="center">中国象棋</h1>
<div class="board" id="board"></div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const board = document.getElementById('board');
const pieces = {
'red': ['车_red', '马_red', '相_red', '仕_red', '帅_red', '仕_red', '相_red', '马_red', '车_red', '炮_red', '炮_red', '兵_red', '兵_red', '兵_red', '兵_red', '兵_red'],
'black': ['车_black', '马_black', '象_black', '士_black', '将_black', '士_black', '象_black', '马_black', '车_black', '炮_black', '炮_black', '卒_black', '卒_black', '卒_black', '卒_black', '卒_black']
};
const initialPositions = {
'red': [
[0, 1, 2, 3, 4, 5, 6, 7, 8],
[null, null, null, null, null, null, null, null, null],
[null, 9, null, null, null, null, null, 10, null],
[11, null, 12, null, 13, null, 14, null, 15],
[null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null]
],
'black': [
[null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null],
[null, null, null, null, null, null, null, null, null],
[11, null, 12, null, 13, null, 14, null, 15],
[null, 9, null, null, null, null, null, 10, null],
[null, null, null, null, null, null, null, null, null],
[0, 1, 2, 3, 4, 5, 6, 7, 8]
]
};
let selectedPiece = null;
function createBoard() {
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 9; col++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.row = row;
cell.dataset.col = col;
board.appendChild(cell);
const redPieceIndex = initialPositions.red[row]?.[col];
const blackPieceIndex = initialPositions.black[row]?.[col];
if (redPieceIndex !== null && redPieceIndex !== undefined) {
const piece = document.createElement('div');
piece.classList.add('piece', 'red');
piece.textContent = pieces.red[redPieceIndex].split('_')[0];
cell.appendChild(piece);
} else if (blackPieceIndex !== null && blackPieceIndex !== undefined) {
const piece = document.createElement('div');
piece.classList.add('piece', 'black');
piece.textContent = pieces.black[blackPieceIndex].split('_')[0];
cell.appendChild(piece);
}
cell.addEventListener('click', handleCellClick);
}
}
}
function handleCellClick(event) {
let cell = event.target;
if (cell.classList.contains('piece')) {
cell = cell.parentElement;
}
if (!cell) {
console.error('Cell is null');
return;
}
const row = parseInt(cell.dataset.row);
const col = parseInt(cell.dataset.col);
if (selectedPiece) {
if (cell.classList.contains('possible-move')) {
const oldRow = parseInt(selectedPiece.parentElement.dataset.row);
const oldCol = parseInt(selectedPiece.parentElement.dataset.col);
const targetCell = document.querySelector(`.cell[data-row="${oldRow}"][data-col="${oldCol}"]`);
const targetPiece = cell.querySelector('.piece');
if (targetPiece) {
cell.removeChild(targetPiece);
}
targetCell.innerHTML = '';
cell.appendChild(selectedPiece);
selectedPiece = null;
renderBoard();
setTimeout(aiMove, 500);
} else {
selectedPiece = null;
}
clearPossibleMoves();
} else {
const piece = cell.querySelector('.piece');
if (piece) {
const pieceText = piece.textContent;
const color = piece.classList.contains('red') ? 'red' : 'black';
if (color) {
selectedPiece = piece;
highlightPossibleMoves(row, col, color);
} else {
console.error('Color is undefined');
}
}
}
}
function renderBoard() {
const boardState = getBoardState();
const board = document.getElementById('board');
board.innerHTML = '';
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 9; col++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.row = row;
cell.dataset.col = col;
board.appendChild(cell);
const piece = boardState[row][col];
if (piece) {
const [pieceText, color] = piece.split('_');
const pieceElement = document.createElement('div');
pieceElement.classList.add('piece', color);
pieceElement.textContent = pieceText;
cell.appendChild(pieceElement);
}
cell.addEventListener('click', handleCellClick);
}
}
}
function highlightPossibleMoves(row, col, color) {
const piece = selectedPiece.textContent;
const possibleMoves = getPossibleMoves(getBoardState(), row, col, color);
possibleMoves.forEach(([r, c]) => {
const targetCell = document.querySelector(`.cell[data-row="${r}"][data-col="${c}"]`);
if (targetCell) {
targetCell.classList.add('possible-move');
}
});
}
function clearPossibleMoves() {
document.querySelectorAll('.possible-move').forEach(cell => {
cell.classList.remove('possible-move');
});
}
function getPossibleMoves(boardState, row, col, color) {
const piece = boardState[row][col];
const [pieceText, _] = piece.split('_');
const possibleMoves = [];
switch (pieceText) {
case '车':
// 水平移动
for (let i = col + 1; i < 9; i++) {
if (boardState[row][i]) {
if (boardState[row][i].includes(color)) {
break;
} else {
possibleMoves.push([row, i]);
break;
}
}
possibleMoves.push([row, i]);
}
for (let i = col - 1; i >= 0; i--) {
if (boardState[row][i]) {
if (boardState[row][i].includes(color)) {
break;
} else {
possibleMoves.push([row, i]);
break;
}
}
possibleMoves.push([row, i]);
}
// 垂直移动
for (let i = row + 1; i < 10; i++) {
if (boardState[i][col]) {
if (boardState[i][col].includes(color)) {
break;
} else {
possibleMoves.push([i, col]);
break;
}
}
possibleMoves.push([i, col]);
}
for (let i = row - 1; i >= 0; i--) {
if (boardState[i][col]) {
if (boardState[i][col].includes(color)) {
break;
} else {
possibleMoves.push([i, col]);
break;
}
}
possibleMoves.push([i, col]);
}
break;
case '马':
const horseMoves = [
[row - 2, col - 1], [row - 2, col + 1],
[row - 1, col - 2], [row - 1, col + 2],
[row + 1, col - 2], [row + 1, col + 2],
[row + 2, col - 1], [row + 2, col + 1]
];
const horseBlockers = [
[row - 1, col], [row - 1, col],
[row, col - 1], [row, col + 1],
[row, col - 1], [row, col + 1],
[row + 1, col], [row + 1, col]
];
for (let i = 0; i < horseMoves.length; i++) {
const [r, c] = horseMoves[i];
const [br, bc] = horseBlockers[i];
if (r >= 0 && r < 10 && c >= 0 && c < 9) {
if (!boardState[br][bc] && (!boardState[r][c] || !boardState[r][c].includes(color))) {
possibleMoves.push([r, c]);
}
}
}
break;
case '象':
case '相':
const elephantMoves = [
[row - 2, col - 2], [row - 2, col + 2],
[row + 2, col - 2], [row + 2, col + 2]
];
const elephantBlockers = [
[row - 1, col - 1], [row - 1, col + 1],
[row + 1, col - 1], [row + 1, col + 1]
];
for (let i = 0; i < elephantMoves.length; i++) {
const [r, c] = elephantMoves[i];
const [br, bc] = elephantBlockers[i];
if (r >= 0 && r < 10 && c >= 0 && c < 9) {
if (!boardState[br][bc] && (!boardState[r][c] || !boardState[r][c].includes(color))) {
possibleMoves.push([r, c]);
}
}
}
break;
case '士':
case '仕':
const guardMoves = [
[row - 1, col - 1], [row - 1, col + 1],
[row + 1, col - 1], [row + 1, col + 1]
];
guardMoves.forEach(([r, c]) => {
if (r >= 0 && r < 10 && c >= 3 && c <= 5) {
if (!boardState[r][c] || !boardState[r][c].includes(color)) {
possibleMoves.push([r, c]);
}
}
});
break;
case '帅':
case '将':
const kingMoves = [
[row - 1, col], [row + 1, col],
[row, col - 1], [row, col + 1]
];
kingMoves.forEach(([r, c]) => {
if (r >= 0 && r < 10 && c >= 3 && c <= 5) {
if (!boardState[r][c] || !boardState[r][c].includes(color)) {
possibleMoves.push([r, c]);
}
}
});
break;
case '炮':
function checkMoves(start, end, step, fixedRow, fixedCol) {
let piecesBetween = 0;
for (let i = start; i !== end; i += step) {
const targetCell = fixedRow !== null ? boardState[fixedRow][i] : boardState[i][fixedCol];
if (targetCell) {
piecesBetween++;
}
if (piecesBetween === 0) {
if (!targetCell || !targetCell.includes(color)) {
possibleMoves.push(fixedRow !== null ? [fixedRow, i] : [i, fixedCol]);
}
} else if (piecesBetween === 2) {
if (targetCell && !targetCell.includes(color)) {
possibleMoves.push(fixedRow !== null ? [fixedRow, i] : [i, fixedCol]);
}
}
}
}
// 水平移动
checkMoves(col + 1, 9, 1, row, null);
checkMoves(col - 1, -1, -1, row, null);
// 垂直移动
checkMoves(row + 1, 10, 1, null, col);
checkMoves(row - 1, -1, -1, null, col);
break;
case '兵':
case '卒':
if (color === 'red') {
if (row < 5) {
possibleMoves.push([row + 1, col]);
} else {
possibleMoves.push([row - 1, col]);
possibleMoves.push([row, col - 1]);
possibleMoves.push([row, col + 1]);
}
} else {
if (row > 4) {
possibleMoves.push([row - 1, col]);
} else {
possibleMoves.push([row + 1, col]);
possibleMoves.push([row, col - 1]);
possibleMoves.push([row, col + 1]);
}
}
break;
}
return possibleMoves;
}
function getBoardState() {
const boardState = [];
for (let row = 0; row < 10; row++) {
const rowState = [];
for (let col = 0; col < 9; col++) {
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
const piece = cell.querySelector('.piece');
if (piece) {
const color = piece.classList.contains('red') ? 'red' : 'black';
rowState.push(`${piece.textContent}_${color}`);
} else {
rowState.push(null);
}
}
boardState.push(rowState);
}
return boardState;
}
function aiMove() {
const boardState = getBoardState();
const moves = generateMoves(boardState, true);
let bestMove = null;
let bestValue = -Infinity;
for (const [fromRow, fromCol, toRow, toCol] of moves) {
const newBoardState = makeMove(boardState, fromRow, fromCol, toRow, toCol);
const value = evaluateBoard(newBoardState, true);
if (value > bestValue) {
bestValue = value;
bestMove = [fromRow, fromCol, toRow, toCol];
}
}
if (bestMove) {
const [fromRow, fromCol, toRow, toCol] = bestMove;
const fromCell = document.querySelector(`.cell[data-row="${fromRow}"][data-col="${fromCol}"]`);
const toCell = document.querySelector(`.cell[data-row="${toRow}"][data-col="${toCol}"]`);
const piece = fromCell.querySelector('.piece');
const targetPiece = toCell.querySelector('.piece');
if (targetPiece) {
toCell.removeChild(targetPiece);
}
fromCell.innerHTML = '';
toCell.appendChild(piece);
piece.classList.add('blink');
setTimeout(() => {
piece.classList.remove('blink');
}, 2000);
// Check if the game is over after the move
const newBoardState = getBoardState();
const gameStatus = isGameOver(newBoardState);
if (gameStatus) {
if (gameStatus === 'check') {
alert('将军!');
} else {
alert(`游戏结束!${gameStatus === 'red' ? '红方' : '黑方'}获胜!`);
resetBoard();
}
}
} else {
console.error('No valid move found');
}
}
function resetBoard() {
const board = document.getElementById('board');
board.innerHTML = '';
createBoard();
}
function generateMoves(boardState, isMaximizingPlayer) {
const moves = [];
const player = isMaximizingPlayer ? 'red' : 'black';
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 9; col++) {
const piece = boardState[row][col];
if (piece && (piece.includes(player))) {
const possibleMoves = getPossibleMoves(boardState, row, col, player);
for (const [toRow, toCol] of possibleMoves) {
moves.push([row, col, toRow, toCol]);
}
}
}
}
return moves;
}
function makeMove(boardState, fromRow, fromCol, toRow, toCol) {
const newBoardState = boardState.map(row => row.slice());
newBoardState[toRow][toCol] = newBoardState[fromRow][fromCol];
newBoardState[fromRow][fromCol] = null;
return newBoardState;
}
function evaluateBoard(boardState, isMaximizingPlayer) {
const pieceValues = {
'车': 10,
'马': 5,
'象': 3,
'相': 3,
'士': 3,
'仕': 3,
'将': 100,
'帅': 100,
'炮': 5,
'兵': 2,
'卒': 2
};
let score = 0;
const player = isMaximizingPlayer ? 'red' : 'black';
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 9; col++) {
const piece = boardState[row][col];
if (piece){
const [pieceText, color] = piece.split('_');
if (pieceText) {
const value = pieceValues[pieceText];
if (color ===player) {
score += value;
} else {
score -= value;
}
}
}
}
}
return score;
}
function isGameOver(boardState) {
let redKingExists = false;
let blackKingExists = false;
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 9; col++) {
const piece = boardState[row][col];
if (piece) {
const [pieceText, color] = piece.split('_');
if (pieceText === '帅') {
redKingExists = true;
} else if (pieceText === '将') {
blackKingExists = true;
}
}
}
}
if (!redKingExists) {
return 'black'; // 黑方获胜
} else if (!blackKingExists) {
return 'red'; // 红方获胜
}
// Check for check condition
const redKingPosition = findKingPosition(boardState, 'red');
const blackKingPosition = findKingPosition(boardState, 'black');
if (isKingInCheck(boardState, redKingPosition, 'red')) {
return 'check';
}
if (isKingInCheck(boardState, blackKingPosition, 'black')) {
return 'check';
}
return null;
}
function findKingPosition(boardState, color) {
const kingText = color === 'red' ? '帅' : '将';
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 9; col++) {
const piece = boardState[row][col];
if (piece && piece.includes(kingText)) {
return [row, col];
}
}
}
return null;
}
function isKingInCheck(boardState, kingPosition, color) {
const [kingRow, kingCol] = kingPosition;
const opponentColor = color === 'red' ? 'black' : 'red';
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 9; col++) {
const piece = boardState[row][col];
if (piece && piece.includes(opponentColor)) {
const [pieceText, _] = piece.split('_');
const possibleMoves = getPossibleMoves(boardState, row, col, opponentColor);
if (possibleMoves.some(([r, c]) => r === kingRow && c === kingCol)) {
return true;
}
}
}
}
return false;
}
createBoard();
});
</script>
</body>
</html>