Created
January 6, 2010 05:21
-
-
Save kemiller/270034 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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