如何用JavaScript遍历棋盘对角线实现棋子多步吃子检测?
跳棋多步吃子功能的JavaScript实现方案
问题描述
我已经用HTML、CSS和JavaScript实现了一个简单的跳棋棋盘,上方的金方棋子可以向下、向左或向右方向吃子。现在我想编写一个函数,检测被点击的棋子能否跳过对手棋子。单步吃子的逻辑很简单,只需检查棋子相邻单元格是否存在被空单元格包围的对手棋子;但多步吃子的实现比较复杂,需要循环检测吃子模式,并等待用户依次点击空方格完成移动。请问该如何用JavaScript实现这个功能?
现有代码
HTML
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="styles.css" /> <script src="main.js"></script> </head> <body> <div id="playerA"></div> <table id="checkertable"></table> <div id="playerB"></div> <!-- 修正重复的id问题 --> </body> </html>
CSS
body { background: green; } /*style the table*/ .table { margin: auto; width: 800px; height: 800px; } .square { width: 100px; height: 100px; } .white { background-color: mintcream; } .brown { background-color: chocolate; } /*styles for the pieces*/ .gold-piece { box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5); background-color: gold; width: 80%; height: 80%; border-radius: 50%; margin: 10%; } .red-piece { box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5); background-color: orangered; width: 80%; height: 80%; border-radius: 50%; margin: 10%; } /*style for styling a selected piece*/ .selected-piece { border: 2px solid white; }
原始JavaScript
let A = true; let B = false; //define views that we will use to send messages to both players let messageForA = document.getElementById("playerA"); let messageForB = document.getElementById("playerB"); //this function checks if the tapped piece can make single or multiple jumps over opponent cells function canJumpOverOpponent(table, row, col) { /*a piece can jump over opponent pieces if there is an opponent piece between that piece and an empty square we use a path finding algorithm to check for empty squares in the path of that piece */ //the top pieces can either capture left or right downwards } //this function draws the chekerboard function createCheckerboard() { const table = document.getElementById('checkertable'); for (let row = 0; row < 8; row++) { const newRow = table.insertRow(); for (let col = 0; col < 8; col++) { const newCell = newRow.insertCell(); newCell.classList.add('square'); if ((row + col) % 2 === 0) { //paint the white squares newCell.classList.add('white'); } else { //paint the brow squares where we will draw pieces newCell.classList.add('brown'); //we draw the old pieces in this section if (row < 3) { const goldpiece = document.createElement('div'); goldpiece.classList.add('gold-piece'); newCell.appendChild(goldpiece); goldpiece.addEventListener('click', function(event) { if (A) { if (event.target.classList.contains('gold-piece')) { //means the cell contains a piece console.log("This square contains a gold piece"); //highlight this piece and remove highlights from any previous square const goldpieces = document.querySelectorAll('.gold-piece'); console.log(goldpieces.length); goldpieces.forEach(function(element) { if (element.classList.contains('selected-piece')) { //remove the highlight from previous cells element.classList.remove('selected-piece'); } }); event.target.classList.add('selected-piece'); //check if the piece can make a jump over opponent pieces } else { //this square is empty console.log("this piece is empty, check if user had tapped a previous square or he is completing a capture"); } } else { console.log("player one attempts to play while it is not his turn"); } }); } //we draw the red pieces in this section if (row >= 5) { const redpiece = document.createElement('div'); redpiece.classList.add('red-piece'); newCell.appendChild(redpiece); } } } } } window.onload = () => createCheckerboard();
实现方案
核心思路
- 状态跟踪:用全局变量管理当前选中棋子、玩家回合、是否处于连续吃子阶段,避免交互混乱。
- 单步吃子检测:判断棋子向某个方向移动时,中间是否有对手棋子,目标位置是否为空。
- 多步吃子逻辑:吃完一次后立即检测当前位置是否还能继续吃子,若可以则保持连续吃子状态,直到无法继续。
- 交互逻辑优化:区分用户点击的是己方棋子、空格子,在连续吃子阶段强制用户选择合法目标。
修改后的完整JavaScript代码
// 游戏状态管理 let currentPlayer = 'gold'; // 当前玩家:gold(金方)或red(红方) let selectedPiece = null; // 当前选中的棋子元素 let selectedPos = { row: -1, col: -1 }; // 选中棋子的行列位置 let isInMultiJump = false; // 是否处于连续吃子阶段 // 玩家提示元素 let messageForA = document.getElementById("playerA"); let messageForB = document.getElementById("playerB"); // 获取单元格的行列位置 function getCellPos(cell) { const row = cell.parentElement.rowIndex; const col = cell.cellIndex; return { row, col }; } // 判断指定位置是否存在指定类型的棋子 function hasPiece(row, col, pieceClass) { const table = document.getElementById('checkertable'); // 超出棋盘范围直接返回false if (row < 0 || row >= 8 || col < 0 || col >= 8) return false; const cell = table.rows[row].cells[col]; return cell.querySelector(`.${pieceClass}`) !== null; } // 判断指定位置是否为空(无任何棋子) function isEmpty(row, col) { const table = document.getElementById('checkertable'); if (row < 0 || row >= 8 || col < 0 || col >= 8) return false; const cell = table.rows[row].cells[col]; return !cell.querySelector('.gold-piece') && !cell.querySelector('.red-piece'); } // 检测单步吃子是否可行:从(startRow, startCol)向(dRow, dCol)方向 function canSingleJump(startRow, startCol, dRow, dCol) { const opponentClass = currentPlayer === 'gold' ? 'red-piece' : 'gold-piece'; const jumpOverRow = startRow + dRow; const jumpOverCol = startCol + dCol; const targetRow = startRow + 2 * dRow; const targetCol = startCol + 2 * dCol; // 满足三个条件:中间有对手棋子、目标位置为空、所有位置在棋盘内 return hasPiece(jumpOverRow, jumpOverCol, opponentClass) && isEmpty(targetRow, targetCol); } // 获取当前棋子所有可行的单步吃子方向 function getAllPossibleJumps(row, col) { // 根据玩家确定可移动方向:金方向下左右,红方向上左右 const directions = currentPlayer === 'gold' ? [{ dRow: 1, dCol: -1 }, { dRow: 1, dCol: 1 }] : [{ dRow: -1, dCol: -1 }, { dRow: -1, dCol: 1 }]; const possibleJumps = []; directions.forEach(dir => { if (canSingleJump(row, col, dir.dRow, dir.dCol)) { possibleJumps.push({ targetRow: row + 2 * dir.dRow, targetCol: col + 2 * dir.dCol, jumpOverRow: row + dir.dRow, jumpOverCol: col + dir.dCol }); } }); return possibleJumps; } // 执行吃子操作:移动棋子、移除被吃棋子、更新状态 function performJump(jump) { const table = document.getElementById('checkertable'); // 将选中棋子移动到目标单元格 const targetCell = table.rows[jump.targetRow].cells[jump.targetCol]; targetCell.appendChild(selectedPiece); // 移除被跳过的对手棋子 const jumpedCell = table.rows[jump.jumpOverRow].cells[jump.jumpOverCol]; jumpedCell.innerHTML = ''; // 更新选中棋子的位置 selectedPos = { row: jump.targetRow, col: jump.targetCol }; // 检查吃完后是否还能继续吃子 const nextPossibleJumps = getAllPossibleJumps(jump.targetRow, jump.targetCol); isInMultiJump = nextPossibleJumps.length > 0; if (!isInMultiJump) { // 结束连续吃子,切换玩家回合 currentPlayer = currentPlayer === 'gold' ? 'red' : 'gold'; selectedPiece.classList.remove('selected-piece'); selectedPiece = null; selectedPos = { row: -1, col: -1 }; // 更新玩家提示 const messageEl = currentPlayer === 'gold' ? messageForA : messageForB; messageEl.textContent = `轮到${currentPlayer === 'gold' ? '金方' : '红方'}行动`; // 清空另一玩家的提示 const otherMessageEl = currentPlayer === 'gold' ? messageForB : messageForA; otherMessageEl.textContent = ''; } else { // 继续连续吃子,提示当前玩家继续操作 const messageEl = currentPlayer === 'gold' ? messageForA : messageForB; messageEl.textContent = '请选择下一个吃子目标'; const otherMessageEl = currentPlayer === 'gold' ? messageForB : messageForA; otherMessageEl.textContent = ''; } } // 处理棋子点击事件 function handlePieceClick(event) { const piece = event.target; const cell = piece.parentElement; const pos = getCellPos(cell); // 不是当前玩家的棋子,直接忽略 if ((currentPlayer === 'gold' && !piece.classList.contains('gold-piece')) || (currentPlayer === 'red' && !piece.classList.contains('red-piece'))) { return; } // 处于连续吃子阶段时,只能选择当前正在吃子的棋子 if (isInMultiJump && piece !== selectedPiece) { return; } // 移除之前的选中状态 document.querySelectorAll('.selected-piece').forEach(p => p.classList.remove('selected-piece')); // 设置新的选中状态 selectedPiece = piece; selectedPos = pos; piece.classList.add('selected-piece'); // 检测该棋子的所有可行吃子路径 const possibleJumps = getAllPossibleJumps(pos.row, pos.col); const messageEl = currentPlayer === 'gold' ? messageForA : messageForB; if (possibleJumps.length > 0) { messageEl.textContent = '请选择吃子目标'; } else { messageEl.textContent = '该棋子无法吃子'; } const otherMessageEl = currentPlayer === 'gold' ? messageForB : messageForA; otherMessageEl.textContent = ''; } // 处理空单元格点击事件 function handleEmptyCellClick(event) { const cell = event.target; const pos = getCellPos(cell); // 没有选中棋子,或当前棋子无法吃子,直接忽略 if (!selectedPiece || getAllPossibleJumps(selectedPos.row, selectedPos.col).length === 0) { return; } // 检查点击的位置是否是合法的吃子目标 const possibleJumps = getAllPossibleJumps(selectedPos.row, selectedPos.col); const targetJump = possibleJumps.find(jump => jump.targetRow === pos.row && jump.targetCol === pos.col); if (targetJump) { // 执行吃子操作 performJump(targetJump); } } // 创建棋盘 function createCheckerboard() { const table = document.getElementById('checkertable'); table.classList.add('table'); // 绑定CSS中的table样式 for (let row = 0; row < 8; row++) { const newRow = table.insertRow(); for (let col = 0; col < 8; col++) { const newCell = newRow.insertCell(); newCell.classList.add('square'); if ((row + col) % 2 === 0) { newCell.classList.add('white'); } else { newCell.classList.add('brown'); // 为空单元格绑定点击事件 newCell.addEventListener('click', handleEmptyCellClick); if (row < 3) { const goldpiece = document.createElement('div'); goldpiece.classList.add('gold-piece'); newCell.appendChild(goldpiece); goldpiece.addEventListener('click', handlePieceClick); } if (row >= 5) { const redpiece = document.createElement('div'); redpiece.classList.add('red-piece'); newCell.appendChild(redpiece); redpiece.addEventListener('click', handlePieceClick); } } } } // 初始化提示信息 messageForA.textContent = '轮到金方行动'; } window.onload = () => createCheckerboard();
内容的提问来源于stack exchange,提问作者Anthony Kinyua
相关产品推荐
相关产品推荐

