Skip to content

Instantly share code, notes, and snippets.

@kemiller
Created January 6, 2010 05:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kemiller/270034 to your computer and use it in GitHub Desktop.
Save kemiller/270034 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
# Represents the state of a single game, including the board, the current player,
# and the winner, if any.
#
class Game
def initialize(first_player)
@current_player = first_player
@board = Board.new
@state = :continue
end
# Make a move for the current player.
def move(space)
@board.move(@current_player, space)
@state =
if @board.winning?
:win
elsif @board.full?
:draw
else
@current_player = @current_player.opponent
:continue
end
rescue InvalidSpaceException
:continue
end
def to_s
"\n#{@board.to_s}\n\n" +
case @state
when :win
"#{@current_player} Wins!\n"
when :draw
"It's a Draw!\n"
when :continue
"Select a square, #{@current_player}: "
end
end
def main
while @state == :continue
print self
move(gets.to_i)
end
print self
end
end
# Represents the current state of a single space on the board
class Space
attr_reader :player, :number
def initialize(number)
@number = number
end
def place(player)
raise InvalidSpaceException if occupied?
@player = player
end
def occupied?
@player != nil
end
def ===(other)
(@player == other.instance_variable_get(:@player)) && self
end
def to_s
if occupied?
" #{@player} "
else
"(#{@number})"
end
end
end
class InvalidSpaceException < Exception; end
# Represents the 3x3 tic-tac-toe grid
class Board
def initialize
@board = default_board
@slices = rows + columns + diagonals
end
def move(player, desired_space)
space(desired_space).place(player)
end
def winning?
@slices.detect { |s| s.winning? }
end
def full?
@board.map { |s| s.occupied? }.reduce(:&)
end
def to_s
rows.join("\n---+---+---\n")
end
private
def default_board
(1..9).map { |i| Space.new(i) }
end
def space(n)
raise InvalidSpaceException if n > @board.size || n < 1
@board[n-1]
end
def row_arrays
[@board[0,3], @board[3,3], @board[6,3]]
end
def rows
@rows ||= row_arrays.map { |row| Slice.new(row) }
end
def columns
@columns ||= row_arrays.transpose.map { |col| Slice.new(col) }
end
def diagonals
@diagonals ||=
[ Slice.new(space(1),space(5),space(9)),
Slice.new(space(7),space(5),space(3)) ]
end
end
# Represents a 3-in-a-row subset of the spaces on the board
class Slice
def initialize(*spaces)
@spaces = spaces.flatten
end
def winning?
(result = @spaces.reduce(:===)) && result.player
end
def to_s
@spaces.join("|")
end
end
# Players
X = 'X'
O = 'O'
def X.opponent; O; end
def O.opponent; X; end
Game.new(X).main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment