Project Ramon

A learning journey from a Ruby noob perspective

My TicTacToe Failure: Airpair Mentor to the Rescue

tictactoe1_header_img

Today I’ll be covering my interpretation of a TicTacToe program that was corrected and then refactored by Marek Publicewicz. He’s been a highly valued supporter of mine since I’ve started learning at AirPair, sharing both insights and offering precise constructive critiques of my newbie programming works.

First lets cover my TicTacToe program that I wrote about Friday, and point out the pieces of flawed functionality that I had in my code base.

tictactoe_img1

My Smelly & Incorrect Code

There are several places in the above TicTacToe class where I made pretty blatant errors, here is list I received in the form of a comment left by Marek Publicewicz.

  1. @horizontal_rules and @vertical_rules need to be swapped.
  2. The @vertical_winner‘s middle row is wrong
  3. The three winner? rules as a whole don’t take into account the fact that a user’s move sequence could be different than what is stored in the instance variables because I set them up as literal sequences. I interpret this to mean that a user has to not only have 3 X‘s or 3 O‘s in a row (either diagonally, vertically or horizontally). But as these instance variable’s values stand, a user would have to make moves in the exact order as I defined them. Marek’s version goes about making these move sequences dynamic in nature. Which adds the benefit of checking for a winning sequence no matter which order the winning moves occur.
  4. The current winner? method’s conditional in my code returns true only after both players make their move. Marek points out that if player 1 aka @plays['X'] makes a winning move, the program should rightfully exit the conditional and return true (which makes the play method’s if winner? send a string saying "#{ @current_player } wins!" for @plays['X'] upon a move satisfying a horizontal, vertical, or diagonal winning rule.

Lets endeavor to walk through the necessary corrections in my TicTacToe program (the first illustration in this post), in order to make sure that we aren’t needing to change any behavior during my inevitable refactor phase thats coming up in my next post. I think an illustration of an actual TicTacToe board with the coordinates written in each box will be beneficial as we try and visualize our problem space. tictactoe_img2 @horizontal_rules & @vertical_rules need to be swapped: Here’s a look at my three rules to win a TicTacToe game.

Based on Marek’s accurate critique and also the TicTacToe board above, we can see that @horizontal_winner and @vertical_winner should indeed be swapped for one another.

Here’s the code after this revision:

Next up is a correction to the newly appropriated @horizontal_winner instance variable. On line 2 in the above illustration we can see this error. [[1, 0], [0, 0], [0, -1]] needs to be changed to [[1, 0], [0, 0], [-1, 0]] for a right-to-left, middle row, horizontal win to occur. Taking another look at our TicTacToe board we can see that tictactoe_img2 the 2nd element in the coordinate arrays (i.e. [-n, 1] [n, 1] [n, 1] / [-n, 0] [n, 0] [n, 0] / [-n, -1] [n, -1] [n, -1] always references one of three rows on our board. And as we can now obviously reason, the first element in each of @play's  subarrays represents a coordinate’s column.

Third on the list of errors are my literally sequenced rules that don’t account for a winning move sequence that fails to declare a winner if a user doesn’t enter a move in the exact order as I defined them in the three winner instance variables.

This was the exact place I was stuck on Friday. I’ve done some poking around in the documentation here and also here, but honestly did not grasp much of an understanding until I read over the answers in this StackOverflow post. And as I was in the midst of writing this post, Marek left a comment providing an easier to understand way to reason through creating dynamically sequenced horizontal, vertical and diagonal winning rules.

Here’s how he explains it:

These ‘map’ clauses really illustrate the following questions: – what makes a sequence of moves, where each move is defined as a pair of [x, y] coords, be oriented horizontally i.e. being positioned in the same row, knowing that they are guaranteed to be unique (no pair may appear more than once – since it’s not possible to place anything on the field that already has been occupied before) – same for the ‘vertical’ orientation – the cryptic array.map(&:first) is really a short version ‘create an array from an array, taking 1st element from each element’ which can also be written as: array.map { |pair| pair.first } Example: [ [‘a’, ‘b’], [‘c’, ‘d’], [‘e’, ‘f’] ].map { |pair| pair.first } => [“a”, “c”, “e”]

I interpret this as:

What makes a sequence of moves horizontally, vertically or diagonally oriented?

How can we ensure that all @plays['X'] && @plays['O'] have an ability to only make a play on an open TicTacToe square (i.e. How to make sure neither player can enter a move thats already been made).

By taking the first element from every @plays['X'] || @plays['O'] (i.e. @plays['X'] << [-1, 1], [0, 1], [1, 1] ) and doing the same for the second element from every @plays['X'] || @plays['O'] (i.e. @plays['X'] << [-1, 1], [0, 1], [1, 1]), we can then perform our checks for a game winner.

Here’s how Marek explains our winning rule checks:

In general think about array.map(&:method1) as a convenient way to take some existing array (enumerable actually), invoke the ‘method1′ method on each element and create a new array from results of these calls. Once we can extract 1st (x) coords or 2nd (y) coords, we can then try performing the checks like: – how many distinct ‘x’ coordinates are used in a sequence (hint: vertical pattern means all moves have the same ‘x’ value) – how many distinct ‘y’ coordinates are used in a sequence (hint: horizontal pattern means all moves have the same ‘y’ value) – array.uniq.size returns number of unique elements in an array (or more precisely: size of a new array, which consists only of unique elements of the original array)

I interpret this as a better (i.e his dynamically sequenced versus my literally sequenced) way to write the winner rules. Since he gives examples in his above statement for defining both vertical and horizontal patterns, I’ll attempt to make a distinction for a vertical pattern.

A vertical winning coordinate will be exactly 3 coordinates that contain [-1, 1] && [1, -1] || [1, -1] && [-1, 1] plus the center middle coordinate of [0, 0].

And for the other diagonal winning pattern it would be [1, 1] && [-1, -1] || [-1, -1] && [1, 1] with the center middle coordinate again being [0, 0].

In closing, if you are interested in seeing Marek’s elegantly written TicTacToe program, and don’t want to wait until my next post,  you can have a peak here.

If you are ever in need of a mentor on AirPair, or are interested in hiring a developer with over a decade of experience, check out his developer page. On it, you’ll see that I’m not even close to the only person who finds communicating with him a huge benefit!

Next up.  TicTacToe, the Refactor.

Stay tuned…

Advertisements

Categories: Ruby

Tags: , , ,

3 replies

  1. Hello there, just became aware of your blog through
    Google, and found that it is truly informative. I’m gonna watch out for brussels.
    I’ll be grateful if you continue this in future.
    Many people will be benefited from your writing. Cheers!

  2. You can certainly see your skills in the work you write.
    The arena hopes for even more passionate writers such as you who are not afraid to say how they believe.
    Always go after your heart.

  3. Hello Ferne and Denice!

    Thank you both for the kind words.

    I am planning on continuing with my latest post sometime early next week.

    All the best,

    Ramon

Leave a Reply

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

WordPress.com Logo

You are commenting using your WordPress.com 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 )

Google+ photo

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

Connecting to %s