Building An Unbeatable Tic-Tac-Toe Game In Ruby Pt. 2
This is the second part of a short series describing how I designed and built a unbeatable game of Tic-Tac-Toe using Ruby. Check out part 1 here.
Getting the Game Going
Last time we left off with a working game board able to display itself as well as any ‘players’ that occupy any of it’s spaces. Now it’s time to put that board into action and build something that resembles an actual game.
In order to figure out how our game ‘engine’ should work, lets first come up with an algorithm describing the normal flow of a Tic-Tac-Toe game.
Tic-Tac-Toe Gameplay Algorithm:
Player ‘X’ goes first
- Player ‘X’ places their marker on an empty board piece
- Next, check to see if player ‘X’ won by getting three ‘X’s in a row
- If not, check to see if there is a tie (a full board with no three of a kind in a row)
- If neither of these conditions are true, repeat these steps with player ‘O’ instead of player ‘X’
Because our brains are so good at noticing patterns, we normally don’t explicitly perform steps 2 & 3 after each turn. Instead, we can usually determine before we even make our move whether or not it’s possible for us to win, lose or tie (more on this in the next post).
Now that we have an algorithm, lets turn that into ‘psuedocode’.
Now here is the cool part: If you check out the actual code on Github, you’ll notice that this ‘psuedocode’ is almost exactly the same as the actual Ruby code!
Delegators, Mount Up
Looking back at the code, you’ll notice that there are a lot of calls to a UI object such as ‘turn’, ‘won’, ‘tie’, ‘thinking’, ‘banner’, etc. in order to print and receive information from the user. Initially, I inlined these methods right there in the game engine. This worked, however it made the code look very cluttered and also seemed to violate the separation of concerns principle to have the game engine also deal with UI. For that reason, I extracted everything having to deal with input or output into a UI class, which is where the above methods live.
Well, that last part is only half true. If you look at the UI code, you’ll notice that UI does not actually implement any logic in these methods. Instead, it passes every call to a ‘delegate’ object along with the calling parameters. The reason I took this route is that the UI is really an ‘abstract’ concept since there are multiple ways that the game could communicate with a user.
For example: Lets say that after seeing our console game, our boss asked us to make a Tic-Tac-Toe Service for our enterprise business clients to enjoy. The only thing that would need to change for this implementation would be the ‘user interface’. Instead of printing and receiving input directly to and from the console, our service might communicate via HTTP using JSON as our messaging format. All we would need to do would be to create a HTTP delegate that could accept and respond with JSON and wire up our UI class to use this new delegate. None of the underlying calling code would have to change.
Another benefit of having all UI methods in a single class or set of classes is that it helps keep our dependencies isolated to only those classes in which they are actually required. For example, in the IO::Console class, I make use of the awesome ‘colorize’ gem which adds the ability to ‘colorize’ console output using ANSI escape sequences. This makes our game a little more exciting and also easier to read on the normally ‘black and white’ console.
Here’s what the final result looks like:
That’s about it for this post. Next time I’ll go over implementing the actual players, both human and ‘AI’.
As always, any feedback in the comments or on Twitter is greatly appreciated!
Update: Part 3 is available here