Project Ramon

A learning journey from a Ruby noob perspective

Ruby TicTacToe: The Unravel


Before we get to refactoring our tic-tac-toe game from the past couple of posts, I wanted to walk through my understanding of how Marek Publicewicz explained in his comments how to both think about and create dynamically sequenced rules, because this was challenging for me to understand at first.

The rules for winning a TicTacToe game wherein a player can enter a set of coordinates, in any order, and have the horizontal, vertical, or diagonal winner rules to return true when any one of these winner patterns match player 1’s (represented as X) or player 2’s (represented as O) list of plays (represented as @plays['X'] && @plays['O']) respectively.

Dynamically Sequence Our Rules

Just as a refresher, here are my statically sequenced rules, they don’t take into account that player 1 and player 2 may enter a series of moves that match up for a win, but if the moves aren’t in the order I defined them, no winner is generated. Here’s a quick peak: tictactoe2_img1 Again, the difference between the above illustration, and the one below, is this. If the TicTacToe program used my set of winning rules, a player could enter in 3 symbols (X’s or O’s) that fill the left column of the tic-tac-toe board and still not trigger the @vertical_winner rule. This can easily happen if player1 say, enters the first move as [0, -1], or any other first move coordinate besides [1, -1].

The winning rules we just looked at are the ones dynamically sequenced by Marek. Now we can replace the contents of the winner? method. Here is the way I have it currently written: tictactoe2_img2 And here’s how we’ll replace the statically sequenced rules with some dynamic ones. tictactoe2_img3

Checking Things Out with PRY

Now we can run this script to see if we have a working game. Here’s an illustration of a TicTacToe object being set up inside of PRY. tictactoe2_img4 To save myself a little time, I decided to get player 1 and player 2 going when a new game is created. Basically player 1 is cued up to make the final move of [1, 1] for a top row, horizontal victory! Player 2 is in a similar situation, with the moves of [-1, 0] & [0, 0] already completed as the game begins. Before we decide to allow player 1 or player 2 to make their winning move, lets check out the instance methods of this game to ensure that they are firing correctly. Here’s a demonstration. tictactoe2_img5

Oops! It appears as though all but one of our instance methods were invoked as expected. horizontal? is already returning true, why is this? Lets look at our horizontal? instance method a little closer. To do this, I’ll set us up another @plays hash in a separate PRY session, and then pull out @plays['X'].map(&:last).uniq.size == 1 to see whats going on.

tictactoe2_img6 Above, I’m just defining the @plays hash and then adding the coordinates for move #1.


I’m  grabbing the last element in the array nested inside of the @plays['X'] hash and then finding the size of this new array’s unique values. To put it a different way, I’m taking player 1’s only move so far, the coordinate [-1, 1], copying it into a new array with the .map method, then ascertain the .unique .size of the new array. No matter if we had one move or three, since our horizontal pattern from yesterday was created from understanding that 3 horizontal moves on the same row, the unique size will always be one. A couple of illustrations to explain this better.

tictactoe2_img8Here, I’m just adding a top-row winning set of coordinates, by adding two additional moves to player 1.

And here,


demonstrates the result of our experiment. Player 1 after making one move, and player 1 after making all three moves, still returns the result of 1 .uniq .size. Which means that after using .map to create a second array that contains only the :last element in each of the original arrays. Since all of the second elements for a horizontal line has to be the same, horizontal? will give us false positives unless we can be sure that our @plays = { 'X' =>; [[-1, 1], [0, 1], [1, 1]] } hash containing all of player 1’s moves can be counted before we check for a unique size.


Adding the conditional if @plays['X'].size >= 3 && horizontal? first checks to ensure that player 1 has made at least three moves before winner? can return true. This offers a clue as to why our call to t.winner? returned false like we expected, while calling horizontal? returned true. I’ll show that slide one more time as a refresher. tictactoe2_img5

Lets check out our program now, and see if we can get player 1 to win. tictactoe2_img11


I remember Marek mentioning that I had this problem. Player 2 still has to make a move before my program declares a winner.So a winner is generated, and its the correct player, but I still have a bug to chase down. In my next post, I’ll share how I was able to eliminate this unexpected functionality, and I will also wrap up this series with a refactor of a working TicTacToe game.

Stay tuned…


Categories: Newbie, Ruby

Tags: ,

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s