I'm working on a Tic Tac Toe project and having trouble with a certain task.
Build the functions that allow players to add marks to a specific spot on the board, and then tie it to the DOM, letting players click on the gameboard to place their marker. Don’t forget the logic that keeps players from playing in spots that are already taken!
Specifically the "Don't forget the logic that keeps players from playing in spots that are already taken!
I'm trying to see how to prevent the players from updating their marker from "X" to "O" and vice versa. I'd have to implement some sort of way to check if the cell is empty then don't update the array/board any more, but I'm not sure how to go about it.
const Gameboard = (() => {
"use strict"
let gameboard = new Array(9).fill("");
const setCell = (index, value) => {
console.log({ index, value });
gameboard[index] = value;
};
const resetBoard = () => {
gameboard = ["", "", "", "", "", "", "", "", ""];
}
const getBoard = () => [...gameboard];
return {
getBoard,
resetBoard,
setCell
}
})();
const displayController = (() => {
"use strict"
const board = document.querySelector(".board")
const renderBoard = (arr) => {
for (let i = 0; i < arr.length; i++) {
let cell = document.createElement("div")
cell.id = i;
cell.classList = "cell";
cell.setAttribute("cell-data", i)
cell.textContent = arr[i]
board.append(cell)
}
}
const clearBoard = () => {
while (board.hasChildNodes()) {
board.removeChild(board.firstChild);
}
}
return {
board,
renderBoard,
clearBoard
}
})();
const gameController = (() => {
"use strict"
const playerFunction = (player, marker) => {
return {
player,
marker,
}
}
let player1 = playerFunction("Test1", "X")
let player2 = playerFunction("Test2", "O")
let isPlayerOneTurn = true
const playerTurn = () => {
if (isPlayerOneTurn) {
isPlayerOneTurn = false
return player1.marker
} else {
isPlayerOneTurn = true
return player2.marker
}
}
const boardEl = displayController.board;
displayController.renderBoard(Gameboard.getBoard());
boardEl.addEventListener("click", (e) => {
if (e.target.classList.contains("cell")) {
const index = e.target.getAttribute("cell-data");
Gameboard.setCell(index, playerTurn());
displayController.clearBoard();
displayController.renderBoard(Gameboard.getBoard());
}
});
})();
body {
margin-top: 10rem;
font-family: 'Dongle', sans-serif;
}
header {
display: flex;
justify-content: center;
}
h1 {
color: rgba(0, 0, 0);
font-size: 70px;
font-weight: 500;
margin-bottom: auto;
}
.board {
display: grid;
grid-template-columns: repeat(3, 100px);
justify-content: center;
}
.cell {
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
font-size: 70px;
border: solid 1px;
width: 100px;
user-select: none;
height: 100px;
}
.cell:hover {
background-color: rgb(175, 175, 175, .5);
}
.cell:nth-child(1) {
border-top: none;
border-left: none;
}
.cell:nth-child(2) {
border-top: none;
}
.cell:nth-child(3),
.cell:nth-child(4),
.cell:nth-child(6) {
border-top: none;
border-right: none;
border-left: none;
}
.cell:nth-child(5) {
border-top: none;
}
.cell:nth-child(7) {
border: none;
}
.cell:nth-child(8) {
border-top: none;
border-bottom: none;
}
.cell:nth-child(9) {
border: none;
}
<div class="board"></div>
With the code you already have in place, you can read a cell's value with Gameboard.getBoard()[index]
. If that is still the empty string (and thus not "X" or "O"), then it is OK to play at that index.
So you could add this statement:
if (Gameboard.getBoard()[index]) return; // Exit the handler without action
Here is your updated snippet:
const Gameboard = (() => {
"use strict"
let gameboard = new Array(9).fill("");
const setCell = (index, value) => {
gameboard[index] = value;
};
const resetBoard = () => {
gameboard = ["", "", "", "", "", "", "", "", ""];
}
const getBoard = () => [...gameboard];
return {
getBoard,
resetBoard,
setCell
}
})();
const displayController = (() => {
"use strict"
const board = document.querySelector(".board")
const renderBoard = (arr) => {
for (let i = 0; i < arr.length; i++) {
let cell = document.createElement("div")
cell.id = i;
cell.classList = "cell";
cell.setAttribute("cell-data", i)
cell.textContent = arr[i]
board.append(cell)
}
}
const clearBoard = () => {
while (board.hasChildNodes()) {
board.removeChild(board.firstChild);
}
}
return {
board,
renderBoard,
clearBoard
}
})();
const gameController = (() => {
"use strict"
const playerFunction = (player, marker) => {
return {
player,
marker,
}
}
let player1 = playerFunction("Test1", "X")
let player2 = playerFunction("Test2", "O")
let isPlayerOneTurn = true
const playerTurn = () => {
if (isPlayerOneTurn) {
isPlayerOneTurn = false
return player1.marker
} else {
isPlayerOneTurn = true
return player2.marker
}
}
const boardEl = displayController.board;
displayController.renderBoard(Gameboard.getBoard());
boardEl.addEventListener("click", (e) => {
if (!e.target.classList.contains("cell")) return;
const index = e.target.getAttribute("cell-data");
if (Gameboard.getBoard()[index]) return;
Gameboard.setCell(index, playerTurn());
displayController.clearBoard();
displayController.renderBoard(Gameboard.getBoard());
});
})();
body {
margin-top: 10rem;
font-family: 'Dongle', sans-serif;
}
header {
display: flex;
justify-content: center;
}
h1 {
color: rgba(0, 0, 0);
font-size: 70px;
font-weight: 500;
margin-bottom: auto;
}
.board {
display: grid;
grid-template-columns: repeat(3, 100px);
justify-content: center;
}
.cell {
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
font-size: 70px;
border: solid 1px;
width: 100px;
user-select: none;
height: 100px;
}
.cell:hover {
background-color: rgb(175, 175, 175, .5);
}
.cell:nth-child(1) {
border-top: none;
border-left: none;
}
.cell:nth-child(2) {
border-top: none;
}
.cell:nth-child(3),
.cell:nth-child(4),
.cell:nth-child(6) {
border-top: none;
border-right: none;
border-left: none;
}
.cell:nth-child(5) {
border-top: none;
}
.cell:nth-child(7) {
border: none;
}
.cell:nth-child(8) {
border-top: none;
border-bottom: none;
}
.cell:nth-child(9) {
border: none;
}
<div class="board"></div>
Not your question, but don't forget to add logic that detects a win or a draw and act accordingly.