11.7 脚本网站 中国象棋
本来flask部署了一下,但是太臭太长,直接给个能能玩的htnl,需要完整也可以私信我

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>中国象棋</title>
<style>
/* 基础样式 - 添加响应式设计 */
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 10px;
background-color: #f0e6d2;
font-family: Arial, sans-serif;
box-sizing: border-box;
}
.game-container {
text-align: center;
max-width: 100vw;
overflow-x: auto;
}
/* 模式选择弹窗 */
.mode-selection {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.mode-box {
background-color: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
max-width: 90vw;
}
.mode-box h2 {
margin-bottom: 20px;
color: #8b4513;
}
.mode-button {
display: block;
width: 200px;
margin: 10px auto;
padding: 15px;
font-size: 18px;
background-color: #8b4513;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.mode-button:hover {
background-color: #a0522d;
}
/* 响应式棋盘样式 */
.board {
position: relative;
width: 90vw;
max-width: 600px;
height: calc(90vw * 1.1);
max-height: 660px;
background-color: #f9d4b4;
border: 3px solid #8b4513;
margin: 20px auto;
box-shadow: 0 0 20px rgba(0,0,0,0.3);
}
@media (min-width: 768px) {
.board {
width: 600px;
height: 660px;
}
}
.piece {
position: absolute;
width: 8.33vw;
max-width: 50px;
height: 8.33vw;
max-height: 50px;
border-radius: 50%;
font-size: 4vw;
max-font-size: 24px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
z-index: 10;
user-select: none;
}
@media (min-width: 768px) {
.piece {
width: 50px;
height: 50px;
font-size: 24px;
}
}
.piece.red {
background: radial-gradient(circle at 30% 30%, #ff6b6b, #cc0000);
color: #fff;
border: 2px solid #990000;
box-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.piece.black {
background: radial-gradient(circle at 30% 30%, #666, #000);
color: #fff;
border: 2px solid #000;
box-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.piece.selected {
transform: scale(1.1);
box-shadow: 0 0 20px #ffff00, 2px 2px 4px rgba(0,0,0,0.3);
border-color: #ffff00;
}
.piece.hint {
animation: pulse 1s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.15); }
100% { transform: scale(1); }
}
.grid {
position: absolute;
width: 10vw;
max-width: 60px;
height: 10vw;
max-height: 60px;
cursor: pointer;
z-index: 1;
}
@media (min-width: 768px) {
.grid {
width: 60px;
height: 60px;
}
}
/* 棋盘线条响应式 */
.board-lines {
position: absolute;
top: 5vw;
left: 5vw;
width: 80vw;
max-width: 540px;
height: calc(80vw * 1.11);
max-height: 600px;
}
@media (min-width: 768px) {
.board-lines {
top: 30px;
left: 30px;
width: 540px;
height: 600px;
}
}
.horizontal-line {
position: absolute;
width: 80vw;
max-width: 540px;
height: 1px;
background-color: #000;
}
@media (min-width: 768px) {
.horizontal-line {
width: 540px;
}
}
.vertical-line {
position: absolute;
width: 1px;
height: calc(80vw * 1.11);
max-height: 600px;
background-color: #000;
}
@media (min-width: 768px) {
.vertical-line {
height: 600px;
}
}
.river {
position: absolute;
top: calc(45vw - 5vw);
left: 5vw;
width: 80vw;
max-width: 540px;
height: 10vw;
max-height: 60px;
background-color: rgba(135, 206, 235, 0.3);
pointer-events: none;
}
@media (min-width: 768px) {
.river {
top: 270px;
left: 30px;
width: 540px;
height: 60px;
}
}
.controls {
margin-top: 20px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 10px;
}
button {
padding: 10px 20px;
font-size: 16px;
background-color: #8b4513;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
margin: 5px;
}
button:hover {
background-color: #a0522d;
}
.status {
margin-top: 10px;
font-size: 18px;
font-weight: bold;
}
.hint-text {
margin-top: 10px;
font-size: 14px;
color: #666;
min-height: 20px;
}
.move-history {
margin-top: 20px;
max-height: 150px;
overflow-y: auto;
background-color: rgba(255, 255, 255, 0.5);
padding: 10px;
border-radius: 5px;
width: 90vw;
max-width: 300px;
margin-left: auto;
margin-right: auto;
}
.move-item {
font-size: 12px;
margin: 2px 0;
padding: 2px 5px;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 3px;
}
.move-item.red {
color: #cc0000;
}
.move-item.black {
color: #000;
}
/* 新增 . 数据页面*/
.stats-btn {
display: inline-block;
padding: 6px 14px;
background: #f9d4b4;
border: 2px solid #8b4513;
border-radius: 6px;
color: #8b4513;
text-decoration: none;
font-weight: bold;
margin-left: 20px;
transition: all 0.3s ease;
}
.stats-btn:hover {
background: #8b4513;
color: #fff;
}
</style>
</head>
<body>
<!-- 模式选择 -->
<div class="mode-selection" id="modeSelection">
<div class="mode-box">
<h2>选择游戏模式</h2>
<button class="mode-button" onclick="startGame('pvp')">双人对战</button>
<button class="mode-button" onclick="startGame('ai')">AI对战</button>
</div>
</div>
<div class="game-container">
<h1>中国象棋</h1>
<a href="/stats.html" class="stats-btn">📊 数据排行</a>
<div class="board" id="board">
<div class="board-lines">
<div class="horizontal-line" style="top: 0%;"></div>
<div class="horizontal-line" style="top: 10%;"></div>
<div class="horizontal-line" style="top: 20%;"></div>
<div class="horizontal-line" style="top: 30%;"></div>
<div class="horizontal-line" style="top: 40%;"></div>
<div class="horizontal-line" style="top: 50%;"></div>
<div class="horizontal-line" style="top: 60%;"></div>
<div class="horizontal-line" style="top: 70%;"></div>
<div class="horizontal-line" style="top: 80%;"></div>
<div class="horizontal-line" style="top: 90%;"></div>
<div class="horizontal-line" style="top: 100%;"></div>
<div class="vertical-line" style="left: 0%;"></div>
<div class="vertical-line" style="left: 12.5%;"></div>
<div class="vertical-line" style="left: 25%;"></div>
<div class="vertical-line" style="left: 37.5%;"></div>
<div class="vertical-line" style="left: 50%;"></div>
<div class="vertical-line" style="left: 62.5%;"></div>
<div class="vertical-line" style="left: 75%;"></div>
<div class="vertical-line" style="left: 87.5%;"></div>
<div class="vertical-line" style="left: 100%;"></div>
<div class="river"></div>
<div class="diagonal" style="left: 37.5%; top: 0%; transform: rotate(45deg);"></div>
<div class="diagonal" style="left: 87.5%; top: 0%; transform: rotate(-45deg);"></div>
<div class="diagonal" style="left: 37.5%; top: 75%; transform: rotate(-45deg);"></div>
<div class="diagonal" style="left: 87.5%; top: 75%; transform: rotate(45deg);"></div>
</div>
</div>
<div class="controls">
<button onclick="resetGame()">重新开始</button>
<button onclick="showRules()">规则说明</button>
<button onclick="undoMove()">悔棋</button>
<button onclick="getHint()">提示走法</button>
</div>
<div class="status">
当前玩家:<span id="currentPlayer" class="current-player">红方</span>
<span id="gameMode" style="margin-left: 20px; color: #666;"></span>
</div>
<div class="hint-text" id="hintText"></div>
<div class="move-history" id="moveHistory">
<h4>走棋记录</h4>
<div id="moveList"></div>
</div>
</div>
<script>
// 棋盘尺寸
const BOARD_WIDTH = 9;
const BOARD_HEIGHT = 10;
// 响应式尺寸计算
function getCellSize() {
const board = document.getElementById('board');
const boardWidth = board.offsetWidth;
return boardWidth / BOARD_WIDTH;
}
function getPieceSize() {
const cellSize = getCellSize();
return cellSize * 0.833; // 棋子占格子的83.3%
}
// 棋子类型
const PIECE_TYPES = {
SHUAI: '帅',
SHI: '士',
XIANG: '相',
MA: '马',
JU: '车',
PAO: '炮',
BING: '兵'
};
// 游戏状态
let board = [];
let selectedPiece = null;
let currentPlayer = 'red';
let gameOver = false;
let moveHistory = [];
let gameMode = '';
let aiThinking = false;
// 初始化棋盘
function initBoard() {
board = Array(BOARD_HEIGHT).fill(null).map(() => Array(BOARD_WIDTH).fill(null));
// 红方棋子
const redPieces = [
{type: PIECE_TYPES.JU, row: 9, col: 0, player: 'red'},
{type: PIECE_TYPES.MA, row: 9, col: 1, player: 'red'},
{type: PIECE_TYPES.XIANG, row: 9, col: 2, player: 'red'},
{type: PIECE_TYPES.SHI, row: 9, col: 3, player: 'red'},
{type: PIECE_TYPES.SHUAI, row: 9, col: 4, player: 'red'},
{type: PIECE_TYPES.SHI, row: 9, col: 5, player: 'red'},
{type: PIECE_TYPES.XIANG, row: 9, col: 6, player: 'red'},
{type: PIECE_TYPES.MA, row: 9, col: 7, player: 'red'},
{type: PIECE_TYPES.JU, row: 9, col: 8, player: 'red'},
{type: PIECE_TYPES.PAO, row: 7, col: 1, player: 'red'},
{type: PIECE_TYPES.PAO, row: 7, col: 7, player: 'red'},
{type: PIECE_TYPES.BING, row: 6, col: 0, player: 'red'},
{type: PIECE_TYPES.BING, row: 6, col: 2, player: 'red'},
{type: PIECE_TYPES.BING, row: 6, col: 4, player: 'red'},
{type: PIECE_TYPES.BING, row: 6, col: 6, player: 'red'},
{type: PIECE_TYPES.BING, row: 6, col: 8, player: 'red'}
];
// 黑方棋子
const blackPieces = [
{type: PIECE_TYPES.JU, row: 0, col: 0, player: 'black'},
{type: PIECE_TYPES.MA, row: 0, col: 1, player: 'black'},
{type: PIECE_TYPES.XIANG, row: 0, col: 2, player: 'black'},
{type: PIECE_TYPES.SHI, row: 0, col: 3, player: 'black'},
{type: PIECE_TYPES.SHUAI, row: 0, col: 4, player: 'black'},
{type: PIECE_TYPES.SHI, row: 0, col: 5, player: 'black'},
{type: PIECE_TYPES.XIANG, row: 0, col: 6, player: 'black'},
{type: PIECE_TYPES.MA, row: 0, col: 7, player: 'black'},
{type: PIECE_TYPES.JU, row: 0, col: 8, player: 'black'},
{type: PIECE_TYPES.PAO, row: 2, col: 1, player: 'black'},
{type: PIECE_TYPES.PAO, row: 2, col: 7, player: 'black'},
{type: PIECE_TYPES.BING, row: 3, col: 0, player: 'black'},
{type: PIECE_TYPES.BING, row: 3, col: 2, player: 'black'},
{type: PIECE_TYPES.BING, row: 3, col: 4, player: 'black'},
{type: PIECE_TYPES.BING, row: 3, col: 6, player: 'black'},
{type: PIECE_TYPES.BING, row: 3, col: 8, player: 'black'}
];
// 将所有棋子放置到棋盘上
[...redPieces, ...blackPieces].forEach(piece => {
board[piece.row][piece.col] = piece;
});
}
// 渲染棋盘
function renderBoard() {
const boardElement = document.getElementById('board');
const cellSize = getCellSize();
const pieceSize = getPieceSize();
// 清除现有的棋子和格子
const existingPieces = boardElement.querySelectorAll('.piece, .grid');
existingPieces.forEach(el => el.remove());
// 创建点击格子
for (let row = 0; row < BOARD_HEIGHT; row++) {
for (let col = 0; col < BOARD_WIDTH; col++) {
const grid = document.createElement('div');
grid.className = 'grid';
grid.style.left = `${col * cellSize + (cellSize - pieceSize) / 2}px`;
grid.style.top = `${row * cellSize + (cellSize - pieceSize) / 2}px`;
grid.style.width = `${cellSize}px`;
grid.style.height = `${cellSize}px`;
grid.dataset.row = row;
grid.dataset.col = col;
grid.onclick = () => handleGridClick(row, col);
boardElement.appendChild(grid);
}
}
// 创建棋子
for (let row = 0; row < BOARD_HEIGHT; row++) {
for (let col = 0; col < BOARD_WIDTH; col++) {
const piece = board[row][col];
if (piece) {
const pieceElement = document.createElement('div');
pieceElement.className = `piece ${piece.player}`;
pieceElement.textContent = piece.type;
pieceElement.style.left = `${col * cellSize + (cellSize - pieceSize) / 2}px`;
pieceElement.style.top = `${row * cellSize + (cellSize - pieceSize) / 2}px`;
pieceElement.style.width = `${pieceSize}px`;
pieceElement.style.height = `${pieceSize}px`;
pieceElement.dataset.row = row;
pieceElement.dataset.col = col;
pieceElement.onclick = (e) => {
e.stopPropagation();
handlePieceClick(row, col);
};
boardElement.appendChild(pieceElement);
}
}
}
}
// 处理棋子点击
function handlePieceClick(row, col) {
if (gameOver || aiThinking) return;
const piece = board[row][col];
if (!piece) return;
/*---------- 情况 1:已选中己方棋子,再点对方棋子 → 直接吃 ----------*/
if (selectedPiece && piece.player !== currentPlayer) {
if (isValidMove(selectedPiece.row, selectedPiece.col, row, col)) {
movePiece(selectedPiece.row, selectedPiece.col, row, col);
// 吃完若轮到 AI,立即思考
if (gameMode === 'ai' && currentPlayer === 'black' && !gameOver) {
setTimeout(() => aiMove(), 500);
}
}
return;
}
/*---------- 情况 2:没选中己方棋子,点对方棋子 → 自动找能吃它的己方棋子 ----------*/
if (piece.player !== currentPlayer) {
for (let r = 0; r < BOARD_HEIGHT; r++) {
for (let c = 0; c < BOARD_WIDTH; c++) {
const my = board[r][c];
if (my && my.player === currentPlayer && isValidMove(r, c, row, col)) {
movePiece(r, c, row, col);
// 吃完若轮到 AI,立即思考
if (gameMode === 'ai' && currentPlayer === 'black' && !gameOver) {
setTimeout(() => aiMove(), 500);
}
return;
}
}
}
return; // 没有任何己方棋子能吃它
}
/*---------- 情况 3:点己方棋子 → 正常选中 ----------*/
clearSelection();
selectedPiece = {row, col, piece};
highlightPiece(row, col);
showValidMoves(row, col);
}
// 处理格子点击
function handleGridClick(row, col) {
if (gameOver || !selectedPiece || aiThinking) return;
const targetPiece = board[row][col];
if (targetPiece && targetPiece.player === currentPlayer) {
// 如果点击的是己方棋子,选中它
handlePieceClick(row, col);
return;
}
// 尝试移动棋子
if (isValidMove(selectedPiece.row, selectedPiece.col, row, col)) {
movePiece(selectedPiece.row, selectedPiece.col, row, col);
// AI模式下,如果轮到AI走棋
if (gameMode === 'ai' && currentPlayer === 'black' && !gameOver) {
setTimeout(() => aiMove(), 500);
}
}
}
// AI走棋
function aiMove() {
if (gameOver || aiThinking) return;
aiThinking = true;
document.getElementById('hintText').textContent = 'AI正在思考...';
setTimeout(() => {
const bestMove = findBestMove(3); // 思考3步
if (bestMove) {
movePiece(bestMove.from.row, bestMove.from.col, bestMove.to.row, bestMove.to.col);
}
aiThinking = false;
}, 1000);
}
// 使用Minimax算法找到最佳移动
function findBestMove(depth) {
const possibleMoves = getAllPossibleMoves('black');
if (possibleMoves.length === 0) return null;
let bestMove = null;
let bestScore = -Infinity;
for (const move of possibleMoves) {
// 执行移动
const moveData = executeMove(move);
// 评估局面
const score = minimax(depth - 1, -Infinity, Infinity, false);
// 撤销移动
undoMoveData(moveData);
if (score > bestScore) {
bestScore = score;
bestMove = move;
}
}
return bestMove;
}
// Minimax算法
function minimax(depth, alpha, beta, isMaximizing) {
if (depth === 0 || gameOver) {
return evaluateBoard();
}
const player = isMaximizing ? 'black' : 'red';
const possibleMoves = getAllPossibleMoves(player);
if (isMaximizing) {
let maxEval = -Infinity;
for (const move of possibleMoves) {
const moveData = executeMove(move);
const eval = minimax(depth - 1, alpha, beta, false);
undoMoveData(moveData);
maxEval = Math.max(maxEval, eval);
alpha = Math.max(alpha, eval);
if (beta <= alpha) break;
}
return maxEval;
} else {
let minEval = Infinity;
for (const move of possibleMoves) {
const moveData = executeMove(move);
const eval = minimax(depth - 1, alpha, beta, true);
undoMoveData(moveData);
minEval = Math.min(minEval, eval);
beta = Math.min(beta, eval);
if (beta <= alpha) break;
}
return minEval;
}
}
// 获取所有可能的移动
function getAllPossibleMoves(player) {
const moves = [];
for (let row = 0; row < BOARD_HEIGHT; row++) {
for (let col = 0; col < BOARD_WIDTH; col++) {
const piece = board[row][col];
if (piece && piece.player === player) {
for (let toRow = 0; toRow < BOARD_HEIGHT; toRow++) {
for (let toCol = 0; toCol < BOARD_WIDTH; toCol++) {
if (isValidMove(row, col, toRow, toCol)) {
moves.push({
from: {row, col},
to: {row: toRow, col: toCol},
piece: piece
});
}
}
}
}
}
}
return moves;
}
// 执行移动并返回移动数据
function executeMove(move) {
const piece = board[move.from.row][move.from.col];
const targetPiece = board[move.to.row][move.to.col];
const moveData = {
from: {...move.from},
to: {...move.to},
piece: {...piece},
captured: targetPiece ? {...targetPiece} : null,
board: board.map(row => row.map(cell => cell ? {...cell} : null))
};
board[move.to.row][move.to.col] = piece;
board[move.from.row][move.from.col] = null;
return moveData;
}
// 撤销移动数据
function undoMoveData(moveData) {
board = moveData.board.map(row => row.map(cell => cell ? {...cell} : null));
}
// 评估棋盘局面
function evaluateBoard() {
let score = 0;
const pieceValues = {
[PIECE_TYPES.BING]: 10,
[PIECE_TYPES.PAO]: 30,
[PIECE_TYPES.MA]: 30,
[PIECE_TYPES.XIANG]: 20,
[PIECE_TYPES.SHI]: 20,
[PIECE_TYPES.JU]: 50,
[PIECE_TYPES.SHUAI]: 1000
};
for (let row = 0; row < BOARD_HEIGHT; row++) {
for (let col = 0; col < BOARD_WIDTH; col++) {
const piece = board[row][col];
if (piece) {
const value = pieceValues[piece.type] || 0;
if (piece.player === 'black') {
score += value;
} else {
score -= value;
}
}
}
}
return score;
}
// 清除选中状态
function clearSelection() {
selectedPiece = null;
document.querySelectorAll('.piece.selected').forEach(el => {
el.classList.remove('selected');
});
document.querySelectorAll('.piece.hint').forEach(el => {
el.classList.remove('hint');
});
document.querySelectorAll('.piece.valid-move').forEach(el => {
el.classList.remove('valid-move');
});
}
// 高亮选中的棋子
function highlightPiece(row, col) {
const pieceElement = document.querySelector(`.piece[data-row="${row}"][data-col="${col}"]`);
if (pieceElement) {
pieceElement.classList.add('selected');
}
}
// 显示有效移动位置
function showValidMoves(row, col) {
for (let r = 0; r < BOARD_HEIGHT; r++) {
for (let c = 0; c < BOARD_WIDTH; c++) {
if (isValidMove(row, col, r, c)) {
const pieceElement = document.querySelector(`.piece[data-row="${r}"][data-col="${c}"]`);
if (pieceElement) {
pieceElement.classList.add('valid-move');
}
}
}
}
}
// 检查移动是否有效
function isValidMove(fromRow, fromCol, toRow, toCol) {
const piece = board[fromRow][fromCol];
if (!piece) return false;
const targetPiece = board[toRow][toCol];
if (targetPiece && targetPiece.player === piece.player) return false;
const rowDiff = toRow - fromRow;
const colDiff = toCol - fromCol;
const absRowDiff = Math.abs(rowDiff);
const absColDiff = Math.abs(colDiff);
switch (piece.type) {
case PIECE_TYPES.SHUAI:
return isValidShuaiMove(fromRow, fromCol, toRow, toCol);
case PIECE_TYPES.SHI:
if (piece.player === 'red') {
return toRow >= 7 && toRow <= 9 && toCol >= 3 && toCol <= 5 &&
absRowDiff === 1 && absColDiff === 1;
} else {
return toRow >= 0 && toRow <= 2 && toCol >= 3 && toCol <= 5 &&
absRowDiff === 1 && absColDiff === 1;
}
case PIECE_TYPES.XIANG:
if (piece.player === 'red' && toRow < 5) return false;
if (piece.player === 'black' && toRow > 4) return false;
if (absRowDiff !== 2 || absColDiff !== 2) return false;
// 检查象眼是否被堵住
const midRow = fromRow + rowDiff / 2;
const midCol = fromCol + colDiff / 2;
return board[midRow][midCol] === null;
case PIECE_TYPES.MA:
if (!((absRowDiff === 2 && absColDiff === 1) || (absRowDiff === 1 && absColDiff === 2))) return false;
// 检查马腿是否被堵住
if (absRowDiff === 2) {
const midRow = fromRow + rowDiff / 2;
return board[midRow][fromCol] === null;
} else {
const midCol = fromCol + colDiff / 2;
return board[fromRow][midCol] === null;
}
case PIECE_TYPES.JU:
return isValidJuMove(fromRow, fromCol, toRow, toCol);
case PIECE_TYPES.PAO:
return isValidPaoMove(fromRow, fromCol, toRow, toCol);
case PIECE_TYPES.BING:
return isValidBingMove(fromRow, fromCol, toRow, toCol);
default:
return false;
}
}
// 检查帅的移动
function isValidShuaiMove(fromRow, fromCol, toRow, toCol) {
const piece = board[fromRow][fromCol];
const targetPiece = board[toRow][toCol];
// 检查是否在九宫格内
if (piece.player === 'red') {
if (toRow < 7 || toRow > 9 || toCol < 3 || toCol > 5) return false;
} else {
if (toRow < 0 || toRow > 2 || toCol < 3 || toCol > 5) return false;
}
// 检查是否直线移动一格
const rowDiff = Math.abs(toRow - fromRow);
const colDiff = Math.abs(toCol - fromCol);
if (!((rowDiff === 1 && colDiff === 0) || (rowDiff === 0 && colDiff === 1))) {
return false;
}
// 检查是否在对将(飞将)
if (targetPiece && targetPiece.type === PIECE_TYPES.SHUAI) {
if (fromCol === toCol) {
// 检查中间是否有其他棋子
const minRow = Math.min(fromRow, toRow);
const maxRow = Math.max(fromRow, toRow);
for (let row = minRow + 1; row < maxRow; row++) {
if (board[row][fromCol] !== null) return false;
}
return true;
}
}
return true;
}
// 检查车的移动
function isValidJuMove(fromRow, fromCol, toRow, toCol) {
if (fromRow !== toRow && fromCol !== toCol) return false;
if (fromRow === toRow) {
const minCol = Math.min(fromCol, toCol);
const maxCol = Math.max(fromCol, toCol);
for (let col = minCol + 1; col < maxCol; col++) {
if (board[fromRow][col] !== null) return false;
}
} else {
const minRow = Math.min(fromRow, toRow);
const maxRow = Math.max(fromRow, toRow);
for (let row = minRow + 1; row < maxRow; row++) {
if (board[row][fromCol] !== null) return false;
}
}
return true;
}
// 检查炮的移动
function isValidPaoMove(fromRow, fromCol, toRow, toCol) {
if (fromRow !== toRow && fromCol !== toCol) return false;
const targetPiece = board[toRow][toCol];
let pieceCount = 0;
if (fromRow === toRow) {
const minCol = Math.min(fromCol, toCol);
const maxCol = Math.max(fromCol, toCol);
for (let col = minCol + 1; col < maxCol; col++) {
if (board[fromRow][col] !== null) pieceCount++;
}
} else {
const minRow = Math.min(fromRow, toRow);
const maxRow = Math.max(fromRow, toRow);
for (let row = minRow + 1; row < maxRow; row++) {
if (board[row][fromCol] !== null) pieceCount++;
}
}
if (targetPiece) {
return pieceCount === 1;
} else {
return pieceCount === 0;
}
}
// 检查兵的移动
function isValidBingMove(fromRow, fromCol, toRow, toCol) {
const piece = board[fromRow][fromCol];
const rowDiff = toRow - fromRow;
const colDiff = Math.abs(toCol - fromCol);
if (piece.player === 'red') {
// 红方兵向下移动
if (rowDiff > 0) return false;
if (fromRow > 4) {
// 过河后可以左右移动
return (rowDiff === -1 && colDiff === 0) || (rowDiff === 0 && colDiff === 1);
} else {
// 未过河只能向前
return rowDiff === -1 && colDiff === 0;
}
} else {
// 黑方兵向上移动
if (rowDiff < 0) return false;
if (fromRow < 5) {
// 过河后可以左右移动
return (rowDiff === 1 && colDiff === 0) || (rowDiff === 0 && colDiff === 1);
} else {
// 未过河只能向前
return rowDiff === 1 && colDiff === 0;
}
}
}
// 移动棋子
function movePiece(fromRow, fromCol, toRow, toCol) {
const piece = board[fromRow][fromCol];
const targetPiece = board[toRow][toCol];
// 记录移动前的状态
const moveData = {
from: {row: fromRow, col: fromCol},
to: {row: toRow, col: toCol},
piece: {...piece},
captured: targetPiece ? {...targetPiece} : null,
board: board.map(row => row.map(cell => cell ? {...cell} : null))
};
// 执行移动
board[toRow][toCol] = piece;
board[fromRow][fromCol] = null;
// 添加到历史记录
addMoveToHistory(moveData);
// 检查游戏结束条件
if (targetPiece) {
if (targetPiece.type === PIECE_TYPES.SHUAI) {
gameOver = true;
setTimeout(() => {
alert(`${currentPlayer === 'red' ? '红方' : '黑方'}获胜!`);
}, 100);
}
}
// 切换玩家
currentPlayer = currentPlayer === 'red' ? 'black' : 'red';
updateStatus();
// 清除提示
document.getElementById('hintText').textContent = '';
// 清除选中状态
clearSelection();
// 重新渲染棋盘
renderBoard();
}
// 添加移动到历史记录
function addMoveToHistory(moveData) {
const moveList = document.getElementById('moveList');
const moveItem = document.createElement('div');
moveItem.className = `move-item ${currentPlayer}`;
const fromPos = `${9 - moveData.from.row}${String.fromCharCode(97 + moveData.from.col)}`;
const toPos = `${9 - moveData.to.row}${String.fromCharCode(97 + moveData.to.col)}`;
const captureText = moveData.captured ? ` 吃${moveData.captured.type}` : '';
moveItem.textContent = `${moveData.piece.type}: ${fromPos} → ${toPos}${captureText}`;
moveList.insertBefore(moveItem, moveList.firstChild);
moveHistory.push(moveData);
}
// 悔棋
function undoMove() {
if (moveHistory.length === 0) {
alert('没有可以悔棋的记录了!');
return;
}
if (gameMode === 'ai' && moveHistory.length < 2) {
alert('AI模式下至少需要走两步才能悔棋!');
return;
}
if (confirm('确定要悔棋吗?')) {
// AI模式下需要撤销两步(玩家和AI各一步)
const steps = gameMode === 'ai' ? 2 : 1;
for (let i = 0; i < steps; i++) {
if (moveHistory.length === 0) break;
const lastMove = moveHistory.pop();
// 恢复棋盘状态
board = lastMove.board.map(row => row.map(cell => cell ? {...cell} : null));
// 移除最后一个走棋记录
const moveList = document.getElementById('moveList');
if (moveList.firstChild) {
moveList.removeChild(moveList.firstChild);
}
}
// 切换回上一个玩家
currentPlayer = currentPlayer === 'red' ? 'black' : 'red';
updateStatus();
// 清除选中状态和提示
clearSelection();
document.getElementById('hintText').textContent = '';
// 重新渲染棋盘
renderBoard();
}
}
// 获取提示(增强版)
function getHint() {
if (gameOver || aiThinking) return;
// 获取当前玩家的所有棋子
const playerPieces = [];
for (let row = 0; row < BOARD_HEIGHT; row++) {
for (let col = 0; col < BOARD_WIDTH; col++) {
const piece = board[row][col];
if (piece && piece.player === currentPlayer) {
playerPieces.push({row, col, piece});
}
}
}
// 寻找所有可能的移动
const possibleMoves = [];
for (const pieceInfo of playerPieces) {
for (let toRow = 0; toRow < BOARD_HEIGHT; toRow++) {
for (let toCol = 0; toCol < BOARD_WIDTH; toCol++) {
if (isValidMove(pieceInfo.row, pieceInfo.col, toRow, toCol)) {
// 深度评估这个移动(考虑3步)
const score = evaluateMoveDeep(pieceInfo.row, pieceInfo.col, toRow, toCol, 3);
possibleMoves.push({
from: {row: pieceInfo.row, col: pieceInfo.col},
to: {row: toRow, col: toCol},
piece: pieceInfo.piece,
score: score
});
}
}
}
}
if (possibleMoves.length === 0) {
document.getElementById('hintText').textContent = '没有可走的棋子了!';
return;
}
// 按分数排序,选择最好的移动
possibleMoves.sort((a, b) => b.score - a.score);
const bestMove = possibleMoves[0];
// 显示提示
const hintText = document.getElementById('hintText');
const fromPos = `${9 - bestMove.from.row}${String.fromCharCode(97 + bestMove.from.col)}`;
const toPos = `${9 - bestMove.to.row}${String.fromCharCode(97 + bestMove.to.col)}`;
let reason = '';
if (bestMove.score > 50) {
reason = ' (可以吃掉对方重要棋子)';
} else if (bestMove.score < -20) {
reason = ' (可能被吃,谨慎考虑)';
} else {
reason = ' (较为安全的走法)';
}
hintText.textContent = `建议走法:${bestMove.piece.type} 从 ${fromPos} 走到 ${toPos}${reason}`;
hintText.style.color = currentPlayer === 'red' ? '#cc0000' : '#000';
// 高亮提示的棋子
clearSelection();
const pieceElement = document.querySelector(`.piece[data-row="${bestMove.from.row}"][data-col="${bestMove.from.col}"]`);
if (pieceElement) {
pieceElement.classList.add('hint');
}
}
// 深度评估移动(考虑多步)
function evaluateMoveDeep(fromRow, fromCol, toRow, toCol, depth) {
if (depth === 0) return 0;
const piece = board[fromRow][fromCol];
const targetPiece = board[toRow][toCol];
// 基础分数
let score = 0;
// 吃子的分数
const pieceValues = {
[PIECE_TYPES.BING]: 10,
[PIECE_TYPES.PAO]: 30,
[PIECE_TYPES.MA]: 30,
[PIECE_TYPES.XIANG]: 20,
[PIECE_TYPES.SHI]: 20,
[PIECE_TYPES.JU]: 50,
[PIECE_TYPES.SHUAI]: 1000
};
if (targetPiece) {
score += pieceValues[targetPiece.type] || 0;
}
// 保存当前状态
const originalBoard = board.map(row => row.map(cell => cell ? {...cell} : null));
const originalGameOver = gameOver;
// 执行移动
board[toRow][toCol] = piece;
board[fromRow][fromCol] = null;
// 检查是否会被吃(考虑对手的回应)
let canBeCaptured = false;
let captureValue = 0;
for (let row = 0; row < BOARD_HEIGHT; row++) {
for (let col = 0; col < BOARD_WIDTH; col++) {
const enemyPiece = board[row][col];
if (enemyPiece && enemyPiece.player !== currentPlayer) {
if (isValidMove(row, col, toRow, toCol)) {
canBeCaptured = true;
const value = pieceValues[piece.type] || 0;
captureValue = Math.max(captureValue, value);
}
}
}
}
if (canBeCaptured) {
score -= captureValue * 0.8; // 可能被吃的惩罚
}
// 递归评估后续步骤
if (depth > 1 && !gameOver) {
const nextPlayer = currentPlayer === 'red' ? 'black' : 'red';
const nextMoves = getAllPossibleMoves(nextPlayer);
if (nextMoves.length > 0) {
let worstResponse = Infinity;
for (const nextMove of nextMoves.slice(0, 10)) { // 限制搜索数量
const nextScore = evaluateMoveDeep(nextMove.from.row, nextMove.from.col,
nextMove.to.row, nextMove.to.col, depth - 1);
worstResponse = Math.min(worstResponse, nextScore);
}
score += worstResponse * 0.3; // 后续影响
}
}
// 恢复状态
board = originalBoard;
gameOver = originalGameOver;
// 位置优势
if (piece.player === 'red' && toRow < fromRow) score += 2; // 红方前进
if (piece.player === 'black' && toRow > fromRow) score += 2; // 黑方前进
// 控制中心区域
if (toRow >= 2 && toRow <= 7 && toCol >= 2 && toCol <= 6) {
score += 3;
}
// 保护将/帅
if (piece.type === PIECE_TYPES.SHUAI) {
const inPalace = piece.player === 'red' ?
(toRow >= 7 && toRow <= 9 && toCol >= 3 && toCol <= 5) :
(toRow >= 0 && toRow <= 2 && toCol >= 3 && toCol <= 5);
if (!inPalace) score -= 50;
}
return score;
}
// 开始游戏
function startGame(mode) {
gameMode = mode;
document.getElementById('modeSelection').style.display = 'none';
document.getElementById('gameMode').textContent = mode === 'ai' ? '(AI对战)' : '(双人对战)';
if (mode === 'ai' && currentPlayer === 'black') {
setTimeout(() => aiMove(), 1000);
}
}
// 更新状态显示
function updateStatus() {
const statusElement = document.getElementById('currentPlayer');
statusElement.textContent = currentPlayer === 'red' ? '红方' : '黑方';
statusElement.style.color = currentPlayer === 'red' ? '#cc0000' : '#000';
}
// 重置游戏
function resetGame() {
if (confirm('确定要重新开始游戏吗?')) {
gameOver = false;
currentPlayer = 'red';
selectedPiece = null;
moveHistory = [];
aiThinking = false;
document.getElementById('moveList').innerHTML = '';
document.getElementById('hintText').textContent = '';
initBoard();
renderBoard();
updateStatus();
if (gameMode === 'ai' && currentPlayer === 'black') {
setTimeout(() => aiMove(), 1000);
}
}
}
// 显示规则
function showRules() {
alert(`象棋规则:
1. 帅(将):只能在九宫格内移动,每次只能走一格
2. 士(仕):只能在九宫格内斜走一格
3. 象(相):走田字,不能过河,不能塞象眼
4. 马:走日字,不能蹩马腿
5. 车:直线行走,格数不限
6. 炮:直线行走,吃子时需要隔一个棋子
7. 兵(卒):过河前只能向前,过河后可以左右移动
操作说明:
- 点击棋子选中,再点击目标位置移动
- 点击"悔棋"按钮可以撤销上一步
- 点击"提示走法"可以获得AI建议(考虑3步)
- AI对战模式下,AI会自动走棋
吃掉对方的将/帅即可获胜!`);
}
// 窗口大小改变时重新渲染
window.addEventListener('resize', () => {
if (board.length > 0) {
renderBoard();
}
});
// 初始化游戏
window.onload = function() {
initBoard();
renderBoard();
updateStatus();
};
</script>
</body>
</html>
