Assignment – Bottom-up Planning

Share your exercise results here, and don’t be afraid to ask questions if you need help!

It’s a little sloppy in that I had to set the current player to the opposite of the desired choice when defining the variable in the ticTacToe() function because it needed to be reversed at the beginning of the do/while loop.

https://codesandbox.io/s/tender-colden-3ke87?file=/game.js

``````/**
* Prompts user for move coordinates.
*/
function getUserInput(nextPlayerSymbol) {
return prompt(`Place \${nextPlayerSymbol} at coordinate (0 - 9):`);
}

/**
* Checks if square is available and coordinates are within the game board.
*/
function isMoveValid(coordinates, gameBoard) {
return (
gameBoard[coordinates] === null && Number(coordinates) < gameBoard.length
);
}

/**
* If move is valid, applies current move to game board.
*/
function makeAMove(gameBoard, nextPlayerSymbol) {
let coordinates = getUserInput(nextPlayerSymbol);
let newGameBoard = [...gameBoard];

// Continue prompting user until valid coordinates provided
while (!isMoveValid(coordinates, gameBoard)) {
coordinates = getUserInput(nextPlayerSymbol);
}

// Apply move to copy of game board
newGameBoard[coordinates] = nextPlayerSymbol;

return newGameBoard;
}

/**
* Checks if last move satisfies 'win' criteria.
*/
function hasLastMoverWon(lastMove, gameBoard) {
let winnerCombos = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
for (let [i1, i2, i3] of winnerCombos) {
if (
gameBoard[i1] === lastMove &&
gameBoard[i1] === gameBoard[i2] &&
gameBoard[i1] === gameBoard[i3]
) {
return true;
}
}
return false;
}

/**
* Checks if game has ended with a win or draw.
*/
function isGameOver(gameBoard, currentPlayerSymbol) {
const lastMove = currentPlayerSymbol;

// Check if there is a winner
if (hasLastMoverWon(lastMove, gameBoard)) {
// Write a message that last mover has won the game
alert(`Congratulations, \${currentPlayerSymbol} has won the game`);
return true;
}
// Check if the board is full
if (!gameBoard.includes(null)) {
alert("Game has ended in a draw!");
return true;
}

// Continue game
return false;
}

