Estimated Time: 15-35 Hours
I had the most trouble with simply keeping changes in user input constant. So when switching between 1 player and 2 player modes, there was no cross talk and a clean refresh was made. The same when switching icon styles.
I also had some difficulty getting the turns to rotate. So that if player 1 goes first, then next game player 2/computer goes first.
Don’t let not knowing something get to you. Just Google it or check Stack Overflow. Also if something isn’t working, check that:
- Your settings are what you think they are. Log them to the console and switch back and forth to make sure they work correctly
- Your computer decision making can only place moves in places that arent already played. The same goes for players. Make a way to disable input to already selected squares.
- You spelled everything correctly. Even 1 wrong letter can throw it all off.
- You have no errors in the developer console. If on chrome hit Ctrl+Shift+I to see the console log.
I feel I must first state that I chose to add an option to allow 2 players. I know this was not part of the requirements, but I felt like it was needed. This greatly complicated my design, and only suggest doing this if you are up for the challenge.\
Design and Layout
With that said, I began this project by deciding what my layout would look like. I decided to go with a top row that would collapse on smaller screens. In this row would be a settings button, the title of the game, and a scoreboard.
For the setting button, I had a click trigger a popup modal that I made similar to the bootstrap modal, but I did not need bootstrap anywhere else in my project so I used my own. The settings would be simple. A header section with a title and an X button, and a body section with two setting choices.
I would allow the switch between Player vs. Computer and Player vs. Player, and let player 1 choose between being X or O.
Back to the main screen, my scoreboard is a table showing what pieces the current players are, who’s turn it is, and how many games they have won in the current session.
For my tic tac toe board, I thought about making 9 square divs, but aligning those would not be as easier as making a 9×9 table. So, I used a table and only applied borders to the inner columns and rows.
When adding a new piece, there is a slight fade-in effect that I feel adds to the design. As for the overall design, I went with a gray and red theme, using a background texture that has some white speckling to break apart what would otherwise be a monotonous color.
Okay, so this is where things get interesting. Because I decided to include a player vs player option, I had a whole extra bit of functionality to add.
For most of my FCC projects so far, I basically could skate by and just use purely functional programming to get done what I needed. But, I realized pretty quickly that this was going to have a lot of moving parts, and I should probably start using objects. So, I started to use ES6 classes.
I had to do a little reading, but it was very easy to get the hang of once I started working with them. I ended up making classes for each player, for the game board, and for the settings. To supplement these I added a few functions that I called on. For the most part, this made things a lot easier.
The only times I had trouble were when I had code between the computer logic and player vs player logic that was very similar, but too different to add into a single function easily. So I ended up coding both of those in, which made my code a little bit lengthier.
So I coded most of everything up, I finished the player vs player first, added all the little nuances like updating the score, changing the currently active player, etc…, but then I was left with making the computer logic.
Without much hesitation, I decided I could just leverage the win-case checking I made for the player vs player section to my advantage. This checking was just a simple method that checks if any of the 8 combinations that could win based on a numbering scheme shown below were present.
So, for example, if I run the method for a specific player (which has access to which pieces are currently down by each of the players) and it sees that player has played on spaces 1,2 and 3, then that player wins.
I took this and transferred it to my computer logic, so that each time the computer plays, it runs this check as well, making it very easy to enact the end case.
As for getting the computer to play decent choices, I decided on the following. First I had the computer randomly select from the remaining available pieces on the board, and play at one of those spaces. Once I got this functionality working, albeit was a very bad computer, I added two more cases to run before deciding on a random location.
The first case uses the previously mentioned win-case checking method. Instead of checking for a win, I modified it to check to see if there are any moves that would lead to a winning scenario, as this would be the most important and logical move to make.
The second case is similar to the first, but it checks if the computer’s opponent has any moves that would allow them to move, and if they do, it places its piece in that spot. This would be the second most important place to play (Note: this doesn’t help if there are two places in which the opponent could win, a loss is inevitable at that point so the computer just plays in one of them).
If neither of these cases applies, the computer simply plays at random. Due to this logic, the computer typically plays randomly for the first two moves it makes, which allows for the user to beat the computer if playing a certain algorithm. But, if all you are to do is play an algorithm that wins every time, where is the fun in that. I wanted my computer to feel like a human, not an invincible bot programmed to know every winning scenario.
With this method, I ended up being able to accomplish designing a computer logic in very little lines of code.
This was a very enjoyable project for me, and I learned a lot. I began getting experience in ES6 classes and learning how to combine functional with object-oriented programming. In the end, there were a lot of little nuances to hammer out, but I think I finally arrived at a fluid, bug-free game.
(I know I went out of order and wrote about this project before many of the other ones, but this was fresh on my mind and wanted to get everything right while I remembered what I did)