I am working on a battleship game and I am trying to update the x and y coordinates on an HTMLCollection. The way I am trying to update is not working because I am accessing it as a 2d array when it is a 1d array.
// display.js
const displayCompAttk = () => {
const result = game.computer.sendAttack(game.player)
const x = result[0]
const y = result[1]
playerCells[x][y].textContent = result[2]
}
Meaning I can only update the x or y coordinates like so playerCells[x] or playerCells[y] and that is not what I want. Is there a possible way to update the x and y coordinates of the HTMLCollection to display true or false ?
const createShip = (shipLength, name) => {
const hits = 0
let sunk = false
function getLength() {
return this.shipLength
}
function hit() {
this.hits += 1
return this.hits
}
function isSunk() {
if (this.hits >= this.shipLength) {
sunk = true
return sunk
}
sunk = false
return sunk
}
return {
getLength,
hit,
isSunk,
shipLength,
name,
hits,
}
}
// gameboard.js
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 shipIsInbounds = (x, y, ship) => {
const shipsLength = ship.getLength()
if (x + shipsLength > columns) {
// console.log("Cannot place ship horizontally, out of bounds.");
return false;
}
if (y + shipsLength > rows) {
// console.log("Cannot place ship vertically, out of bounds.");
return false;
}
return true
}
const doShipsCollide = (x, y, ship) => {
const shipsLength = ship.getLength()
const currentBoard = getBoard()
for (let i = 0; i < shipsLength; i += 1) {
if (currentBoard[x][y + i].type !== "water") {
// console.log("Ships cannot overlap!")
return false
}
}
for (let i = 0; i < shipsLength; i += 1) {
if (currentBoard[x + i][y].type !== "water") {
// console.log("Ships cannot overlap!")
return false
}
}
return true
}
const placeHorizontal = (x, y, ship) => {
const shipsLength = ship.getLength()
const currentBoard = getBoard()
// Loop through ships length
if (validCoords(x, y) && shipIsInbounds(x, y, ship) && doShipsCollide(x, y, ship)) {
for (let i = 0; i < shipsLength; i += 1) {
// Change ship.name back to ship
currentBoard[x][y + i] = ship
shipArr.push(ship)
}
return true
}
return false
}
const placeVertical = (x, y, ship) => {
const shipsLength = ship.getLength()
const currentBoard = getBoard()
// Loop through ships length
if (validCoords(x, y) && shipIsInbounds(x, y, ship) && doShipsCollide(x, y, ship)) {
for (let i = 0; i < shipsLength; i += 1) {
// Change ship.name back to ship
currentBoard[x + i][y] = ship
shipArr.push(ship)
}
return true
}
return false
}
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 (canShipBeHitAgain(x, y) && validCoords(x, y) && currentBoard[x][y].hit()) {
attackCoord.push([x, y])
console.log(`Hit at coordinates ${[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 false
})
return true
}
return {
placeVertical,
placeHorizontal,
getBoard,
cellCount,
receiveAttack,
allSunk,
canShipBeHitAgain,
validCoords,
shipIsInbounds,
doShipsCollide,
attackCoord
}
}
// player.js
const createComputer = () => {
const computerGameboard = Gameboard()
// console.log(computerGameboard.attackCoord)
const arrayOfCoords = []
const compAttkCoords = []
let counter = 0
const shuffleArray = (array) => {
for (let i = array.length - 1; i > 0; i -= 1) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]]
}
}
for (let i = 0; i < 10; i += 1) {
for (let j = 0; j < 10; j += 1) {
arrayOfCoords.push([i, j]);
}
}
shuffleArray(arrayOfCoords)
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)
}
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)
}
const setEnemyBoard = (player) => player.getPlayerBoard
const sendAttack = (player) => {
const randomXCoords = arrayOfCoords[counter][0]
const randomYCoords = arrayOfCoords[counter][1]
compAttkCoords.push([randomXCoords, randomYCoords])
const response = player.playerGameboard.receiveAttack(randomXCoords, randomYCoords)
counter += 1;
return [randomXCoords, randomYCoords, response]
}
return {
computerGameboard,
setEnemyBoard,
placeShipHorizontal,
placeShipVertical,
sendAttack,
compAttkCoords
}
}
const createPlayer = (name) => {
const getName = () => name
const playerGameboard = Gameboard()
// console.log(playerGameboard.attackCoord)
const placeShipHorizontal = (x, y, ship) => playerGameboard.placeHorizontal(x, y, ship)
const placeShipVertical = (x, y, ship) => playerGameboard.placeVertical(x, y, ship)
const setEnemyBoard = (comp) => comp.getCompBoard
const sendAttack = (x, y, comp) => comp.computerGameboard.receiveAttack(x, y)
return {
getName,
placeShipHorizontal,
placeShipVertical,
setEnemyBoard,
sendAttack,
playerGameboard
}
}
// game.js
const createGame = () => {
const player = createPlayer("Player")
const computer = createComputer()
const carrier = createShip(5, "Carrier")
const battleship = createShip(4, "Battleship")
const destroyer = createShip(3, "Destroyer")
const submarine = createShip(3, "Submarine")
const patrolBoat = createShip(2, "Patrol Boat")
return {
player,
computer,
carrier,
battleship,
destroyer,
submarine,
patrolBoat
}
}
// display.js
const playerContainer = document.querySelector(".player-container")
const computerContainer = document.querySelector(".computer-container")
const compCells = document.getElementsByClassName("c-board-cell")
const playerCells = document.getElementsByClassName("p-board-cell")
const game = createGame()
const createPlayerDisplay = () => {
const board = game.player.playerGameboard.getBoard()
for (let i = 0; i < board.length; i += 1) {
board[i] = []
for (let j = 0; j < board.length; j += 1) {
const cell = document.createElement("td")
cell.classList.add("p-board-cell")
cell.setAttribute("x", i)
cell.setAttribute("y", j)
board[i][j] = cell
}
}
for (let i = 0; i < 10; i += 1) {
const row = document.createElement("tr")
for (let j = 0; j < 10; j += 1) {
row.append(board[i][j])
}
row.classList.add("p-board-row")
playerContainer.append(row)
}
}
createPlayerDisplay()
const createCompDisplay = () => {
const board = game.computer.computerGameboard.getBoard()
for (let i = 0; i < board.length; i += 1) {
board[i] = []
for (let j = 0; j < board.length; j += 1) {
const cell = document.createElement("td")
cell.classList.add("c-board-cell")
cell.setAttribute("x", i)
cell.setAttribute("y", j)
board[i][j] = cell
}
}
for (let i = 0; i < 10; i += 1) {
const row = document.createElement("tr")
for (let j = 0; j < 10; j += 1) {
row.append(board[i][j])
}
row.classList.add("c-board-row")
computerContainer.append(row)
}
}
createCompDisplay()
const displayPlayerAttk = () => {
for (let i = 0; i < compCells.length; i += 1) {
compCells[i].addEventListener("click", (e) => {
if (compCells[i].textContent.includes("true" || "false")) {
return false
}
const xPos = e.currentTarget.getAttribute("x")
const yPos = e.currentTarget.getAttribute("y")
compCells[i].textContent = game.player.sendAttack(Number(xPos), Number(yPos), game.computer)
return true
})
}
}
displayPlayerAttk()
const displayCompAttk = () => {
const result = game.computer.sendAttack(game.player)
const x = result[0]
const y = result[1]
playerCells[x][y].textContent = result[2]
}
game.player.sendAttack(1, 2, game.computer)
game.computer.placeShipHorizontal(game.carrier)
game.computer.placeShipVertical(game.battleship)
game.player.placeShipHorizontal(1, 2, game.carrier)
game.player.placeShipVertical(6, 6, game.battleship)
displayCompAttk()
* {
margin: 0;
padding: 0;
box-sizing: border-box;
text-decoration: none;
list-style: none;
}
td {
border: black solid 1px;
width: 25px;
height: 25px;
}
tr {
border: black solid 1px;
}
<body>
<header>
<h1 class="title">Battleship</h1>
</header>
<main>
<div class="main-container">
<h1 class="computer-header">Player</h1>
<table class="player-container"></table>
<h1 class="computer-header">Computer</h1>
<table class="computer-container"></table>
<div class="ship-container">
</div>
</div>
</main>
</body>
Frame Challenge
A confusing part of code is overwriting the variable board
array content in the create display functions. board
initially contains a cloned 2D array of Water
objects (to get its dimensions) but entries are overwritten with td
elements used to create a board table in HTML.
In createPlayerDisplay
the 2D array of board elements is not saved or returned, but if made a property of player
, say player.grid
, could be used to set HTML table cells using x
and y
coordinates.
Possible patch
Insert game.player.grid = board;
at the end of the createPlayerDisplay
function body
replace the last line of displayCompAttk
, playerCells[x][y].textContent = result[2]
, with
game.player.grid[x][y].textContent = result[2]
Note I did not attempt to finish or debug the game further. With the patch the game ran without runtime errors and clicks on the "Computer" display board updated cells with "true" or "false".
Conversion formula
To update the linear playerCells
array as asked in the title however, you could use the conversion formula
index = y * rowLength + x