Skip to content

Instantly share code, notes, and snippets.

@marksim
Created June 15, 2013 03:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marksim/5786740 to your computer and use it in GitHub Desktop.
Save marksim/5786740 to your computer and use it in GitHub Desktop.
My favorite TicTacToe implementation yet. Doing testing right results in better design.
require 'rspec'
class TicTacToe
def initialize
@x_moves = []
@o_moves = []
end
def play space
return false unless (1..9).include?(space)
return false if occupied?(space)
next_player << space
true
end
def at space
return 'X' if @x_moves.include?(space)
return 'O' if @o_moves.include?(space)
space
end
def draw?
full? && !won?
end
def winner
[[1, 2, 3], [4, 5, 6], [7, 8, 9],
[1, 4, 7], [2, 5, 8], [3, 6, 9],
[1, 5, 9], [3, 5, 7]].each do |row|
return 'X' if row & @x_moves == row
return 'O' if row & @o_moves == row
end
nil
end
def won?
!winner.nil?
end
def finished?
won? || draw?
end
private
def occupied? space
(@x_moves + @o_moves).include?(space)
end
def next_player
@x_moves.count == @o_moves.count ? @x_moves : @o_moves
end
def full?
(@x_moves.count + @o_moves.count) == 9
end
end
describe "TicTacToe" do
let(:game) { TicTacToe.new }
it "allows a player to play in an unoccupied space" do
expect(game.play(5)).to be_true
end
it "does not allow a player to play in an occupied by X" do
game.play(5)
expect(game.play(5)).to be_false
end
it "does not allow a player to play in an occupied by O" do
game.play(5)
game.play(6)
expect(game.play(6)).to be_false
end
it "does not allow a player to play off the board" do
expect(game.play(10)).to be_false
end
it "alternates between players" do
game.play(5)
game.play(6)
expect(game.at(5)).to eql('X')
expect(game.at(6)).to eql('O')
end
it "lets x go first" do
game.play(5)
expect(game.at(5)).to eql('X')
end
it "knows when a draw game occurs" do
expect(game).not_to be_draw
[5, 6, 7, 8, 9, 1, 2, 3, 4].each {|m| game.play(m) }
expect(game).not_to be_won
expect(game).to be_draw
end
describe "winning" do
it "happens when 3 in a row" do
[1, 6, 2, 7, 3].each {|m| game.play(m) }
expect(game).to be_won
expect(game.winner).to eql('X')
end
it "happens when 3 in a col" do
[1, 9, 4, 8, 7].each {|m| game.play(m) }
expect(game).to be_won
expect(game.winner).to eql('X')
end
it "happens when 3 in a diagonal" do
[1, 5, 2, 3, 9, 7].each {|m| game.play(m) }
expect(game).to be_won
expect(game.winner).to eql('O')
end
end
end
class TicTacToeCLI
def initialize(game)
@game = game
end
def loop
while !@game.finished?
print_board
m = gets
@game.play(m.to_i)
end
puts result
end
def result
@game.won? ? "#{@game.winner} wins" : "draw"
end
def print_board
puts "\n"
(1..3).each do |r|
(1..3).each do |c|
print "#{@game.at((r-1)*3 + c)} "
end
print "\n"
end
end
end
game = TicTacToe.new
TicTacToeCLI.new(game).loop
@marksim
Copy link
Author

marksim commented Jun 15, 2013

about 3/5ths of the amount of total code as this (and this one has a UI):

http://blog.dev/blog/2013/04/25/tic-tac-toe-tdd/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment