手把手教你用 HTML+CSS+JS 写一个能玩的简易象棋!(附完整源码)

手把手教你用 HTML+CSS+JS 写一个能玩的简易象棋!(附完整源码)
最新回答
寄纸筏

2020-05-19 09:13:47

下面我将带你一步步实现一个简易中国象棋游戏,包含完整的HTML、CSS和JavaScript代码,并详细解释关键逻辑。

一、项目结构index.htmlstyle.cssscript.js二、完整实现代码1. HTML 结构 (index.html)<!DOCTYPE html><html lang="zh-CN"><head> <meta charset="UTF-8"> <title>简易象棋</title> <link rel="stylesheet" href="style.css"></head><body> <h1>简易象棋</h1> <div id="controls"> <button onclick="restartGame()">重新开始</button> <button onclick="undoMove()">悔棋</button> </div> <div id="chessboard"></div> <script src="script.js"></script></body></html>2. CSS 样式 (style.css)body { font-family: Arial, sans-serif; text-align: center; background: #f5f5dc;}h1 { margin-top: 20px;}#controls { margin-bottom: 10px;}#chessboard { width: 450px; height: 500px; margin: 0 auto; display: grid; grid-template-columns: repeat(9, 1fr); grid-template-rows: repeat(10, 1fr); border: 2px solid #333; background: #ffe4c4; position: relative;}.cell { border: 1px solid #333; position: relative;}.piece { width: 90%; height: 90%; margin: 5%; border-radius: 50%; background: #d9b382; display: flex; justify-content: center; align-items: center; font-size: 18px; cursor: pointer; user-select: none;}.red { color: red;}.black { color: black;}3. JavaScript 核心逻辑 (script.js)const board = document.getElementById('chessboard');let selectedPiece = null;let selectedCell = null;let moveHistory = [];let currentTurn = 'red'; // 红方先行const initialPieces = { "0,0": { text: "车", color: "black" }, "1,0": { text: "马", color: "black" }, "2,0": { text: "象", color: "black" }, "3,0": { text: "士", color: "black" }, "4,0": { text: "将", color: "black" }, "5,0": { text: "士", color: "black" }, "6,0": { text: "象", color: "black" }, "7,0": { text: "马", color: "black" }, "8,0": { text: "车", color: "black" }, "1,2": { text: "炮", color: "black" }, "7,2": { text: "炮", color: "black" }, "0,3": { text: "兵", color: "black" }, "2,3": { text: "兵", color: "black" }, "4,3": { text: "兵", color: "black" }, "6,3": { text: "兵", color: "black" }, "8,3": { text: "兵", color: "black" }, "0,9": { text: "车", color: "red" }, "1,9": { text: "马", color: "red" }, "2,9": { text: "相", color: "red" }, "3,9": { text: "仕", color: "red" }, "4,9": { text: "帅", color: "red" }, "5,9": { text: "仕", color: "red" }, "6,9": { text: "相", color: "red" }, "7,9": { text: "马", color: "red" }, "8,9": { text: "车", color: "red" }, "1,7": { text: "炮", color: "red" }, "7,7": { text: "炮", color: "red" }, "0,6": { text: "兵", color: "red" }, "2,6": { text: "兵", color: "red" }, "4,6": { text: "兵", color: "red" }, "6,6": { text: "兵", color: "red" }, "8,6": { text: "兵", color: "red" },};function createBoard() { board.innerHTML = ''; for (let y = 0; y < 10; y++) { for (let x = 0; x < 9; x++) { const cell = document.createElement('div'); cell.className = 'cell'; cell.dataset.x = x; cell.dataset.y = y; const key = `${x},${y}`; if (initialPieces[key]) { const piece = document.createElement('div'); piece.className = `piece ${initialPieces[key].color}`; piece.innerText = initialPieces[key].text; cell.appendChild(piece); } cell.addEventListener('click', onCellClick); board.appendChild(cell); } }}function onCellClick(e) { const cell = e.currentTarget; const piece = cell.querySelector('.piece'); const x = parseInt(cell.dataset.x); const y = parseInt(cell.dataset.y); // 如果已经选中了一个棋子 if (selectedPiece) { const fromX = parseInt(selectedCell.dataset.x); const fromY = parseInt(selectedCell.dataset.y); // 检查是否是合法的移动(基础验证) if (!isValidMove(fromX, fromY, x, y)) { alert("不合法的移动!"); return; } // 检查是否可以吃子 if (piece && selectedPiece.classList.contains('red') === piece.classList.contains('red')) { alert("不能吃自己人!"); return; } // 记录移动历史 moveHistory.push({ fromX: fromX, fromY: fromY, toX: x, toY: y, movedPiece: selectedPiece, capturedPiece: piece }); // 移动棋子 if (piece) { piece.remove(); } cell.appendChild(selectedPiece); selectedPiece.style.border = "none"; // 检查胜负 if (piece && (piece.innerText === "帅" || piece.innerText === "将")) { alert((piece.classList.contains('red') ? "黑方胜利!" : "红方胜利!")); restartGame(); return; } // 切换回合 currentTurn = currentTurn === 'red' ? 'black' : 'red'; selectedPiece = null; selectedCell = null; } // 如果没有选中棋子,尝试选择新棋子 else if (piece) { // 检查是否是当前回合的棋子 if ((currentTurn === 'red' && !piece.classList.contains('red')) || (currentTurn === 'black' && piece.classList.contains('red'))) { alert("不是你的回合!"); return; } selectedPiece = piece; selectedCell = cell; piece.style.border = "2px solid blue"; }}// 基础移动验证(简化版)function isValidMove(fromX, fromY, toX, toY) { const pieceText = selectedPiece.innerText; const dx = Math.abs(toX - fromX); const dy = Math.abs(toY - fromY); // 兵/卒的移动规则 if (pieceText === "兵" || pieceText === "卒") { const direction = selectedPiece.classList.contains('red') ? -1 : 1; if (fromX !== toX && fromY !== toY) return false; if (fromX === toX) { return (toY - fromY) * direction > 0; } // 过河后才能左右移动 if (dy === 1 && ((selectedPiece.classList.contains('red') && fromY < 5) || (!selectedPiece.classList.contains('red') && fromY > 4))) { return true; } return false; } // 车 if (pieceText === "车" || pieceText === "车") { return fromX === toX || fromY === toY; } // 马(简化版,不检查蹩脚) if (pieceText === "马") { return (dx === 1 && dy === 2) || (dx === 2 && dy === 1); } // 炮 if (pieceText === "炮") { return fromX === toX || fromY === toY; } // 将/帅(简化版,不检查飞将) if (pieceText === "将" || pieceText === "帅") { return (dx === 1 || dy === 1) && toX >= 3 && toX <= 5 && ((selectedPiece.classList.contains('red') && toY >= 7 && toY <= 9) || (!selectedPiece.classList.contains('red') && toY >= 0 && toY <= 2)); } // 士/仕 if (pieceText === "士" || pieceText === "仕") { return dx === 1 && dy === 1 && toX >= 3 && toX <= 5 && ((selectedPiece.classList.contains('red') && toY >= 7 && toY <= 9) || (!selectedPiece.classList.contains('red') && toY >= 0 && toY <= 2)); } // 相/象 if (pieceText === "相" || pieceText === "象") { return dx === 2 && dy === 2 && ((selectedPiece.classList.contains('red') && toY >= 5) || (!selectedPiece.classList.contains('red') && toY <= 4)); } return true;}function restartGame() { selectedPiece = null; selectedCell = null; moveHistory = []; currentTurn = 'red'; createBoard();}function undoMove() { if (moveHistory.length === 0) { alert("没有可以悔棋的步骤!"); return; } const lastMove = moveHistory.pop(); const fromCell = getCell(lastMove.fromX, lastMove.fromY); const toCell = getCell(lastMove.toX, lastMove.toY); fromCell.appendChild(lastMove.movedPiece); if (lastMove.capturedPiece) { toCell.appendChild(lastMove.capturedPiece); } // 恢复回合 currentTurn = currentTurn === 'red' ? 'black' : 'red';}function getCell(x, y) { return document.querySelector(`.cell[data-x="${x}"][data-y="${y}"]`);}createBoard();三、关键功能说明
  1. 棋盘绘制

    使用CSS Grid布局创建9×10的棋盘

    每个格子(cell)都有data-x和data-y属性标记坐标

  2. 棋子初始化

    使用对象存储初始棋子位置和属性

    红黑双方使用不同样式区分

  3. 核心交互逻辑

    选子:点击棋子时高亮显示

    移动:点击目标位置进行移动

    吃子:自动检测并移除被吃棋子

    胜负判定:当一方将/帅被吃时游戏结束

  4. 新增功能

    回合控制:添加currentTurn变量控制红黑方轮流行棋

    基础规则验证:在isValidMove函数中实现各棋子基本走法

    移动限制

    兵/卒过河前只能前进,过河后可以左右移动

    将/帅只能在九宫格内移动

    士/仕只能在九宫格内斜走

    相/象不能过河

  5. 悔棋功能

    记录每一步的移动和被吃棋子

    恢复时还原棋子位置和状态

四、升级建议
  1. 完善走法规则

    马的蹩脚验证

    炮的隔子吃法

    将/帅的飞将规则

  2. 增强用户体验

    添加移动动画

    高亮显示合法移动位置

    添加游戏状态提示

  3. 高级功能

    添加AI对战

    保存/加载棋局

    移动设备触控支持

这个实现已经包含了象棋游戏的核心功能,可以作为前端练习的优秀项目。你可以直接复制代码运行,或者在此基础上继续完善功能。