Building An Unbeatable Tic-Tac-Toe Game In Ruby

February 14, 2015

Recently, I took up a programming challenge to build an unbeatable game of Tic-Tac-Toe using Ruby.

At first glance this seemed like it would be relatively simple. I mean, I learned how to play Tic-Tac-Toe in grade school, so how hard could it be to teach a computer to do the same? Much harder as it turns out.

Through a short series of posts, I’m going to describe my thought process while solving this problem and also how I implemented the solution. If you’d like to see the finished code, it’s available on my Github.

Understanding The Problem

Before trying to solve any problem, technical or otherwise, you first need to understand what is being asked of you and what the solution ‘looks like’.

The requirements for the Tic-Tac-Toe problem were the following:

Ok, so this helps clarify a few things. At the very least, I knew that I would need to implement the following:

As you can probably guess, these highlighted nouns became my Ruby classes.

Something To Look At

After breaking down the problem into a few smaller problems as described above, I got to work.

I find that when working on a new project, I can easily begin to feel overwhelmed with how much I have left to do. The best way that I’ve found to combat this is to get something ‘presentable’ as soon as possible. The sooner I can actually see something on the screen, the more motivated I become.

For my Tic-Tac-Toe game, the obvious first step was to be able to print the game board, so I started with the ‘Board’ class.

Since a Tic-Tac-Toe game board is a 3x3 grid, my first instinct was to create 2 arrays, one for the rows and one for the columns. After a little tinkering, I realized that while this models the way that we think about a game board, it didn’t translate very naturally to code and that there was actually a simpler way.

Create a single empty Array of size 9.

# board.rb

# [0][1][2]
# [3][4][5]
# [6][7][8] 
def initialize
  @spaces = Array.new(9)
end

The next step was to be able to get the board to print to the screen and actually look like a real Tic-Tac-Toe board.

Again, after some trial and error, I came to this solution:

# board.rb

def to_s
  output = ""
    0.upto(8) do |position|
      output << " #{spaces[position] || position} "
      case position % 3
      when 0, 1 then output << "|"
      when 2 then output << "\n-----------\n" unless position == 8
    end
  end
  output
end

This ‘overrides’ Board’s to_string method and does the following:

  1. Creates an empty string
  2. Iterates through each position from 0 to 8 (since array’s are 0-indexed)
  3. For each ‘position’:
    • If the space is occupied, it appends the contents of that space (the player’s piece)
    • Otherwise, it just appends the position number itself
    • If the position is in the first or second column, then it appends a vertical line representing a column
    • Otherwise, if the position is the last in it’s row, it appends the row seperator (unless it is the final row)
  4. Finally, returns the built string

If you ran this code by creating a new Board object and called to_s on it (which ‘puts’ will do implicitly), you would get the following:

puts Board.new

 0 | 1 | 2
-----------
 3 | 4 | 5
-----------
 6 | 7 | 8

Success! We’ve got something on the screen that actually looks like a Tic-Tac-Toe board.

Next time we’ll implement the Human player and the Game engine. Thanks for reading and let me know what you think in the comments!

Update: Part 2 is available here

Did you find this content helpful?


Let me send you more stuff like this! Unsubscribe at any time. No spam ever. Period.


Subscribe to MarkPhelps.me

* indicates required

Discussion, links, and tweets

Mark Phelps

I'm a Software Engineer in Durham, NC. I mostly write about Go, Ruby, and some Java from time to time.