Last week, I went through the solutions of the first three tasks at tic-tac-toe game tutorial. This time, I will cover up the rest. Please see the list of tasks again:
- Display the location for each move in the format (col, row) in the move history list.
- Bold the currently selected item in the move list.
- Rewrite Board to use two loops to make the squares instead of hardcoding them.
- Add a toggle button that lets you sort the moves in either ascending or descending order.
- When someone wins, highlight the three squares that caused the win.
- When no one wins, display a message about the result being a draw.
If you’d like to take a look at the initial article, please check this one out.
Task #4 Add a toggle button to sort the moves
I must admit that I struggled with this task a bit, because I couldn’t make my mind whether I should reverse the history in this.state or in a copy of history array. After my first implementation, I realised that things get fuzzy when you mingle with the state quite often. Because, making changes in the state side-effects the other functions inside Game.js. So, I threw away my first code and followed a brand new plan.
To start with, we definitely need a new state attribute for the sorting choice. I added it to my constructor with default value ascending.
We surely need a new function to toggle the sorting direction. This function will be called each time the user clicks on our sort button. So, I added toggleSort() inside Game component.
It is time to make some changes at rendering now. Since I do not want to reverse this.state.history, I get a copy of history and reverse it if sorting direction is descending. Also, please note that I also reverse the move value inside map(step, move).
Next change inside the render creates our toggle button. I have been too lazy to add some SVG images and work on CSS. Therefore, I used UTF-8 arrows 🙂 And do you remember the toggleSort() function? This is a good time to use it.
The last snippet is a piece of cake.
Ta taaa!!! Below is the outcome. If you’d like to see the commit, click here.
Task #5 When someone wins, highlight the three squares that caused the win.
As we go further, the tasks become a bit more challenging. So, this implementation requires changes on all components. I will follow bottom to top approach. That means we will see the implementation sequence as following:
Square component is called by Board and it is responsible of rendering the boxes. Assume that Square.js receives an additional prop parameter which is boolean. And this input helps rendering the given box with gray background color or not. See the code below:
And we certainly make this minor change inside index.css.
After these changes, we are done with the Square component. The next stop is Board. Let us first establish our previous assumption for the Square. Consider that renderSquare() function will pass the boolean to the Square.
And our createTheBoard() function still calls the previous function inside two for-loops. Assume that we have a handy function which figures out if the current square box is a winner box. So, call it accordingly.
And it is time to keep our promises again. Implement isWinnerSquare(). But see that we again make a new assumption here. Assume that Board will receive the winner coordinates from Game.js. If the coordinates are null, boolean is false. If current square box position is one of the winners, boolean is true. Simple huh?
“If you can’t write it down in English, you can’t code it.” – Peter Halpern
We are about to finalise the implementation. calculateWinner(…) knows which lines are the winner squares. Instead of returning the name of the winner, it’d better start sending an object which keeps both the coordinates and the winner name.
As the final touch, I edited the use of winner object and the result is astonishing 🙂 See the commit here.
Task #6 When no one wins, display a message about the result being a draw.
The last task is kind of easy. I see that the draw happens when all squares have some X or O value, but the calculateWinner function yet does not return a winner. So, I insert my check inside the if scope:
If you are curious about the commit, check it here. And below is the final output of our tic-tac-toe game:
As you see, the winner of the last game is neither X or O, because the real winner is YOU who puts effort to learn React.
Sercan Leylek / OSLO