/**
* Initializes game and handles player turns.
*/
function ticTacToe() {
let gameBoard = new Array(9).fill(null);
let players = ["X", "O"];
let nextPlayerIndex = 1;
let currentPlayerSymbol = players[nextPlayerIndex];

// Process turns
do {
// Set current player symbol
if (nextPlayerIndex === 0) {
nextPlayerIndex = 1;
} else {
nextPlayerIndex = 0;
}
currentPlayerSymbol = players[nextPlayerIndex];

// Make a move
gameBoard = makeAMove(gameBoard, currentPlayerSymbol);

// Show updated game board to user
\${gameBoard[0]}|\${gameBoard[1]}|\${gameBoard[2]}
\${gameBoard[3]}|\${gameBoard[4]}|\${gameBoard[5]}
\${gameBoard[6]}|\${gameBoard[7]}|\${gameBoard[8]}
`);
} while (!isGameOver(gameBoard, currentPlayerSymbol));
}

// Initialize game
ticTacToe();
``````
2 Likes

Im lost… i copied and past the code into sandbox in the index.html under the script tag so i can edit and run the code. i think we need to learn how to use sandbox first

``````var lastMove = null;

function getUserInput(nextPlayerSymbol) {
let coordinates = prompt('Enter coordinates (xy) between 0-2: ');
[x,y] = [...coordinates];
return Number.parseInt(x),Number.parseInt(y);
}

function isMoveValid(coordinates, gameBoard) {
if ((gameBoard[(Number.parseInt(y)*Number.parseInt(3)) + Number.parseInt(x)] == null) && (x<3) && (y<3)){
return true
}
return false;
}

function makeAMove(gameBoard, nextPlayerSymbol) {
// clone the game board before placing moves in it
let gb = gameBoard.slice();
let coord = null;
do {
coord = getUserInput(nextPlayerSymbol);
} while ( !isMoveValid(coord, gb) );
lastMove = (Number.parseInt(y)*Number.parseInt(3)) + Number.parseInt(x);
gb[lastMove] = nextPlayerSymbol;
return gb
}

function hasLastMoverWon(_lastMover, gameBoard) {
let winnerCombos = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
console.log("lastmove" + lastMove)
for (let [i1, i2, i3] of winnerCombos) {
console.log("combos:  " + i1 + ", " + i2 + ', ' + i3)
if (gameBoard[i1] == _lastMover &&
gameBoard[i1] === gameBoard[i2] &&
gameBoard[i1] === gameBoard[i3]
) {
return true;
}
}
return false;
}

function isGameOver(gameBoard, currentPlayerSymbol) {
// 1. check if there is a winner
if (hasLastMoverWon(currentPlayerSymbol, gameBoard) ) {
// Write a message that last mover has won the game
alert(`Congratulations, \${currentPlayerSymbol} has won the game`);
return true;
}
// 2. check if the board is full
if (gameBoard.every(e => e != null)){
return true
}

// Return: winner/draw OR game is still in progress
}

function ticTacToe() {
// State space
let gameBoard = new Array(9).fill(null);
let players = ['X', 'O'];
let nextPlayerIndex = 0;
currentPlayerSymbol = players[nextPlayerIndex];

// Computations
do {
currentPlayerSymbol = players[nextPlayerIndex];
gameBoard = makeAMove(gameBoard, currentPlayerSymbol);
console.log(gameBoard.slice(0,3));
console.log(gameBoard.slice(3,6));
console.log(gameBoard.slice(6,9));

// console.log("lastmove" + lastMove)

if (nextPlayerIndex == 0){
nextPlayerIndex = 1;
} else{
nextPlayerIndex = 0;
}

} while ( !isGameOver(gameBoard, currentPlayerSymbol) );

// Return value
// return undefined;
}
``````
1 Like

I could’nt use it either… but if you program in visual studio code and copy the entire code (ctrl + a ) to the Chrome developer console and press enter… and then type in ticTacToe() it should work. It did for me.

``````let lastMove;

function getUserInputAsBoardIndex(nextPlayerSymbol, gameBoard) {
const promptMsg = `\${getBoardPrintStr(gameBoard)}\nEnter move for \${nextPlayerSymbol} by specifying row and column separated by comma, e.g. 1,1. Board is [3,3]`;
const result = prompt(promptMsg);
return convertUserInputToBoardIndex(result);
}

function convertUserInputToBoardIndex(userInput) {
if (!userInput)
return null;

const inputArr = userInput.trim().split(',');
if (inputArr.length !== 2)
return null;

const x = inputArr[0].trim();
const y = inputArr[1].trim();

if (!Number.isInteger(Number(x)) || !Number.isInteger(Number(y)))
return null;

if (x > 3 || y > 3)
return null;

// End users count from 1, thus the subtraction
const boardIndex = ((x - 1) * 3) + (y - 1);
return boardIndex;
}

function isMoveValid(boardIndex, gameBoard) {
if (Number.isInteger(boardIndex)) {
if (boardIndex >= 0 && boardIndex <= 8) {
if (!gameBoard[boardIndex]) {
return true; // valid index of empty slot board
}
}
}

return false;
}

function makeAMove(gameBoard, nextPlayerSymbol) {
let userInputBoardIndex;
do {
userInputBoardIndex = getUserInputAsBoardIndex(nextPlayerSymbol, gameBoard);
} while (!isMoveValid(userInputBoardIndex, gameBoard));

lastMove = nextPlayerSymbol;
// clone the game board before placing moves in it
const newGameBoard = [...gameBoard];
newGameBoard[userInputBoardIndex] = nextPlayerSymbol;
return newGameBoard;
}

function hasLastMoverWon(lastMove, gameBoard) {
let winnerCombos = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
for (let [i1, i2, i3] of winnerCombos) {
if (gameBoard[i1] === lastMove &&
gameBoard[i1] === gameBoard[i2] &&
gameBoard[i1] === gameBoard[i3]
) {
return true;
}
}
return false;
}

function isGameOver(gameBoard, currentPlayerSymbol) {
// 1. check if there is a winner
if (hasLastMoverWon(lastMove, gameBoard)) {
// Write a message that last mover has won the game
alert(`\${getBoardPrintStr(gameBoard)}\nCongratulations, \${currentPlayerSymbol} has won the game`);
return true;
}

const hasBoardEmptySlots = gameBoard.some(x => !x);
if (hasBoardEmptySlots) {
console.log('Game in progress...');
return false;
} else {
return true;
}
}

function getBoardPrintStr(gameBoard) {
let str = '';
let newLineCounter = 0;

gameBoard.forEach(element => {
str += `\${element || '__'}  `;
if (newLineCounter >= 2) {
str += '\n';
newLineCounter = 0;
// new line and reset
} else {
newLineCounter++;
}
});

return str;
}

function ticTacToe() {
// State space
let gameBoard = new Array(9).fill(null);
let players = ['X', 'O'];
let nextPlayerIndex = 0;
let currentPlayerSymbol = players[nextPlayerIndex];

// Computations
do {
currentPlayerSymbol = players[nextPlayerIndex];
gameBoard = makeAMove(gameBoard, currentPlayerSymbol);
nextPlayerIndex = nextPlayerIndex % 2 === 0 ? 1 : 0;
} while (!isGameOver(gameBoard, currentPlayerSymbol));

// Return value: undefined
}
``````

Can someone explain me how he made the destructuring with the [i1, i2, i3] in this function :

``````function hasLastMoverWon(lastMove, gameBoard) {
let winnerCombos = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
for (let [i1, i2, i3] of winnerCombos) {
if (gameBoard[i1] === lastMove &&
gameBoard[i1] === gameBoard[i2] &&
gameBoard[i1] === gameBoard[i3]
) {
return true;
}
}
return false;
}
``````

I already read the chapter “Destructuring” of the PDF “ES6”.

Thanks for the help!

With a little help from the Internet

@AdamFortuna @zsolt-nagy Would you please review the TODOs in the comments of my code?

1. Not sure why we have to clone the board in makeMove(). The game seems to work fine without cloning
2. I tried to break out changePlayer() into a separate function. The function worked fine, but I could not get the new player value to return to the calling function. Can you explain why?

Thanks and onward!

``````const PLAYERX_WON = 'Player X won!! Game over.';
const PLAYERO_WON = 'Player 0 won!! Game over.';
const DRAW = 'It\'s a draw. Game over.';

function getUserInput(board, currentPlayer) {
displayBoard(board);

let tic = prompt(`Player: \${currentPlayer}. It's your move.
A valid move is a number from 1 - 9, which is not already taken.`);
return tic - 1;
}

function displayBoard(board) {

}

function getBoardString(board) {
let boardString = '';
let tile = 0;

for (let i = 0; i < board.length; i += 1) {
boardString += board[i] === '' ? ` \${i+1} ` : ` \${board[i]} `;
if (i === 2) {boardString += '\n'};
if (i === 5) {boardString += '\n'};
if (i === 8) {boardString += '\n'};
}
return boardString;
}

function ticTacToe() {

let board = ['','','','','','','','',''];
//let board = new Array(9).fill('');
let currentPlayer = 'O';

do {
// changePlayer(currentPlayer); <--TODO: The new changePlayer value is not passed back to this call. Why??
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
board = makeMove(board, currentPlayer);
} while ( !isGameOver(board, currentPlayer) );

console.log('END')
}

function changePlayer(currentPlayer) {
currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
//console.log(`changePlayer() returns: \${currentPlayer}`)
return currentPlayer; // TODO: I could not get the value to pass back to the calling function
}

function makeMove(board, currentPlayer) {
//TODO: why do we need to clone the board? The game seems to work fine without cloning
let newBoard = [...board];
do {
//have to use var to make 'move' visible outside the 'do' block
var move = getUserInput(board, currentPlayer);
} while ( !isMoveValid(move, board) );
newBoard[move] = currentPlayer;
return newBoard;
}

function isMoveValid (move, board) {
let result = board[move] === '' ? true : false;
return result;
}

function isGameOver(board, currentPlayer) {
let gameWon = false;

let winningCombos = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];

//check if there is a winner
for (let i = 0; i <= 7; i += 1) {
const winCondition = winningCombos[i];
const a = board[winCondition[0]];
const b = board[winCondition[1]];
const c = board[winCondition[2]];
if (a === '' || b === '' || c === '') {
continue;
}
if (a === b && b === c) {
gameWon = true;
break;
}
}

if (gameWon) {
announce(currentPlayer === 'X' ? PLAYERX_WON : PLAYERO_WON);
displayBoard(board);
return true;
}

if ( !board.includes('') ) {
announce(DRAW);
displayBoard(board);
return true;
}
return false;
}

function announce(message) {
switch(message){
case PLAYERO_WON:
console.log(`\${PLAYERO_WON}`);
break;
case PLAYERX_WON:
console.log(`\${PLAYERX_WON}`);
break;
case DRAW:
console.log(`\${DRAW}`);
break;
}
}

//console.log(ticTacToe())
``````
1 Like

Hey @pivot, hope you are ok.

I think you have just complete the exercise in a different way, you might not need to clone the board since your `getUserInput` function does return the index position of the array.

Try with this, instead of changing the value of the currentPlayer on that function, try to just calculate and return the next value.

``````function changePlayer(currentPlayer) {
let res = currentPlayer === 'X' ? 'O' : 'X';
//console.log(`changePlayer() returns: \${currentPlayer}`)
return res; // TODO: I could not get the value to pass back to the calling function
}
``````

Then in your `ticTacToe` function change to:

``````function ticTacToe() {

let board = ['','','','','','','','',''];
//let board = new Array(9).fill('');
let currentPlayer = 'O';

do {
// changePlayer(currentPlayer); <--TODO: The new changePlayer value is not passed back to this call. Why??
currentPlayer = changePlayer(currentPlayer)
board = makeMove(board, currentPlayer);
} while ( !isGameOver(board, currentPlayer) );

console.log('END')
}
``````

Hope it helps

Carlos Z

Hi @thecil

Great. Thanks for the response.

1. When is it necessary to clone an array when passing as an argument? How do you decide?

2. I made the change you suggested and changePlayer() works now. However, i don’t understand why passing back a reference to new variable (e.g., ‘return res;’) works, while modifying currentPlayer and passing that back doesn’t. Can you elucidate?

Thanks and best

1 Like

Can’t tell for sure, depends on the logic you need for it, although, is good practice to make a reference of a variable value, instead directly working on it, depend on the case, might be necessary or not.

There is some JS fundamental about this, the first time `currentPlayer ` variable is declared, its on the `tictactoe()` function, but it’s a local variable (`let`), is just accessible only by the function itself.

Keeping that in mind, each of the other declaration of `currentPlayer ` is not directly related to the same variable, although the name is the same. For example:

`function makeMove(board, currentPlayer){...}` and `function changePlayer(currentPlayer){...}` share the same argument name, inside their body logic, you will use the same argument name to work within it, but their not related at all in value’s terms.

The reference is just a calculation I made before returning the result, just to be sure my calculation work.

Carlos Z

I kind of took this exercise a step further and completed the tic tac toe game my own way.
Please see code for html, css, and js below

Index.html

``````<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JS Intro Playground</title>
<script src="./TicTacToe.js"></script>

<body>

<h1>Tic-Tac-Toe</h1>
<h4>Instructions (2 Players)</h4>
<p style="width: 500px;">Take turns claiming a spot on the game board.
Player 1 will be denoted by the letter X while Player 2 will be denoted by the letter O.</p>
<p style="width: 500px;">A player wins if they can claim all three spots either horizontally, vertically, or diagonally.
If the objective is not met before all spots are filled then the game results in a tie</p>

<!--Play Tic Tac Toe Button-->
<button id="playButton" style="background-color:lightgreen;" onclick="clearBoard()">Play Tic-Tac-Toe</button>
<p id="playerMsg"></p>
</div>

<!--HTML Gameboard-->
<p></p>
<table id="gameBoard" style="width:250px;">
<tr>
<td><button id="Tic1" class="ticButton" onclick="checkBoard('Tic1')" disabled></button></td>
<td><button id="Tic2" class="ticButton" onclick="checkBoard('Tic2')" disabled></button></td>
<td><button id="Tic3" class="ticButton" onclick="checkBoard('Tic3')" disabled></button></td>
</tr>
<tr>
<td><button id="Tic4" class="ticButton" onclick="checkBoard('Tic4')" disabled></button></td>
<td><button id="Tic5" class="ticButton" onclick="checkBoard('Tic5')" disabled></button></td>
<td><button id="Tic6" class="ticButton" onclick="checkBoard('Tic6')" disabled></button></td>
</tr>
<tr>
<td><button id="Tic7" class="ticButton" onclick="checkBoard('Tic7')" disabled></button></td>
<td><button id="Tic8" class="ticButton" onclick="checkBoard('Tic8')" disabled></button></td>
<td><button id="Tic9" class="ticButton" onclick="checkBoard('Tic9')" disabled></button></td>
</tr>
</table>

</body>
</html>
``````

TicTacToe.js

``````//Global Var
let currPlayer;
let gameOver;
let moveCount = 0;

function playGame() {

let gameOver = false;
clearBoard();
document.getElementById("playButton").innerHTML = "Reset Game";

document.getElementById("playerMsg").innerHTML = "Player 1 make your move!";

}

function clearBoard() {

// Reset currPlayer and game status
currPlayer = 'X';
gameOver = false;
moveCount = 0;

// Setup some user messages
document.getElementById("playButton").innerHTML = "Reset Game";
document.getElementById("playerMsg").innerHTML = "Player 1 make your move!";

// Enabled all buttons
document.getElementById("Tic1").disabled = false;
document.getElementById("Tic2").disabled = false;
document.getElementById("Tic3").disabled = false;
document.getElementById("Tic4").disabled = false;
document.getElementById("Tic5").disabled = false;
document.getElementById("Tic6").disabled = false;
document.getElementById("Tic7").disabled = false;
document.getElementById("Tic8").disabled = false;
document.getElementById("Tic9").disabled = false;

// Clear moves
document.getElementById("Tic1").innerHTML = '';
document.getElementById("Tic2").innerHTML = '';
document.getElementById("Tic3").innerHTML = '';
document.getElementById("Tic4").innerHTML = '';
document.getElementById("Tic5").innerHTML = '';
document.getElementById("Tic6").innerHTML = '';
document.getElementById("Tic7").innerHTML = '';
document.getElementById("Tic8").innerHTML = '';
document.getElementById("Tic9").innerHTML = '';

// Clear background
document.getElementById("Tic1").style.backgroundColor = "white";
document.getElementById("Tic2").style.backgroundColor = "white";
document.getElementById("Tic3").style.backgroundColor = "white";
document.getElementById("Tic4").style.backgroundColor = "white";
document.getElementById("Tic5").style.backgroundColor = "white";
document.getElementById("Tic6").style.backgroundColor = "white";
document.getElementById("Tic7").style.backgroundColor = "white";
document.getElementById("Tic8").style.backgroundColor = "white";
document.getElementById("Tic9").style.backgroundColor = "white";

}

function checkBoard(ticPressed) {

document.getElementById(ticPressed).innerHTML = currPlayer;
document.getElementById(ticPressed).disabled = true;
moveCount ++;

if (currPlayer == 'X') {
currPlayer = 'O';
document.getElementById("playerMsg").innerHTML = "Player 2 make your move!";
} else {
currPlayer = 'X';
document.getElementById("playerMsg").innerHTML = "Player 1 make your move!";
}

checkGameOver();

}

function checkGameOver() {

//Declare win conditions as array
const winCond1 = [document.getElementById("Tic1").innerHTML, document.getElementById("Tic2").innerHTML, document.getElementById("Tic3").innerHTML];  // Top-left horizontal win
const winCond2 = [document.getElementById("Tic1").innerHTML, document.getElementById("Tic4").innerHTML, document.getElementById("Tic7").innerHTML];  // Top-left vertical win
const winCond3 = [document.getElementById("Tic1").innerHTML, document.getElementById("Tic5").innerHTML, document.getElementById("Tic9").innerHTML];  // Top-left diagonal win
const winCond4 = [document.getElementById("Tic2").innerHTML, document.getElementById("Tic5").innerHTML, document.getElementById("Tic8").innerHTML];  // Top-middle vertical win
const winCond5 = [document.getElementById("Tic3").innerHTML, document.getElementById("Tic5").innerHTML, document.getElementById("Tic7").innerHTML];  // Top-right diagonal win
const winCond6 = [document.getElementById("Tic3").innerHTML, document.getElementById("Tic6").innerHTML, document.getElementById("Tic9").innerHTML];  // Top-right vertical win
const winCond7 = [document.getElementById("Tic4").innerHTML, document.getElementById("Tic5").innerHTML, document.getElementById("Tic6").innerHTML];  // Middle horizontal win
const winCond8 = [document.getElementById("Tic7").innerHTML, document.getElementById("Tic8").innerHTML, document.getElementById("Tic9").innerHTML];  // Bottom horizontal win

if ((winCond1[0] != "" && winCond1[0] == winCond1[1]) && (winCond1[1] == winCond1[2])) {
document.getElementById("playerMsg").innerHTML = "You Win!";
document.getElementById("Tic1").style.backgroundColor = "blue";
document.getElementById("Tic2").style.backgroundColor = "blue";
document.getElementById("Tic3").style.backgroundColor = "blue";
disableBoard();

} else if ((winCond2[0] != "" && winCond2[0] == winCond2[1]) && (winCond2[1] == winCond2[2])) {
document.getElementById("playerMsg").innerHTML = "You Win!";
document.getElementById("Tic1").style.backgroundColor = "blue";
document.getElementById("Tic4").style.backgroundColor = "blue";
document.getElementById("Tic7").style.backgroundColor = "blue";
disableBoard();

} else if ((winCond3[0] != "" && winCond3[0] == winCond3[1]) && (winCond3[1] == winCond3[2])) {
document.getElementById("playerMsg").innerHTML = "You Win!";
document.getElementById("Tic1").style.backgroundColor = "blue";
document.getElementById("Tic5").style.backgroundColor = "blue";
document.getElementById("Tic9").style.backgroundColor = "blue";
disableBoard();

} else if ((winCond4[0] != "" && winCond4[0] == winCond4[1]) && (winCond4[1] == winCond4[2])) {
document.getElementById("playerMsg").innerHTML = "You Win!";
document.getElementById("Tic2").style.backgroundColor = "blue";
document.getElementById("Tic5").style.backgroundColor = "blue";
document.getElementById("Tic8").style.backgroundColor = "blue";
disableBoard();

} else if ((winCond5[0] != "" && winCond5[0] == winCond5[1]) && (winCond5[1] == winCond5[2])) {
document.getElementById("playerMsg").innerHTML = "You Win!";
document.getElementById("Tic3").style.backgroundColor = "blue";
document.getElementById("Tic5").style.backgroundColor = "blue";
document.getElementById("Tic7").style.backgroundColor = "blue";
disableBoard();

} else if ((winCond6[0] != "" && winCond6[0] == winCond6[1]) && (winCond6[1] == winCond6[2])) {
document.getElementById("playerMsg").innerHTML = "You Win!";
document.getElementById("Tic3").style.backgroundColor = "blue";
document.getElementById("Tic6").style.backgroundColor = "blue";
document.getElementById("Tic9").style.backgroundColor = "blue";
disableBoard();

} else if ((winCond7[0] != "" && winCond7[0] == winCond7[1]) && (winCond7[1] == winCond7[2])) {
document.getElementById("playerMsg").innerHTML = "You Win!";
document.getElementById("Tic4").style.backgroundColor = "blue";
document.getElementById("Tic5").style.backgroundColor = "blue";
document.getElementById("Tic6").style.backgroundColor = "blue";
disableBoard();

} else if ((winCond8[0] != "" && winCond8[0] == winCond8[1]) && (winCond8[1] == winCond8[2])) {
document.getElementById("playerMsg").innerHTML = "You Win!";
document.getElementById("Tic7").style.backgroundColor = "blue";
document.getElementById("Tic8").style.backgroundColor = "blue";
document.getElementById("Tic9").style.backgroundColor = "blue";
disableBoard();

} else if (moveCount == 9) {
document.getElementById("playerMsg").innerHTML = "The game is a tie!";
}

}

function disableBoard() {

// Disable all buttons
document.getElementById("Tic1").disabled = true;
document.getElementById("Tic2").disabled = true;
document.getElementById("Tic3").disabled = true;
document.getElementById("Tic4").disabled = true;
document.getElementById("Tic5").disabled = true;
document.getElementById("Tic6").disabled = true;
document.getElementById("Tic7").disabled = true;
document.getElementById("Tic8").disabled = true;
document.getElementById("Tic9").disabled = true;

}
``````

indexCSS.css

``````table, th, td {
border: 1px solid black;
}

th, td {
text-align: center;
width: 20px;
height: 20px;
}

.ticButton {
background-color: white;
}
``````
6 Likes

Why do we need “const boardIndex = move - 1” to have a board Index. The instructor said it helps with simplifying things. But I don’t understand how.

Hi everyone,
I am sharing my code (I am not a developer and I am fully aware it is not the most optimised and the most elegant, still a lot to learn for sure):

``````function visualizeBoard(gameBoard){
let boardString=''
let gameBoardClone=[...gameBoard]
for (let index in gameBoardClone){
if (gameBoardClone[index]===null){
gameBoardClone[index]=parseInt(index)+1;
}

}
for (let i=0;i<=2;i+=1){
boardString=boardString+`\${gameBoardClone[3*i]} \${gameBoardClone[3*i+1]} \${gameBoardClone[3*i+2]}` + '\n';
}
return boardString;
}

function getUserInput(gameBoard, nextPlayerSymbol){
let boardString=visualizeBoard(gameBoard);
let coordinates=prompt(`User \${nextPlayerSymbol}, make your pick\n \${boardString}`);
return coordinates;
}

function isMoveValid(coordinates, gameBoard){
if (gameBoard[coordinates-1] ===null)
{
return true
}
else
{
return false
}
}

function makeAMove(gameBoard, nextPlayerSymbol) {
newgameBoard=[...gameBoard];
let coordinates='';
do {
coordinates = getUserInput(gameBoard,nextPlayerSymbol);
} while ( isMoveValid(coordinates, gameBoard)===false );
newgameBoard[coordinates-1]=nextPlayerSymbol;
return newgameBoard;
}

function isGameOver(gameBoard, currentPlayerSymbol) {
// 1. check if there is a winner
if (hasLastMoverWon(currentPlayerSymbol, gameBoard) )
{
// Write a message that last mover has won the game
alert(`Congratulations, \${currentPlayerSymbol} has won the game`);
return true;
}
else
{
// Return: winner/draw OR game is still in progress
let indic=0;
for (let index in gameBoard)
{
if (gameBoard[index]===null)
{
//console.log(gameBoard[index]);
indic=indic+1;
}
}
if (indic===0)
{
alert('That is a draw');
return true;
}
else
{
return false;
}
}
}

function ticTacToe() {

let gameBoard = new Array(9).fill(null);
let players = ['X', 'O'];
let currentPlayerSymbol=null

do {
if (currentPlayerSymbol==='X'){
currentPlayerSymbol='O'}
else
{currentPlayerSymbol='X'}
gameBoard = makeAMove(gameBoard, currentPlayerSymbol);
} while ( !isGameOver(gameBoard, currentPlayerSymbol) );

}
``````
1 Like
``````function renderBoard(gameBoard){
let s = '\n'
let b = 1
for(let i=0; i<gameBoard.length; i+=1){
if(gameBoard[i]===null){
s += `\${i+1}`
}else{
s += `\${gameBoard[i]}`
}
if(b === 3) {
s += '\n'
b = 0
}
b+=1
}
return s
}

function getUserInput(nextPlayerSymbol, gameBoard) {
return +prompt(`\${renderBoard(gameBoard)} place your move 1-9 : `)
}

function isMoveValid(coordinates, gameBoard) {
if(gameBoard[coordinates-1]===null &&
coordinates>0 && 10>coordinates)return true
else return false
}

function makeAMove(gameBoard, nextPlayerSymbol) {
// clone the game board before placing moves in it
let newGameBoard = JSON.parse(JSON.stringify(gameBoard))
let coordinates = 0
do {
coordinates = getUserInput(nextPlayerSymbol, gameBoard);
} while ( !isMoveValid(coordinates, gameBoard) );
// return newGameBoard;
newGameBoard[coordinates-1] = nextPlayerSymbol
return newGameBoard
}

function hasLastMoverWon(lastMove, gameBoard) {
let winnerCombos = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
for (let [i1, i2, i3] of winnerCombos) {
if (gameBoard[i1] === lastMove &&
gameBoard[i1] === gameBoard[i2] &&
gameBoard[i1] === gameBoard[i3]
) {
return true;
}
}
return false;
}

function isGameOver(gameBoard, currentPlayerSymbol) {
// 1. check if there is a winner
let lastMove = currentPlayerSymbol
if (hasLastMoverWon(lastMove, gameBoard) ) {
// Write a message that last mover has won the game
alert(`Congratulations, \${currentPlayerSymbol} has won the game`);
return true;
}
// 2. check if the board is full
if(gameBoard.includes(null)){
return false
}else{
alert('Game ended a Draw!')
return true
}
// Return: winner/draw OR game is still in progress
}

function ticTacToe() {
// State space
let gameBoard = new Array(9).fill(null);
let players = ['X', 'O'];
let nextPlayerIndex = 1;
let currentPlayerSymbol = players[nextPlayerIndex]

// Computations
do {
if(nextPlayerIndex === 0) nextPlayerIndex = 1
else nextPlayerIndex = 0
currentPlayerSymbol = players[nextPlayerIndex]
gameBoard = makeAMove(gameBoard, currentPlayerSymbol);
} while ( !isGameOver(gameBoard, currentPlayerSymbol) );

// Return value
// return undefined;
}
``````
1 Like
``````function getBoardString(gameBoard) {
let boardString = '';
for (let i= 0; i < gameBoard.lenght; i += 1){ boardString += `\${gameBoard[i] ?? i+1}`;
if (i % 3 === 2) { boardString += '\n'; }
} return boardString;
}
function getUserInput(nextPlayerSymbol, gameBoard) {
return prompt(`Board:\n\${getBoardString(gameBoard)} Enter \${nextPlayerSymbol}'s next move (1-9)`)
}

function isMoveValid(move, coordinates, gameBoard) {
let boardIndex = move - 1;
return (
typeof move === 'number' &&  move >= 1 && move <= 9 &&
gameBoard[boardIndex] === null
);
}

function makeAMove(gameBoard, nextPlayerSymbol) {
const newGameBoard = [...gameBoard];
let move = null;
do {
move = getUserInput(nextPlayerSymbol, gameBoard);
} while ( !isMoveValid(move, gameBoard) );
const boardIndex = move-1; newGameBoard[boardIndex] = nextPlayerSymbol;
return newGameBoard;
}

function hasLastMoverWon(lastMove, gameBoard) {
let winnerCombos = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];
for (let [i1, i2, i3] of winnerCombos) {
if (gameBoard[i1] === lastMove &&
gameBoard[i1] === gameBoard[i2] &&
gameBoard[i1] === gameBoard[i3]
) {
return true;
}
}
return false;
}

