战舰游戏中获取有效新坐标解决越界报错问题
战舰游戏随机放置船只越界问题及修复方案
随机放置战舰时出现越界报错:
Uncaught TypeError: Cannot set properties of undefined (setting '4')
已实现placeHorizontal和placeVertical函数,希望坐标无效时获取新坐标重新放置,但暂未实现getNewCoords函数,相关代码如下:
Gameboard代码
const Gameboard = () => { const rows = 10 const columns = 10 const board = [] const missedCoord = [] const shipArr = [] const attackCoord = [] const getBoard = () => [...board] const Water = () => { let isHit = false const hit = () => { isHit = true }; return { type: "water", hit, get isHit() { if (isHit) { return "BLOOP! Miss." } return "Ship hit!" } } } for (let i = 0; i < rows; i += 1) { board[i] = [] for (let j = 0; j < columns; j += 1) { board[i][j] = Water() } } // Need to reduce array amount to a single value const cellCount = getBoard().reduce((row, col) => row + col.length, 0) const validCoords = (x, y) => { if (x < 0) { return false } if (x > 9) { return false } if (y < 0) { return false } if (y > 9) { return false } return true } const getNewCoords = (x, y) => { } const placeHorizontal = (x, y, ship) => { const shipsLength = ship.getLength() const currentBoard = getBoard() // Loop through ships length for (let i = 0; i < shipsLength; i += 1) { // Change ship.name back to ship currentBoard[x][y + i] = ship.name shipArr.push(ship) } return true } const placeVertical = (x, y, ship) => { const check = validCoords(x, y) if (!check) { const newXandY = getNewCoords(x, y) return placeVertical(newXandY) } const shipsLength = ship.getLength() const currentBoard = getBoard() // Loop through ships length for (let i = 0; i < shipsLength; i += 1) { // Change ship.name back to ship currentBoard[x + i][y] = ship.name shipArr.push(ship) } return true } function canShipBeHitAgain(x, y) { const coords = [x, y] const coordStr = JSON.stringify(coords) const attackCoordStr = JSON.stringify(attackCoord[0]) if (coordStr === attackCoordStr) { console.log("Cannot hit same spot!") return false } return true } function receiveAttack(x, y) { const water = Water() const currentBoard = getBoard() if (currentBoard[x][y].hit() && canShipBeHitAgain(x, y)) { attackCoord.push([x, y]) return true } water.hit() missedCoord.push([x, y]) return false } function allSunk() { shipArr.every((ship) => { if (!ship.isSunk()) { console.log("All ships are not sunk.") return false } return console.log("All ships are sunk!") }) return true } return { placeVertical, placeHorizontal, getBoard, cellCount, receiveAttack, allSunk, canShipBeHitAgain, validCoords } }
AI/电脑放置逻辑代码
const createComputer = () => { const computerGameboard = Gameboard() // const getCompBoard = computerGameboard.getBoard() const placeShipHorizontal = (ship) => { let x; let y; do { x = Math.floor((Math.random() * 9)); y = Math.floor((Math.random() * 9)); } while (computerGameboard.placeHorizontal(x, y, ship) === false); computerGameboard.placeHorizontal(x, y, ship) return true } const placeShipVertical = (ship) => { let x; let y; do { x = Math.floor((Math.random() * 9)); y = Math.floor((Math.random() * 9)); } while (computerGameboard.placeVertical(x, y, ship) === false); computerGameboard.placeVertical(x, y, ship) return true } const setEnemyBoard = (player) => player.getPlayerBoard return { computerGameboard, setEnemyBoard, placeShipHorizontal, placeShipVertical } } const computer = createComputer() computer.setEnemyBoard(player) const testShip = createShip(3, "1") const testShip2 = createShip(3, "2") computer.placeShipVertical(testShip) computer.placeShipHorizontal(testShip2)
问题分析与修复方案
1. 报错根源
当前placeHorizontal和placeVertical仅检查初始坐标是否有效,未验证整个船只放置范围是否在棋盘内。例如横向放置时,若y + 船长度 - 1 > 9,则currentBoard[x][y+i]会访问到undefined的数组索引,触发报错。同时,放置函数未检查目标位置是否已被占用,会导致船只重叠。
2. 修复步骤
(1)更新有效性检查逻辑
新增检查整个船只放置范围的函数,替换原validCoords:
// 检查横向放置是否合法:坐标在棋盘内,且目标位置未被占用 const canPlaceHorizontal = (x, y, shipLength) => { if (x < 0 || x > 9) return false; if (y + shipLength - 1 > 9) return false; for (let i = 0; i < shipLength; i++) { if (board[x][y+i].type !== "water") return false; } return true; }; // 检查纵向放置是否合法:坐标在棋盘内,且目标位置未被占用 const canPlaceVertical = (x, y, shipLength) => { if (y < 0 || y > 9) return false; if (x + shipLength - 1 > 9) return false; for (let i = 0; i < shipLength; i++) { if (board[x+i][y].type !== "water") return false; } return true; };
(2)修复放置函数
修改placeHorizontal和placeVertical,先做合法性检查,无效则返回false,让电脑的do-while循环重新生成坐标:
const placeHorizontal = (x, y, ship) => { const shipLength = ship.getLength(); if (!canPlaceHorizontal(x, y, shipLength)) return false; for (let i = 0; i < shipLength; i += 1) { board[x][y + i] = ship; // 建议直接存储ship对象,方便后续攻击判断 } shipArr.push(ship); // 移出循环,避免重复添加同一艘船 return true; }; const placeVertical = (x, y, ship) => { const shipLength = ship.getLength(); if (!canPlaceVertical(x, y, shipLength)) return false; for (let i = 0; i < shipLength; i += 1) { board[x + i][y] = ship; } shipArr.push(ship); return true; };
(3)实现getNewCoords(可选)
若需单独生成合法随机坐标,可实现如下:
const getNewCoords = () => { return { x: Math.floor(Math.random() * 10), y: Math.floor(Math.random() * 10) }; };
如果要在placeVertical中使用该函数,需修正递归调用的参数:
const placeVertical = (x, y, ship) => { const shipLength = ship.getLength(); if (canPlaceVertical(x, y, shipLength)) { // 放置逻辑... return true; } const newCoords = getNewCoords(); return placeVertical(newCoords.x, newCoords.y, ship); };
(4)修复电脑放置逻辑的坐标范围
原代码中Math.random() *9仅生成0-8的坐标,改为Math.random() *10覆盖0-9的完整棋盘:
const placeShipHorizontal = (ship) => { let x; let y; do { x = Math.floor(Math.random() * 10); y = Math.floor(Math.random() * 10); } while (!computerGameboard.placeHorizontal(x, y, ship)); return true; }; const placeShipVertical = (ship) => { let x; let y; do { x = Math.floor(Math.random() * 10); y = Math.floor(Math.random() * 10); } while (!computerGameboard.placeVertical(x, y, ship)); return true; };
内容的提问来源于stack exchange,提问作者DataMoshing
相关产品推荐
相关产品推荐

