功能,摸鱼小游戏,
可以集成到个人网页或APP


html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>双子星棋</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
body {
font-family: 'PingFang SC', 'Microsoft YaHei', -apple-system, sans-serif;
background: linear-gradient(180deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
/* 确保占据整个高度,不溢出 */
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
align-items: center;
padding: 8px 5px 5px 5px;
overflow: hidden; /* 禁止滚动 */
}
/* 顶部统计栏 */
.stats-bar {
width: 96%;
max-width: 380px;
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
padding: 5px 12px;
margin-bottom: 5px;
display: flex;
justify-content: space-between;
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.8);
font-weight: 500;
}
.stat-item span {
font-weight: bold;
margin-left: 2px;
}
.game-title {
color: #ffd700;
font-size: 1.5rem;
margin-bottom: 2px;
font-weight: 800;
letter-spacing: 1px;
text-shadow: 0 0 15px rgba(255, 215, 0, 0.4);
}
.game-info {
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border-radius: 12px;
padding: 6px 15px;
margin-bottom: 6px;
color: white;
text-align: center;
width: 96%;
max-width: 380px;
border: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
}
.turn-indicator {
font-size: 0.9rem;
font-weight: 600;
}
.current-turn {
display: inline-block;
padding: 3px 10px;
border-radius: 12px;
transition: all 0.3s ease;
}
.current-turn.player {
background: linear-gradient(135deg, #667eea, #764ba2);
box-shadow: 0 0 10px rgba(102, 126, 234, 0.4);
}
.current-turn.ai {
background: linear-gradient(135deg, #f093fb, #f5576c);
box-shadow: 0 0 10px rgba(245, 87, 108, 0.4);
}
.live-score {
font-size: 1.1rem;
font-weight: bold;
display: flex;
gap: 10px;
}
.score-val { font-family: monospace; }
.board-container {
position: relative;
/* 动态计算宽度,确保不溢出 */
width: 94vw;
max-width: 350px;
aspect-ratio: 1;
margin-bottom: 6px;
flex-shrink: 0; /* 防止被压缩 */
}
.board-glow {
position: absolute;
inset: -4px;
background: linear-gradient(135deg, #667eea, #764ba2, #f093fb, #f5576c);
border-radius: 18px;
filter: blur(10px);
opacity: 0.3;
z-index: 0;
}
.board {
position: relative;
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-template-rows: repeat(6, 1fr);
gap: 2px;
width: 100%;
height: 100%;
background: linear-gradient(145deg, #1a1a2e, #0d0d1a);
border-radius: 14px;
padding: 6px;
z-index: 1;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
}
.cell {
background: linear-gradient(145deg, #2a2a4a, #1a1a3a);
border-radius: 6px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: all 0.2s ease;
position: relative;
}
.cell:nth-child(odd) {
background: linear-gradient(145deg, #252545, #151535);
}
.cell:active {
transform: scale(0.95);
}
.cell.selected {
box-shadow: inset 0 0 15px rgba(102, 126, 234, 0.8);
border: 2px solid #667eea;
}
.cell.valid-move {
box-shadow: inset 0 0 12px rgba(46, 213, 115, 0.5);
animation: pulse-green 1.2s infinite;
}
.cell.valid-move::after {
content: '';
position: absolute;
width: 25%;
height: 25%;
background: rgba(46, 213, 115, 0.6);
border-radius: 50%;
animation: fadeInOut 1s infinite;
}
@keyframes pulse-green {
0%, 100% { box-shadow: inset 0 0 12px rgba(46, 213, 115, 0.5); }
50% { box-shadow: inset 0 0 18px rgba(46, 213, 115, 0.8); }
}
@keyframes fadeInOut {
0%, 100% { opacity: 0.3; transform: scale(1); }
50% { opacity: 0.8; transform: scale(1.2); }
}
.piece {
width: 80%;
height: 80%;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.2rem; /* 稍微调小一点棋子字体 */
transition: all 0.3s ease;
position: relative;
-webkit-user-select: none;
user-select: none;
}
.piece.player {
background: linear-gradient(145deg, #667eea, #5a67d8);
box-shadow: 0 3px 10px rgba(102, 126, 234, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.piece.ai {
background: linear-gradient(145deg, #f5576c, #e63946);
box-shadow: 0 3px 10px rgba(245, 87, 108, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.piece.twinstar {
font-size: 1.5rem;
animation: twinkle 2s infinite alternate;
}
.piece.player.twinstar {
box-shadow: 0 0 15px rgba(102, 126, 234, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
.piece.ai.twinstar {
box-shadow: 0 0 15px rgba(245, 87, 108, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.3);
}
@keyframes twinkle {
0% { filter: brightness(1); }
100% { filter: brightness(1.3); }
}
.piece.guard { font-size: 1.3rem; }
.piece.soldier { font-size: 1rem; }
.piece.animate-move {
animation: movePiece 0.3s ease-out;
}
@keyframes movePiece {
0% { transform: scale(1.3); opacity: 0.7; }
100% { transform: scale(1); opacity: 1; }
}
.goal-marker {
position: absolute;
width: 10px;
height: 10px;
border-radius: 50%;
border: 2px dashed;
opacity: 0.4;
}
.goal-marker.player-goal { border-color: #667eea; bottom: 10px; }
.goal-marker.ai-goal { border-color: #f5576c; top: 10px; }
/* 底部控制区紧凑排列 */
.controls-area {
display: flex;
flex-direction: column;
gap: 6px;
width: 100%;
align-items: center;
margin-top: auto; /* 推到底部 */
padding-bottom: 5px;
}
.difficulty-selector {
display: flex;
gap: 6px;
justify-content: center;
width: 100%;
}
.difficulty-btn {
padding: 4px 10px;
font-size: 0.75rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
transition: all 0.2s ease;
}
.difficulty-btn.active {
background: linear-gradient(135deg, #ffd700, #ffaa00);
color: #0f0c29;
border-color: transparent;
font-weight: bold;
box-shadow: 0 0 10px rgba(255, 215, 0, 0.3);
}
.main-controls {
display: flex;
gap: 10px;
}
.btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 8px 18px;
border-radius: 20px;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.btn:active { transform: scale(0.95); }
.btn.rules-btn {
background: linear-gradient(135deg, #f093fb, #f5576c);
box-shadow: 0 4px 15px rgba(245, 87, 108, 0.3);
}
/* 图例简化,缩小字体 */
.piece-legend {
font-size: 0.7rem;
color: rgba(255, 255, 255, 0.5);
margin-bottom: 2px;
display: flex;
gap: 10px;
}
/* 弹窗保持原样 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
justify-content: center;
align-items: center;
z-index: 1000;
padding: 20px;
}
.modal-content {
background: linear-gradient(145deg, #1a1a2e, #0d0d1a);
border-radius: 20px;
padding: 25px;
text-align: center;
color: white;
max-width: 340px;
width: 100%;
animation: modalIn 0.3s ease;
border: 1px solid rgba(255, 255, 255, 0.1);
}
@keyframes modalIn {
from { opacity: 0; transform: scale(0.9); }
to { opacity: 1; transform: scale(1); }
}
.modal h2 { font-size: 1.4rem; margin-bottom: 12px; }
.modal p { margin-bottom: 15px; font-size: 0.95rem; opacity: 0.9; line-height: 1.5; }
.rules-content {
text-align: left;
max-height: 55vh;
overflow-y: auto;
padding-right: 5px;
}
.rules-content h3 {
color: #ffd700;
margin: 12px 0 6px 0;
font-size: 0.95rem;
}
.rules-content ul { padding-left: 15px; }
.rules-content li { font-size: 0.85rem; margin-bottom: 4px; line-height: 1.4; }
.close-btn {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
padding: 10px 25px;
border-radius: 20px;
margin-top: 10px;
font-weight: 600;
font-size: 0.9rem;
}
.thinking {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 15px 25px;
border-radius: 12px;
z-index: 500;
display: none;
align-items: center;
gap: 10px;
font-size: 0.9rem;
}
.thinking-spinner {
width: 20px;
height: 20px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-top-color: #ffd700;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
</style>
</head>
<body>
<!-- 顶部数据统计栏 -->
<div class="stats-bar">
<div class="stat-item">📊 <span id="totalGames">0</span></div>
<div class="stat-item" style="color: #7bed9f;">✅ 胜 <span id="totalWins">0</span></div>
<div class="stat-item" style="color: #ff6b6b;">❌ 负 <span id="totalLosses">0</span></div>
</div>
<h1 class="game-title">⭐ 双子星棋</h1>
<!-- 当局状态栏 -->
<div class="game-info">
<div class="turn-indicator">
<span class="current-turn player" id="currentTurn">👤 玩家</span>
</div>
<div class="live-score">
<span style="color: #7ca5f8;" class="score-val" id="playerScore">0</span>
<span>:</span>
<span style="color: #ff6b81;" class="score-val" id="aiScore">0</span>
</div>
</div>
<div class="board-container">
<div class="board-glow"></div>
<div class="board" id="board">
<div class="goal-marker player-goal" style="left: 12px;"></div>
<div class="goal-marker player-goal" style="right: 12px;"></div>
<div class="goal-marker ai-goal" style="left: 12px;"></div>
<div class="goal-marker ai-goal" style="right: 12px;"></div>
</div>
</div>
<div class="piece-legend">
<span>⭐双子星</span>
<span>🛡️护卫</span>
<span>⚔️士兵</span>
</div>
<div class="controls-area">
<div class="difficulty-selector">
<button class="difficulty-btn active" data-level="1">🌱 新手</button>
<button class="difficulty-btn" data-level="2">⚡ 进阶</button>
<button class="difficulty-btn" data-level="3">🔥 高手</button>
<button class="difficulty-btn" data-level="4">💎 大师</button>
</div>
<div class="main-controls">
<button class="btn" onclick="resetGame()">🔄 重开</button>
<button class="btn rules-btn" onclick="showRules()">📖 规则</button>
</div>
</div>
<div class="thinking" id="thinking">
<div class="thinking-spinner"></div>
<span>AI思考中...</span>
</div>
<!-- 规则弹窗 -->
<div class="modal" id="rulesModal">
<div class="modal-content">
<h2>📖 游戏规则</h2>
<div class="rules-content">
<h3>🎯 胜利条件</h3>
<ul>
<li>吃掉对方双子星(⭐)</li>
<li>双子星到达对角目标</li>
</ul>
<h3>♟️ 棋子移动</h3>
<ul>
<li><strong>⭐双子星</strong>:上下左右1格,或跳过相邻棋子</li>
<li><strong>🛡️护卫</strong>:八方移动1格</li>
<li><strong>⚔️士兵</strong>:上下左右1格</li>
</ul>
</div>
<button class="close-btn" onclick="hideRules()">关闭</button>
</div>
</div>
<!-- 结束弹窗 -->
<div class="modal" id="gameOverModal">
<div class="modal-content">
<h2 id="gameOverTitle">🎉 游戏结束!</h2>
<p id="gameOverMessage"></p>
<button class="btn" onclick="resetGame()">🔄 再来一局</button>
</div>
</div>
<script>
// 游戏常量与逻辑基本不变,主要调整UI交互
const BOARD_SIZE = 6;
const PIECES = {
EMPTY: null,
PLAYER_TWINSTAR: 'PT',
PLAYER_GUARD: 'PG',
PLAYER_SOLDIER: 'PS',
AI_TWINSTAR: 'AT',
AI_GUARD: 'AG',
AI_SOLDIER: 'AS'
};
const PIECE_INFO = {
[PIECES.PLAYER_TWINSTAR]: { symbol: '⭐', type: 'twinstar', owner: 'player' },
[PIECES.PLAYER_GUARD]: { symbol: '🛡️', type: 'guard', owner: 'player' },
[PIECES.PLAYER_SOLDIER]: { symbol: '⚔️', type: 'soldier', owner: 'player' },
[PIECES.AI_TWINSTAR]: { symbol: '⭐', type: 'twinstar', owner: 'ai' },
[PIECES.AI_GUARD]: { symbol: '🛡️', type: 'guard', owner: 'ai' },
[PIECES.AI_SOLDIER]: { symbol: '⚔️', type: 'soldier', owner: 'ai' }
};
const PIECE_VALUES = {
[PIECES.PLAYER_TWINSTAR]: 1000,
[PIECES.PLAYER_GUARD]: 35,
[PIECES.PLAYER_SOLDIER]: 15,
[PIECES.AI_TWINSTAR]: 1000,
[PIECES.AI_GUARD]: 35,
[PIECES.AI_SOLDIER]: 15
};
const PLAYER_GOALS = [[5, 0], [5, 5]];
const AI_GOALS = [[0, 0], [0, 5]];
let board = [];
let currentPlayer = 'player';
let selectedPiece = null;
let validMoves = [];
let gameOver = false;
let difficulty = 1;
// 持久化数据
let stats = {
total: 0,
wins: 0,
losses: 0
};
// 加载本地存储
function loadStats() {
const saved = localStorage.getItem('twinStarStats');
if (saved) {
try {
stats = JSON.parse(saved);
} catch (e) {
console.error("存档损坏");
}
}
updateStatsUI();
}
function saveStats() {
localStorage.setItem('twinStarStats', JSON.stringify(stats));
}
function updateStatsUI() {
document.getElementById('totalGames').textContent = stats.total;
document.getElementById('totalWins').textContent = stats.wins;
document.getElementById('totalLosses').textContent = stats.losses;
}
function initBoard() {
board = Array(BOARD_SIZE).fill(null).map(() => Array(BOARD_SIZE).fill(PIECES.EMPTY));
board[0][2] = PIECES.AI_TWINSTAR;
board[0][3] = PIECES.AI_TWINSTAR;
board[1][1] = PIECES.AI_GUARD;
board[1][4] = PIECES.AI_GUARD;
board[2][0] = PIECES.AI_SOLDIER;
board[2][5] = PIECES.AI_SOLDIER;
board[5][2] = PIECES.PLAYER_TWINSTAR;
board[5][3] = PIECES.PLAYER_TWINSTAR;
board[4][1] = PIECES.PLAYER_GUARD;
board[4][4] = PIECES.PLAYER_GUARD;
board[3][0] = PIECES.PLAYER_SOLDIER;
board[3][5] = PIECES.PLAYER_SOLDIER;
}
function renderBoard() {
const boardEl = document.getElementById('board');
const markers = boardEl.querySelectorAll('.goal-marker');
boardEl.innerHTML = '';
markers.forEach(m => boardEl.appendChild(m));
for (let row = 0; row < BOARD_SIZE; row++) {
for (let col = 0; col < BOARD_SIZE; col++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = row;
cell.dataset.col = col;
if (selectedPiece && selectedPiece.row === row && selectedPiece.col === col) {
cell.classList.add('selected');
}
const isValidMove = validMoves.some(move => move.row === row && move.col === col);
if (isValidMove) {
cell.classList.add('valid-move');
}
const piece = board[row][col];
if (piece) {
const pieceEl = document.createElement('div');
const info = PIECE_INFO[piece];
pieceEl.className = `piece ${info.owner} ${info.type}`;
pieceEl.textContent = info.symbol;
cell.appendChild(pieceEl);
}
cell.addEventListener('click', () => handleCellClick(row, col));
cell.addEventListener('touchend', (e) => {
e.preventDefault();
handleCellClick(row, col);
}, { passive: false });
boardEl.appendChild(cell);
}
}
}
function handleCellClick(row, col) {
if (gameOver || currentPlayer !== 'player') return;
const clickedPiece = board[row][col];
const isPlayerPiece = clickedPiece && PIECE_INFO[clickedPiece].owner === 'player';
if (isPlayerPiece) {
selectedPiece = { row, col };
validMoves = getValidMoves(board, row, col);
if (navigator.vibrate) navigator.vibrate(5);
renderBoard();
return;
}
if (selectedPiece) {
const isValidMove = validMoves.some(move => move.row === row && move.col === col);
if (isValidMove) {
makeMove(board, selectedPiece.row, selectedPiece.col, row, col);
selectedPiece = null;
validMoves = [];
if (navigator.vibrate) navigator.vibrate(10);
renderBoard();
if (checkGameOver(board)) return;
currentPlayer = 'ai';
updateTurnIndicator();
setTimeout(() => aiMove(), 300);
} else {
selectedPiece = null;
validMoves = [];
renderBoard();
}
}
}
function getValidMoves(currentBoard, row, col) {
const piece = currentBoard[row][col];
if (!piece) return [];
const info = PIECE_INFO[piece];
const moves = [];
const basicDirections = [[-1, 0], [1, 0], [0, -1], [0, 1]];
if (info.type === 'guard') {
basicDirections.push([-1, -1], [-1, 1], [1, -1], [1, 1]);
}
for (const [dr, dc] of basicDirections) {
const newRow = row + dr;
const newCol = col + dc;
if (isValidPosition(newRow, newCol)) {
const targetPiece = currentBoard[newRow][newCol];
if (!targetPiece || PIECE_INFO[targetPiece].owner !== info.owner) {
moves.push({ row: newRow, col: newCol });
}
}
}
if (info.type === 'twinstar') {
for (const [dr, dc] of [[-1, 0], [1, 0], [0, -1], [0, 1]]) {
const midRow = row + dr;
const midCol = col + dc;
const jumpRow = row + dr * 2;
const jumpCol = col + dc * 2;
if (isValidPosition(jumpRow, jumpCol) && currentBoard[midRow][midCol] !== PIECES.EMPTY) {
const targetPiece = currentBoard[jumpRow][jumpCol];
if (!targetPiece || PIECE_INFO[targetPiece].owner !== info.owner) {
moves.push({ row: jumpRow, col: jumpCol });
}
}
}
}
return moves;
}
function isValidPosition(row, col) {
return row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE;
}
function makeMove(currentBoard, fromRow, fromCol, toRow, toCol) {
currentBoard[toRow][toCol] = currentBoard[fromRow][fromCol];
currentBoard[fromRow][fromCol] = PIECES.EMPTY;
}
function cloneBoard(currentBoard) {
return currentBoard.map(row => [...row]);
}
function getAllMoves(currentBoard, player) {
const moves = [];
for (let row = 0; row < BOARD_SIZE; row++) {
for (let col = 0; col < BOARD_SIZE; col++) {
const piece = currentBoard[row][col];
if (piece && PIECE_INFO[piece].owner === player) {
const pieceMoves = getValidMoves(currentBoard, row, col);
for (const move of pieceMoves) {
moves.push({
from: { row, col },
to: { row: move.row, col: move.col },
pieceType: PIECE_INFO[piece].type
});
}
}
}
}
return moves;
}
function evaluateBoard(currentBoard) {
let score = 0;
for (let row = 0; row < BOARD_SIZE; row++) {
for (let col = 0; col < BOARD_SIZE; col++) {
const piece = currentBoard[row][col];
if (piece) {
const info = PIECE_INFO[piece];
let pieceValue = PIECE_VALUES[piece];
if (info.type === 'twinstar') {
if (info.owner === 'ai') {
pieceValue += row * 15;
} else {
pieceValue += (BOARD_SIZE - 1 - row) * 15;
}
}
const centerDist = Math.abs(row - 2.5) + Math.abs(col - 2.5);
const posValue = (7 - centerDist) * 3;
if (info.owner === 'ai') {
score += pieceValue + posValue;
} else {
score -= pieceValue + posValue;
}
}
}
}
return score;
}
function checkGameOver(currentBoard) {
let playerTwinstarExists = false;
let aiTwinstarExists = false;
for (let row = 0; row < BOARD_SIZE; row++) {
for (let col = 0; col < BOARD_SIZE; col++) {
const piece = currentBoard[row][col];
if (piece === PIECES.PLAYER_TWINSTAR) {
playerTwinstarExists = true;
if (PLAYER_GOALS.some(g => g[0] === row && g[1] === col)) {
endGame('player', '双子星到达目标!');
return true;
}
}
if (piece === PIECES.AI_TWINSTAR) {
aiTwinstarExists = true;
if (AI_GOALS.some(g => g[0] === row && g[1] === col)) {
endGame('ai', 'AI双子星到达目标!');
return true;
}
}
}
}
if (!playerTwinstarExists) {
endGame('ai', '双子星被消灭!');
return true;
}
if (!aiTwinstarExists) {
endGame('player', '消灭了AI双子星!');
return true;
}
return false;
}
function checkGameOverNoEnd(currentBoard) {
let playerTwinstarExists = false;
let aiTwinstarExists = false;
for (let row = 0; row < BOARD_SIZE; row++) {
for (let col = 0; col < BOARD_SIZE; col++) {
const piece = currentBoard[row][col];
if (piece === PIECES.PLAYER_TWINSTAR) playerTwinstarExists = true;
if (piece === PIECES.AI_TWINSTAR) aiTwinstarExists = true;
}
}
return !playerTwinstarExists || !aiTwinstarExists;
}
function minimax(currentBoard, depth, alpha, beta, maximizingPlayer) {
if (depth === 0 || checkGameOverNoEnd(currentBoard)) {
return evaluateBoard(currentBoard);
}
const player = maximizingPlayer ? 'ai' : 'player';
const moves = getAllMoves(currentBoard, player);
if (moves.length === 0) return evaluateBoard(currentBoard);
moves.sort((a, b) => {
const aCapture = currentBoard[a.to.row][a.to.col] !== PIECES.EMPTY;
const bCapture = currentBoard[b.to.row][b.to.col] !== PIECES.EMPTY;
return (bCapture ? 1 : 0) - (aCapture ? 1 : 0);
});
if (maximizingPlayer) {
let maxEval = -Infinity;
for (const move of moves) {
const newBoard = cloneBoard(currentBoard);
makeMove(newBoard, move.from.row, move.from.col, move.to.row, move.to.col);
const evalScore = minimax(newBoard, depth - 1, alpha, beta, false);
maxEval = Math.max(maxEval, evalScore);
alpha = Math.max(alpha, evalScore);
if (beta <= alpha) break;
}
return maxEval;
} else {
let minEval = Infinity;
for (const move of moves) {
const newBoard = cloneBoard(currentBoard);
makeMove(newBoard, move.from.row, move.from.col, move.to.row, move.to.col);
const evalScore = minimax(newBoard, depth - 1, alpha, beta, true);
minEval = Math.min(minEval, evalScore);
beta = Math.min(beta, evalScore);
if (beta <= alpha) break;
}
return minEval;
}
}
function aiMove() {
if (gameOver) return;
document.getElementById('thinking').style.display = 'flex';
setTimeout(() => {
const moves = getAllMoves(board, 'ai');
if (moves.length === 0) {
document.getElementById('thinking').style.display = 'none';
return;
}
let bestMove = null;
let bestScore = -Infinity;
const searchDepth = Math.min(difficulty + 2, 5);
const moveScores = [];
for (const move of moves) {
const newBoard = cloneBoard(board);
makeMove(newBoard, move.from.row, move.from.col, move.to.row, move.to.col);
const score = minimax(newBoard, searchDepth - 1, -Infinity, Infinity, false);
moveScores.push({ move, score });
}
const maxScore = Math.max(...moveScores.map(m => m.score));
const bestMoves = moveScores.filter(m => m.score === maxScore);
const selected = bestMoves[Math.floor(Math.random() * bestMoves.length)];
bestMove = selected.move;
if (bestMove) {
makeMove(board, bestMove.from.row, bestMove.from.col,
bestMove.to.row, bestMove.to.col);
renderBoard();
if (navigator.vibrate) navigator.vibrate(10);
if (checkGameOver(board)) {
document.getElementById('thinking').style.display = 'none';
return;
}
currentPlayer = 'player';
updateTurnIndicator();
}
document.getElementById('thinking').style.display = 'none';
}, 50);
}
function updateTurnIndicator() {
const turnEl = document.getElementById('currentTurn');
if (currentPlayer === 'player') {
turnEl.textContent = '👤 玩家';
turnEl.className = 'current-turn player';
} else {
turnEl.textContent = '🤖 AI';
turnEl.className = 'current-turn ai';
}
}
// 更新顶部统计和当前比分
function updateScore(playerWin) {
const pScoreEl = document.getElementById('playerScore');
const aScoreEl = document.getElementById('aiScore');
let p = parseInt(pScoreEl.innerText);
let a = parseInt(aScoreEl.innerText);
if (playerWin) {
p++;
stats.wins++;
} else {
a++;
stats.losses++;
}
stats.total++;
saveStats();
updateStatsUI();
pScoreEl.innerText = p;
aScoreEl.innerText = a;
}
function endGame(winner, message) {
gameOver = true;
if (navigator.vibrate) navigator.vibrate([30, 30, 30]);
updateScore(winner === 'player');
const titleEl = document.getElementById('gameOverTitle');
const msgEl = document.getElementById('gameOverMessage');
if (winner === 'player') {
titleEl.textContent = '🎉 胜利!';
titleEl.style.color = '#ffd700';
} else {
titleEl.textContent = '😢 失败';
titleEl.style.color = '#ff6b6b';
}
msgEl.textContent = message;
document.getElementById('gameOverModal').style.display = 'flex';
}
function resetGame() {
gameOver = false;
currentPlayer = 'player';
selectedPiece = null;
validMoves = [];
document.getElementById('playerScore').innerText = '0';
document.getElementById('aiScore').innerText = '0';
initBoard();
renderBoard();
updateTurnIndicator();
document.getElementById('gameOverModal').style.display = 'none';
}
function showRules() {
document.getElementById('rulesModal').style.display = 'flex';
}
function hideRules() {
document.getElementById('rulesModal').style.display = 'none';
}
document.querySelectorAll('.difficulty-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.difficulty-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
difficulty = parseInt(btn.dataset.level);
resetGame();
});
});
document.querySelectorAll('.modal').forEach(modal => {
modal.addEventListener('click', (e) => {
if (e.target === modal) modal.style.display = 'none';
});
});
// 启动加载
loadStats();
initBoard();
renderBoard();
</script>
</body>
</html>