Created
June 15, 2013 03:18
-
-
Save marksim/5786740 to your computer and use it in GitHub Desktop.
My favorite TicTacToe implementation yet. Doing testing right results in better design.
This file contains 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
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 [email protected]? | |
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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/