function isGameOver(gameBoard, currentPlayerSymbol) {
// 1. check if there is a winner
if (hasLastMoverWon(lastMove, gameBoard) ) {
// Write a message that last mover has won the game
alert(`Congratulations, \${currentPlayerSymbol} has won the game`);
return true;
}
// 2. check if the board is full//
if (gameBoard.every(element => element !== null)) {
return true;                                                                           }
return false;
}

function ticTacToe() {
// State space
let gameBoard = new Array(9).fill(null);
let nextPlayerIndex = 0;

// Computations
do {
nextPlayerIndex = nextPlayerIndex +1;
gameBoard = makeAMove(gameBoard, currentPlayerSymbol);
} while ( !isGameOver(gameBoard, currentPlayerSymbol) );

// Return value
// return undefined;
} type or paste code here
``````
``````function makeAMove(gameBoard, nextPlayerSymbol) {
// clone the game board before placing moves in it
let newGameBoard = JSON.parse(JSON.stringify(gameBoard))
``````
``````
function isGameOver(gameBoard, currentPlayerSymbol) {
// 1. check if there is a winner
if (hasLastMoverWon(lastMove, gameBoard) ) {
// Write a message that last mover has won the game
alert(`Congrats, \${currentPlayerSymbol} has won the game`);
return true;
``````

I was able to do these 2 parts so far. Very beginner and I will have to watch some more videos to continue

2 Likes
``````function ticTacToe() {
// State space
let gameBoard = new Array(9).fill(null);
let players = ["X", "O"];
let nextPlayerIndex = 0;

// Computations
do {
gameBoard = makeAMove(gameBoard, players[nextPlayerIndex]);
nextPlayerIndex === 0 ? (nextPlayerIndex = 1) : (nextPlayerIndex = 0);
\${gameBoard[0]}|\${gameBoard[1]}|\${gameBoard[2]}
\${gameBoard[3]}|\${gameBoard[4]}|\${gameBoard[5]}
\${gameBoard[6]}|\${gameBoard[7]}|\${gameBoard[8]}
`);
} while (!isGameOver(gameBoard, players[nextPlayerIndex]));
}

function makeAMove(gameBoard, nextPlayerSymbol) {
// clone the game board before placing moves in it
let newGameBoard = [...gameBoard];
let coordinates = "";

do {
coordinates = getUserInput(nextPlayerSymbol);
} while (!isMoveValid(coordinates, gameBoard));

newGameBoard[coordinates] = nextPlayerSymbol;

return newGameBoard;
}

function getUserInput(nextPlayerSymbol) {
return prompt("Which coordinate would you like to move");
}

function isMoveValid(coordinates, gameBoard) {
if (
coordinates >= 0 &&
coordinates <= 8 &&
!isSpaceTaken(coordinates, gameBoard)
) {
return true;
} else {
return false;
}
}

function hasLastMoverWon(lastMove, gameBoard) {
let winnerCombos = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6]
];

for (let [i1, i2, i3] of winnerCombos) {
if (
gameBoard[i1] === lastMove &&
gameBoard[i1] === gameBoard[i2] &&
gameBoard[i1] === gameBoard[i3]
) {
return true;
}
}
return false;
}

function isSpaceTaken(coordinates, gameBoard) {
if (gameBoard[coordinates] === null) {
return false;
} else {
return true;
}
}

function isGameOver(gameBoard, currentPlayerSymbol) {
let isBoardFull = false;
let lastMove = currentPlayerSymbol === "X" ? "O" : "X";
// 1. check if there is a winner
if (hasLastMoverWon(lastMove, gameBoard)) {
// Write a message that last mover has won the game
alert(`Congratulations, \${lastMove} has won the game`);
return true;
}
// 2. check if the board is full
if (!gameBoard.includes(null)) {
isBoardFull = true;
}

if (isBoardFull) {
return true;
}

return false;
}

// Initialize game

ticTacToe();

``````
2 Likes

About the TICTACTOE program: The small sections seemed fine for me, but the whole thing, with all the sections together does not work well! What is wrong?

function getUserInput(nextPlayerSymbol, gameBoard) {
return +prompt(`Board:\n\${getBoardString(gameBoard)} Enter \${nextPlayerSymbol}'s next move (1 - 9):`);
}

function isMoveValid(move, gameBoard) {
const boardIndex = move - 1;
return (
typeof move === “number” &&
move >= 1 && move <= 9 &&
gameBoard[boardIndex] === null);
}

function makeAMove(gameBoard, nextPlayerSymbol) {
const newGameBoard = […gameBoard];
let move = null;
do { move = getUserInput(nextPlayerSymbol, gameBoard);
} while (!isMoveValid(move, gameBoard) );
const boardIndex = move - 1;
newGameBoard[boardIndex] = nextPlayerSymbol;
return newGameBoard;
}

function hasLastMoverWon(lastMove, gameBoard) {
let winnerCombinations = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6] ]; for (let [i1, i2, i3] of winnerCombinations) { if (gameBoard[i1] === lastMove &&
gameBoard[i1] === gameBoard[i2] &&
gameBoard[i1] === gameBoard[i3])
{ return true; } } return false; }

function isGameOver(gameBoard, currentPlayerSymbol)
{ if (hasLastMoverWon(currentPlayerSymbol, gameBoard) ) {
alert(`Congradtulations \${currentPlayerSymbol} has won the game`);
return true;
} if (gameBoard.every(element => element !== null) ) {
alert(`Tie. Game over.`);
return true; } return false;
}

function getBoardString(gameBoard) {
let boardString = " ";
for (let i = 0; i < gameBoard.length; i += 1) {
boardString += `\${gameBoard[i] ?? i+1}`;
if (i%3 === 2) {
boardString += “\n”; }
return boardString;
}

function ticTacToe() {
let gameBoard = new array(9).fill(null);
let currentPlayerSymbol = null;
do {
currentPlayerSymbol = currentPlayerSymbol === “x” ? “o” : “x”;
gameBoard = makeAMove(gameBoard, currentPlayerSymbol);
} while (!isGameOver(gameBoard, currentPlayerSymbol) );
}

2 Likes

I had a little bit of trouble trying to figure everything out without watching the videos and seeing the full code.

I think if we had a flow chart for this project that showed how everything is tied together and able to see it visually, it would have clicked better. Still being fairly new to Javascript, it can be difficult to visualize how everything is supposed to flow together from scratch without a chart. Things didn’t start clicking until I took the full code and categorized it more into sections with additional comments.

Don’t mind me, I still have a lot to learn lol , but for example:

``````
//Element 1: The Game TicTacToe
//Element 1a: Gameboard
function ticTacToe() {
//...code...

//Element 1b: Players
let currentPlayerSymbol //...code...

//Element 1c: computations
do {
currentPlayerSymbol //...code...
gameBoard = makeAMove //...code...
} while !isGameOver //...code...
}

//==============================

// Element 2: Rules
// Element 2a: Ways to Win

function hasLastMoverWon //...code...

//Element 2: Rules
//Element 2b: Validating Moves

function isMoveValid //...code...

//==============================

//Element 3: User Actions
//Element 3a: Viewing The Current Board

function getBoardString //...code...

//Element 3: User Actions
//Element 3b: Input Prompts

function getUserInput //...code...

//==============================

//Element 4: Computing
//Element 4a: User Moves

function makeAMove //...code...

//Element 4: Computing
//Element 4b: Winner

function isGameOver //...code...

``````

One thing that I didn’t understand was why the Cancel button wasn’t working with the prompt messages. I tried searching on stack overflow and tried some things, but still couldn’t get the cancel button to work.

While messing around with the full code to breakdown and understand some functions, there were several times that I had to close the browser window to go back and adjust the code lol.

Does anyone know how to adjust the code to where the cancel button on the prompt will work?

1 Like