从零到一:用纯前端技术栈打造响应式在线国际象棋对战平台
国际象棋作为一项历史悠久的智力运动,在数字时代焕发出新的活力。本文将带你从零开始,使用HTML5、CSS3和JavaScript构建一个完整的响应式在线国际象棋对战平台。不同于简单的单机版实现,我们将重点关注如何设计一个可扩展的工程化架构,实现真正的在线对战功能。
1. 项目架构设计与技术选型
构建一个完整的国际象棋平台需要考虑多个技术层面的整合。我们选择纯前端技术栈来实现核心功能,这样无需后端服务器也能运行基础版本,同时为后续扩展预留空间。
核心模块划分:
- 游戏逻辑引擎:处理棋局规则、胜负判定
- 用户界面:响应式棋盘、控制面板
- 状态管理:棋局状态、玩家信息
- 网络通信:简单的URL分享机制和WebSocket实时对战
// 基础项目结构 /project-root ├── index.html # 主页面 ├── css/ │ └── style.css # 样式表 ├── js/ │ ├── game.js # 游戏逻辑 │ ├── ui.js # 用户界面 │ └── network.js # 网络通信 └── assets/ # 图片等资源对于状态管理,我们采用Redux-like的轻量级实现,确保应用状态可预测:
class GameStore { constructor() { this.state = { board: Array(8).fill().map(() => Array(8).fill(null)), currentPlayer: 'white', gameStatus: 'playing' }; this.subscribers = []; } subscribe(callback) { this.subscribers.push(callback); } dispatch(action) { this.state = this.reducer(this.state, action); this.subscribers.forEach(cb => cb(this.state)); } reducer(state, action) { switch(action.type) { case 'MOVE_PIECE': // 处理移动逻辑 return newState; // 其他action处理 } } }2. 游戏逻辑引擎实现
国际象棋规则复杂,我们需要精心设计引擎架构。采用面向对象的方式,将棋盘、棋子和规则分离,提高代码可维护性。
棋子基类设计:
class ChessPiece { constructor(color, position) { this.color = color; // 'white' or 'black' this.position = position; // [row, col] this.hasMoved = false; } // 获取可能的移动位置(不考虑棋盘边界和其他棋子) getPossibleMoves() { throw new Error('必须由子类实现'); } // 获取实际可走的合法位置 getLegalMoves(board) { const possible = this.getPossibleMoves(); return possible.filter(([row, col]) => { // 检查是否在棋盘内 if (row < 0 || row > 7 || col < 0 || col > 7) return false; // 检查目标位置是否有己方棋子 const target = board[row][col]; if (target && target.color === this.color) return false; return true; }); } }具体棋子实现示例(车):
class Rook extends ChessPiece { getPossibleMoves() { const [row, col] = this.position; const moves = []; // 水平移动 for (let c = 0; c < 8; c++) { if (c !== col) moves.push([row, c]); } // 垂直移动 for (let r = 0; r < 8; r++) { if (r !== row) moves.push([r, col]); } return moves; } getLegalMoves(board) { const [row, col] = this.position; const moves = []; // 向右 for (let c = col + 1; c < 8; c++) { moves.push([row, c]); if (board[row][c]) break; // 遇到棋子停止 } // 向左 for (let c = col - 1; c >= 0; c--) { moves.push([row, c]); if (board[row][c]) break; } // 向上 for (let r = row - 1; r >= 0; r--) { moves.push([r, col]); if (board[r][col]) break; } // 向下 for (let r = row + 1; r < 8; r++) { moves.push([r, col]); if (board[r][col]) break; } return moves.filter(([r, c]) => { const target = board[r][c]; return !target || target.color !== this.color; }); } }特殊规则处理:
- 王车易位
- 吃过路兵
- 兵的升变
3. 响应式用户界面设计
现代国际象棋平台需要适配各种设备屏幕。我们使用CSS Grid和Flexbox实现响应式布局,确保从手机到桌面都有良好体验。
棋盘布局核心CSS:
.chess-board { display: grid; grid-template-columns: repeat(8, 1fr); grid-template-rows: repeat(8, 1fr); aspect-ratio: 1/1; max-width: 600px; margin: 0 auto; border: 2px solid #333; } .board-square { position: relative; display: flex; align-items: center; justify-content: center; } /* 棋盘格子颜色 */ .board-square.light { background-color: #f0d9b5; } .board-square.dark { background-color: #b58863; } /* 响应式调整 */ @media (max-width: 768px) { .chess-board { max-width: 90vw; } .piece { width: 80%; height: 80%; } }棋子拖放交互实现:
class DragHandler { constructor(boardElement, game) { this.board = boardElement; this.game = game; this.draggedPiece = null; this.sourceSquare = null; this.initDragEvents(); } initDragEvents() { // 拖拽开始 this.board.addEventListener('dragstart', e => { if (!e.target.classList.contains('piece')) return; this.draggedPiece = e.target; this.sourceSquare = e.target.parentElement; e.dataTransfer.effectAllowed = 'move'; // 设置拖拽图像 setTimeout(() => { e.target.classList.add('dragging'); }, 0); }); // 拖拽结束 this.board.addEventListener('dragend', e => { if (!this.draggedPiece) return; e.target.classList.remove('dragging'); this.draggedPiece = null; this.sourceSquare = null; }); // 拖拽经过 this.board.addEventListener('dragover', e => { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; }); // 放置 this.board.addEventListener('drop', e => { e.preventDefault(); if (!this.draggedPiece) return; const targetSquare = e.target.closest('.board-square'); if (!targetSquare || targetSquare === this.sourceSquare) return; // 获取位置信息 const from = this.getSquarePosition(this.sourceSquare); const to = this.getSquarePosition(targetSquare); // 尝试移动 if (this.game.isValidMove(from.row, from.col, to.row, to.col)) { this.game.makeMove(from.row, from.col, to.row, to.col); } }); } getSquarePosition(square) { return { row: parseInt(square.dataset.row), col: parseInt(square.dataset.col) }; } }4. 状态管理与游戏流程控制
良好的状态管理是复杂应用的核心。我们设计了一个专门的状态管理器来处理游戏流程和玩家交互。
游戏状态机设计:
class GameState { constructor() { this.state = { phase: 'waiting', // waiting, playing, gameover players: { white: null, black: null }, currentTurn: 'white', board: this.createInitialBoard(), moveHistory: [], capturedPieces: { white: [], black: [] } }; } createInitialBoard() { const board = Array(8).fill().map(() => Array(8).fill(null)); // 摆放黑方棋子 board[0] = [ new Rook('black', [0,0]), new Knight('black', [0,1]), new Bishop('black', [0,2]), new Queen('black', [0,3]), new King('black', [0,4]), new Bishop('black', [0,5]), new Knight('black', [0,6]), new Rook('black', [0,7]) ]; board[1] = Array(8).fill().map((_,i) => new Pawn('black', [1,i])); // 摆放白方棋子 board[7] = [ new Rook('white', [7,0]), new Knight('white', [7,1]), new Bishop('white', [7,2]), new Queen('white', [7,3]), new King('white', [7,4]), new Bishop('white', [7,5]), new Knight('white', [7,6]), new Rook('white', [7,7]) ]; board[6] = Array(8).fill().map((_,i) => new Pawn('white', [6,i])); return board; } makeMove(from, to) { const [fromRow, fromCol] = from; const [toRow, toCol] = to; const piece = this.state.board[fromRow][fromCol]; if (!piece || piece.color !== this.state.currentTurn) { return { success: false, message: '不是你的回合' }; } // 检查移动是否合法 if (!piece.getLegalMoves(this.state.board).some( ([r,c]) => r === toRow && c === toCol)) { return { success: false, message: '非法移动' }; } // 执行移动 const captured = this.state.board[toRow][toCol]; if (captured) { this.state.capturedPieces[this.state.currentTurn].push(captured); } this.state.board[toRow][toCol] = piece; this.state.board[fromRow][fromCol] = null; piece.position = [toRow, toCol]; piece.hasMoved = true; // 记录移动 this.state.moveHistory.push({ piece: piece.constructor.name, from, to, captured: captured ? captured.constructor.name : null, timestamp: Date.now() }); // 检查游戏状态 this.checkGameStatus(); // 切换回合 this.state.currentTurn = this.state.currentTurn === 'white' ? 'black' : 'white'; return { success: true }; } checkGameStatus() { // 实现胜负检查逻辑 } }5. 实现简单在线对战功能
即使没有后端服务器,我们也能通过URL分享实现基本的"在线"对战功能。核心思路是将棋局状态编码到URL中,玩家可以通过分享链接继续对局。
状态序列化与URL编码:
class GameSerializer { static serialize(gameState) { const data = { board: gameState.board.map(row => row.map(piece => piece ? { type: piece.constructor.name, color: piece.color, position: piece.position, hasMoved: piece.hasMoved } : null) ), currentTurn: gameState.currentTurn, moveHistory: gameState.moveHistory, capturedPieces: gameState.capturedPieces }; return btoa(JSON.stringify(data)); } static deserialize(encoded, gameState) { try { const data = JSON.parse(atob(encoded)); // 重建棋盘 gameState.board = data.board.map(row => row.map(pieceData => { if (!pieceData) return null; const pieceClass = { Pawn, Rook, Knight, Bishop, Queen, King }[pieceData.type]; return new pieceClass( pieceData.color, pieceData.position ); }) ); // 恢复其他状态 gameState.currentTurn = data.currentTurn; gameState.moveHistory = data.moveHistory; gameState.capturedPieces = data.capturedPieces; return true; } catch (e) { console.error('反序列化失败:', e); return false; } } } // URL状态管理 class URLStateManager { constructor(game) { this.game = game; window.addEventListener('popstate', this.handlePopState.bind(this)); } updateURL() { const serialized = GameSerializer.serialize(this.game.state); const url = new URL(window.location); url.searchParams.set('game', serialized); window.history.pushState({}, '', url); } handlePopState() { const params = new URLSearchParams(window.location.search); const gameState = params.get('game'); if (gameState) { GameSerializer.deserialize(gameState, this.game.state); } } getShareableLink() { const serialized = GameSerializer.serialize(this.game.state); const url = new URL(window.location); url.searchParams.set('game', serialized); return url.toString(); } }WebSocket实时对战扩展:
class ChessWebSocket { constructor(game, serverUrl) { this.game = game; this.socket = new WebSocket(serverUrl); this.setupEventHandlers(); } setupEventHandlers() { this.socket.onopen = () => { console.log('WebSocket连接已建立'); // 发送玩家信息 this.send({ type: 'join', color: this.game.state.currentTurn, gameId: this.getGameIdFromURL() }); }; this.socket.onmessage = (event) => { const message = JSON.parse(event.data); switch(message.type) { case 'move': this.handleRemoteMove(message); break; case 'game_state': this.updateGameState(message.state); break; case 'chat': this.displayChatMessage(message); break; } }; } sendMove(from, to) { this.send({ type: 'move', from, to, gameId: this.getGameIdFromURL() }); } handleRemoteMove(message) { // 验证移动合法性 if (this.validateMove(message)) { this.game.state.makeMove(message.from, message.to); } } send(message) { if (this.socket.readyState === WebSocket.OPEN) { this.socket.send(JSON.stringify(message)); } } }6. 性能优化与高级功能
随着项目复杂度增加,我们需要考虑性能优化和实现一些高级功能来提升用户体验。
性能优化技巧:
- 使用Web Workers处理复杂的AI计算
- 实现增量渲染,只更新变化的棋盘部分
- 使用requestAnimationFrame优化动画
// 使用Web Worker进行AI计算 class ChessAI { constructor(game) { this.worker = new Worker('ai-worker.js'); this.worker.onmessage = this.handleAIMove.bind(this); this.game = game; } requestMove(difficulty = 'medium') { const gameState = GameSerializer.serialize(this.game.state); this.worker.postMessage({ type: 'calculate_move', state: gameState, difficulty }); } handleAIMove(event) { const { from, to } = event.data; this.game.makeMove(from, to); } } // ai-worker.js中的代码 self.onmessage = function(e) { if (e.data.type === 'calculate_move') { // 反序列化游戏状态 const gameState = deserialize(e.data.state); // 根据难度选择搜索深度 const depth = { easy: 2, medium: 4, hard: 6 }[e.data.difficulty] || 3; // 使用minimax算法计算最佳移动 const bestMove = findBestMove(gameState, depth); // 返回结果 self.postMessage(bestMove); } }; function findBestMove(gameState, depth) { // 实现minimax算法 // ... return { from: [6,4], to: [4,4] }; // 示例移动 }实现游戏回放功能:
class GameReplay { constructor(game) { this.game = game; this.isReplaying = false; this.replaySpeed = 1.0; // 回放速度 this.replayInterval = null; } startReplay(moves) { if (this.isReplaying) return; this.isReplaying = true; const originalState = { ...this.game.state }; this.game.reset(); let moveIndex = 0; this.replayInterval = setInterval(() => { if (moveIndex >= moves.length) { this.stopReplay(); return; } const move = moves[moveIndex++]; this.game.makeMove(move.from, move.to); }, 1000 / this.replaySpeed); } stopReplay() { clearInterval(this.replayInterval); this.isReplaying = false; } exportPGN() { // 生成标准PGN格式棋谱 let pgn = '[Event "Online Chess Game"]\n'; pgn += `[Date "${new Date().toISOString().split('T')[0]}"]\n`; pgn += '[White "Player1"]\n'; pgn += '[Black "Player2"]\n\n'; this.game.state.moveHistory.forEach((move, i) => { if (i % 2 === 0) { pgn += `${Math.floor(i/2)+1}. `; } pgn += `${this.moveToNotation(move)} `; }); return pgn; } moveToNotation(move) { // 将移动转换为标准代数记法 // ... } }7. 测试与调试策略
确保国际象棋平台的正确性需要全面的测试策略,特别是对于复杂的游戏规则。
单元测试示例(使用Jest):
describe('Chess Game Logic', () => { let game; beforeEach(() => { game = new ChessGame(); }); test('初始棋盘设置正确', () => { // 检查棋盘行数 expect(game.board.length).toBe(8); // 检查每行列数 game.board.forEach(row => { expect(row.length).toBe(8); }); // 检查特定位置棋子 expect(game.board[0][0]?.constructor).toBe(Rook); expect(game.board[0][4]?.constructor).toBe(King); expect(game.board[7][3]?.constructor).toBe(Queen); }); test('兵的合法移动', () => { const whitePawn = game.board[6][0]; // 白兵 const blackPawn = game.board[1][0]; // 黑兵 // 白兵初始可前进1或2格 expect(whitePawn.getLegalMoves(game.board)).toEqual( expect.arrayContaining([[5,0], [4,0]]) ); // 黑兵初始可前进1或2格 expect(blackPawn.getLegalMoves(game.board)).toEqual( expect.arrayContaining([[2,0], [3,0]]) ); }); test('车的移动不能穿过其他棋子', () => { const rook = game.board[0][0]; // 初始位置的车 // 初始时车不能移动(被自己的兵挡住) expect(rook.getLegalMoves(game.board)).toEqual([]); // 移动兵后车可以水平移动 game.board[1][0] = null; const moves = rook.getLegalMoves(game.board); expect(moves).toEqual( expect.arrayContaining([[0,1], [0,2], [0,3], [0,4], [0,5], [0,6], [0,7]]) ); expect(moves).not.toContainEqual([1,0]); // 不能垂直移动(被自己的王挡住) }); });集成测试场景:
describe('游戏流程集成测试', () => { test('完整的游戏流程', () => { const game = new ChessGame(); // 白方e4 game.makeMove(6,4, 4,4); expect(game.board[4][4]?.constructor).toBe(Pawn); expect(game.currentPlayer).toBe('black'); // 黑方e5 game.makeMove(1,4, 3,4); expect(game.board[3][4]?.constructor).toBe(Pawn); expect(game.currentPlayer).toBe('white'); // 白方象c4 game.makeMove(7,5, 4,2); expect(game.board[4][2]?.constructor).toBe(Bishop); expect(game.currentPlayer).toBe('black'); // 黑方马f6 game.makeMove(0,6, 2,5); expect(game.board[2][5]?.constructor).toBe(Knight); expect(game.currentPlayer).toBe('white'); // 白方后f3 game.makeMove(7,3, 5,5); expect(game.board[5][5]?.constructor).toBe(Queen); expect(game.currentPlayer).toBe('black'); // 黑方象c5 game.makeMove(0,2, 3,5); expect(game.board[3][5]?.constructor).toBe(Bishop); expect(game.currentPlayer).toBe('white'); // 白方后f7将死 game.makeMove(5,5, 1,5); expect(game.board[1][5]?.constructor).toBe(Queen); expect(game.isCheckmate('black')).toBe(true); expect(game.gameOver).toBe(true); }); });调试工具实现:
class ChessDebugger { constructor(game) { this.game = game; this.setupDebugUI(); } setupDebugUI() { const debugPanel = document.createElement('div'); debugPanel.style.position = 'fixed'; debugPanel.style.bottom = '10px'; debugPanel.style.right = '10px'; debugPanel.style.background = 'rgba(0,0,0,0.7)'; debugPanel.style.color = 'white'; debugPanel.style.padding = '10px'; debugPanel.style.zIndex = '1000'; // 添加调试按钮 const logStateBtn = document.createElement('button'); logStateBtn.textContent = '打印游戏状态'; logStateBtn.onclick = () => console.log(this.game.state); const validMovesBtn = document.createElement('button'); validMovesBtn.textContent = '显示合法移动'; validMovesBtn.onclick = this.showValidMoves.bind(this); debugPanel.appendChild(logStateBtn); debugPanel.appendChild(document.createElement('br')); debugPanel.appendChild(validMovesBtn); document.body.appendChild(debugPanel); } showValidMoves() { const selectedPiece = this.game.selectedPiece; if (!selectedPiece) { alert('请先选择一个棋子'); return; } const validMoves = selectedPiece.getLegalMoves(this.game.board); console.log('合法移动:', validMoves); // 在棋盘上高亮显示 this.clearHighlights(); validMoves.forEach(([row, col]) => { const square = document.querySelector(`[data-row="${row}"][data-col="${col}"]`); if (square) { square.style.boxShadow = 'inset 0 0 10px yellow'; } }); } clearHighlights() { document.querySelectorAll('.board-square').forEach(square => { square.style.boxShadow = ''; }); } }8. 部署与持续集成
将国际象棋平台部署到生产环境需要考虑性能、安全性和可维护性。以下是部署流程的关键步骤:
部署准备:
- 代码优化:
- 使用Webpack或Vite打包前端资源
- 启用代码分割和懒加载
- 压缩CSS/JavaScript
// webpack.config.js module.exports = { entry: './src/index.js', output: { filename: 'bundle.[contenthash].js', path: path.resolve(__dirname, 'dist'), clean: true }, module: { rules: [ { test: /\.css$/i, use: ['style-loader', 'css-loader', 'postcss-loader'] }, { test: /\.(png|svg|jpg|jpeg|gif)$/i, type: 'asset/resource' } ] }, optimization: { splitChunks: { chunks: 'all' } } };- 静态资源托管:
- 使用CDN加速资源加载
- 配置适当的缓存策略
部署流程:
# 示例部署脚本 #!/bin/bash # 构建生产版本 npm run build # 同步到服务器 rsync -avz --delete dist/ user@server:/var/www/chess-platform/ # 重启服务 ssh user@server "systemctl restart nginx"持续集成配置(GitHub Actions):
name: CI/CD Pipeline on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install dependencies run: npm install - name: Run tests run: npm test deploy: needs: test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v2 - name: Install dependencies run: npm install - name: Build run: npm run build - name: Deploy to Production uses: appleboy/scp-action@master with: host: ${{ secrets.PRODUCTION_HOST }} username: ${{ secrets.PRODUCTION_USER }} key: ${{ secrets.SSH_KEY }} source: "dist/*" target: "/var/www/chess-platform/" - name: Restart Nginx uses: appleboy/ssh-action@master with: host: ${{ secrets.PRODUCTION_HOST }} username: ${{ secrets.PRODUCTION_USER }} key: ${{ secrets.SSH_KEY }} script: "sudo systemctl restart nginx"性能监控与错误跟踪:
// 前端监控初始化 import * as Sentry from '@sentry/browser'; import { Integrations } from '@sentry/tracing'; Sentry.init({ dsn: 'YOUR_DSN_HERE', integrations: [new Integrations.BrowserTracing()], tracesSampleRate: 0.2, }); // 捕获游戏错误 window.addEventListener('unhandledrejection', event => { Sentry.captureException(event.reason); console.error('未处理的Promise拒绝:', event.reason); }); window.addEventListener('error', event => { Sentry.captureException(event.error); console.error('未捕获的错误:', event.error); }); // 自定义性能监控 const gameStartTime = performance.now(); function logGamePerformance() { const loadTime = performance.now() - gameStartTime; Sentry.metrics.distribution('game.load_time', loadTime); // 发送其他自定义指标 Sentry.metrics.increment('game.moves.total'); }9. 扩展功能与未来方向
基础平台完成后,可以考虑添加更多高级功能来提升用户体验和竞技性。
AI对战集成:
class ChessAI { constructor(difficulty = 'medium') { this.difficulty = difficulty; this.worker = new Worker('ai-worker.js'); } calculateMove(gameState, callback) { return new Promise((resolve) => { this.worker.onmessage = (e) => { resolve(e.data); }; this.worker.postMessage({ type: 'calculate_move', state: gameState, difficulty: this.difficulty }); }); } setDifficulty(difficulty) { this.difficulty = difficulty; } } // 在游戏循环中使用AI async function playAgainstAI() { if (game.currentPlayer === 'ai') { const move = await ai.calculateMove(game.getState()); game.makeMove(move.from, move.to); } }比赛与天梯系统设计:
class RatingSystem { constructor() { this.initialRating = 1000; this.kFactor = 32; // 调整系数 } calculateNewRatings(playerRating, opponentRating, result) { // result: 1=赢, 0.5=平, 0=输 const expectedScore = 1 / (1 + Math.pow(10, (opponentRating - playerRating) / 400)); return Math.round(playerRating + this.kFactor * (result - expectedScore)); } } class Tournament { constructor(players) { this.players = players; this.rounds = []; this.currentRound = 0; } generatePairings() { // 根据当前积分生成配对 const sortedPlayers = [...this.players].sort((a, b) => b.rating - a.rating); const pairings = []; for (let i = 0; i < sortedPlayers.length / 2; i++) { pairings.push({ white: sortedPlayers[i], black: sortedPlayers[sortedPlayers.length - 1 - i], result: null }); } this.rounds.push(pairings); return pairings; } recordResult(pairingIndex, result) { const pairing = this.rounds[this.currentRound][pairingIndex]; pairing.result = result; // 更新积分 const ratingSystem = new RatingSystem(); const whiteNewRating = ratingSystem.calculateNewRatings( pairing.white.rating, pairing.black.rating, result === 'white' ? 1 : result === 'draw' ? 0.5 : 0 ); const blackNewRating = ratingSystem.calculateNewRatings( pairing.black.rating, pairing.white.rating, result === 'black' ? 1 : result === 'draw' ? 0.5 : 0 ); pairing.white.rating = whiteNewRating; pairing.black.rating = blackNewRating; } nextRound() { this.currentRound++; return this.generatePairings(); } }社区功能实现:
class ChessCommunity { constructor() { this.players = new Map(); this.games = new Map(); this.chatRooms = new Map(); } addPlayer(player) { this.players.set(player.id, player); } createGame(player1, player2, settings) { const gameId = generateId(); const game = new OnlineGame(player1, player2, settings); this.games.set(gameId, game); return gameId; } joinChatRoom(player, roomId) { if (!this.chatRooms.has(roomId)) { this.chatRooms.set(roomId, new Set()); } this.chatRooms.get(roomId).add(player); } sendChatMessage(player, roomId, message) { if (this.chatRooms.has(roomId) && this.chatRooms.get(roomId).has(player)) { // 广播消息给房间内所有玩家 this.chatRooms.get(roomId).forEach(member => { if (member !== player) { member.receiveChatMessage({ from: player.name, text: message, timestamp: Date.now() }); } }); } } } class PlayerProfile { constructor(userId, name) { this.userId = userId; this.name = name; this.rating = 1000; this.gamesPlayed = 0; this.gamesWon = 0; this.joinDate = new Date(); this.friends = new Set(); } addFriend(player) { this.friends.add(player.userId); player.friends.add(this.userId); } recordGameResult(result) { this.gamesPlayed++; if (result === 'win') this.gamesWon++; } getWinRate() { return this.gamesPlayed > 0 ? (this.gamesWon / this.gamesPlayed * 100).toFixed(1) : 0; } }10. 安全性与防作弊措施
在线游戏平台必须考虑安全性和公平性,特别是在竞技性游戏中。
客户端安全措施:
class SecurityManager { constructor(game) { this.game = game; this.validMovesCache = new Map(); this.lastMoveTime = 0; this.moveRateLimit = 500; // 毫秒 } validateMove(from, to) { // 检查移动是否过快(防自动化脚本) const now = Date.now(); if (now - this.lastMoveTime < this.moveRateLimit) { console.warn('移动速度过快'); return false; } this.lastMoveTime = now; // 检查移动是否合法 const piece = this.game.board[from.row][from.col]; if (!piece || piece.color !== this.game.currentPlayer) { console.warn('非法移动:不是当前玩家的棋子'); return false; } // 使用缓存提高性能 const cacheKey = `${from.row},${from.col}`; if (!this.validMovesCache.has(cacheKey)) { this.validMovesCache.set(cacheKey, piece.getLegalMoves(this.game.board)); } const isValid = this.validMovesCache.get(cacheKey).some( ([r, c]) => r === to.row && c === to.col ); if (!isValid) { console.warn('非法移动:不符合棋子规则'); return false; } return true; } clearCache() { this.validMovesCache.clear(); } }通信安全增强:
class SecureWebSocket extends ChessWebSocket { constructor(game, serverUrl) { super(game