Time Travel is pretty neat, but if you want to cut your chops a little more you can try these additional challenges. They are in progressively more challenging order. For each of the following challenges, create a branch of your React Tic-Tac-Toe tutorial repo. Name the branch whichever challenge you are working on e.g. “Challenge 3”
Remove the loop from calculateWinner()
by making use of the regEx
const re = /^(?:(?:...){0,2}([OX])\1\1|.{0,2}([OX])..\2..\2|([OX])...\3...\3|..([OX]).\4.\4)/g;
re
will match any win-condition in a string of the flattened squares
array. I.e. in squares.join('')
. It is suggested you change empty squares to a -
character to indicate that a square is available rather than leave it empty.
Add a “reset” button to reset the game board to empty (all spaces available to move).
Automate player O. Define a findBestMove()
function that is called after every move by X that finds and sets the best move for player O given the current squares
. To find the best move you can implement for following strategy:
a. Check if there is an open square that will be a win for O, if so, move there.
b. Check if there is an open square that would block a win for X in the next turn.
c. If no winning or blocking move is found, prioritize a move to an open square in this order - center, corners, edges. You can represent this as an array of indices for the `squares` array `moveOrder = [4, 0, 2, 6, 8, 1, 3, 5, 7]` then search for the first empty square with an index in ` moveOrder`
Add a React component “Switch to Player {player}” button that when pressed will make a move using findBestMove()
for the current player then enable the user to play the opposite player (e.g. if player was X, the computer makes the best move for X and the player now plays O when an empty square is clicked). This should be made so that the player can swap at any time. If the player swaps every move without ever selecting a square the game should always end in a tie i.e. the computer is playing itself.
Implement findBestMove()
using the recursive minmax algorithm:
Create a function `minimax(board, depth, isMaximizing)` where:
board: The current state of the Tic-Tac-Toe board.
depth: The depth of the current node in the game tree (for recursion).
isMaximizing: A boolean flag indicating if it’s the maximizing player’s turn.
Inside this function:
a. First check for terminal conditions using `calculateWinner()`
i. If the game is won by 'X', return a positive score.
ii. If the game is won by 'O', return a negative score.
iii. If the game is a draw, return 0.
b. Next if **isMaximizing** is **true** (for current player), initialize **bestScore** to **-Infinity**. Otherwise initialize **bestScore** to **Infinity** (for opposing player)
c. Loop through empty cells on the board:
i. Make a move (either 'X' or 'O') on the current empty cell.
ii. Recursively call **minimax** with the updated board, incremented depth, and inverted **isMaximizing**.
iii. Update **bestScore** based on the recursive call (maximize or minimize).
iv. Undo the move (backtrack).
d. After the loop, return **bestScore** as the score for the current node.
Inside findBestMove()
call the minimax function to find the best move for the current player, and return the index in squares
the move with the highest score (this will be the optimal move).
Add a react component to input the size of the board (default 3x3). Generalize the board to use the number of rows and columns specified in this component. You will need to generalize the win conditions – for an nXm board, a win is any min(n,m) in a row. You may have a hard time generalizing the regEx re
for this. If so, loop over the rows and columns to search for a win. Experiment with letting the computer play itself when you change the board size. You will find that the game will no longer be a tie for many board sizes!
If you are careful about how you implement (6), you should be able to change the board size during play. In this case, choose a sensible way to map the previous board into the new board. Experiment with letting the computer play itself when you change the board size along the way!
By the time and date indicated in Laulima, submit this assignment via Laulima.
Your submission should contain a link to the GitHub repository you created. Make sure you include the complete URL so that I can click on it in my mailer. Note: the final commit to this repo must have been made before the submission time and date, otherwise you will not receive credit for this assignment.