Move 6 / 16. From red to green in 33 seconds. Comments 0 comment(s). 2 modification(s). Kata Summary
# This exception will be thrown when an illegal ttt move occurs: = # This exception will be thrown when an illegal ttt move occurs:
# - Violation of correct order of moves: X, O, X... = # - Violation of correct order of moves: X, O, X...
# - Playing on an already occupied field = # - Playing on an already occupied field
class IllegalMoveException < Exception = class IllegalMoveException < Exception
end = end
=
=
=
========== next file ========== = ========== next file ==========
=
class Player = class Player
  NONE = '-' =   NONE = '-'
  X = 'X' =   X = 'X'
  O = 'O' =   O = 'O'
end = end
=
=
========== next file ========== = ========== next file ==========
=
class Printer = class Printer
=
  # Prints a single line of three-line ttt board. =   # Prints a single line of three-line ttt board.
  # Format is "---" for empty board, =   # Format is "---" for empty board,
  # "X-O" for line with two stones. =   # "X-O" for line with two stones.
  def print_line line =   def print_line line
    puts line =     puts line
  end =   end
=
end = end
=
=
========== next file ========== = ========== next file ==========
=
require 'player' = require 'player'
require 'illegal_move_exception' = require 'illegal_move_exception'
=
class GameOverCallback = class GameOverCallback
=
  # Called whenever the game is finished, i.e. if a player wins or if the board is full =   # Called whenever the game is finished, i.e. if a player wins or if the board is full
  def the_winner_is winner =   def the_winner_is winner
    puts "Winner is #{winner}" =     puts "Winner is #{winner}"
  end =   end
=
end = end
=
class TicTacToe = class TicTacToe
=
  def initialize =   def initialize
    @cells = [] =     @cells = []
    (0..8).each{ @cells << Player::NONE } =     (0..8).each{ @cells << Player::NONE }
    @next_player = Player::X =     @next_player = Player::X
  end =   end
=
  # Output current board state to printer. =   # Output current board state to printer.
  # Print each board line separately starting from top row =   # Print each board line separately starting from top row
  def print_board printer =   def print_board printer
    printer.print_line @cells[0..2].join =     printer.print_line @cells[0..2].join
    printer.print_line @cells[3..5].join =     printer.print_line @cells[3..5].join
    printer.print_line @cells[6..8].join =     printer.print_line @cells[6..8].join
  end =   end
=
  # Play X stone on board. =   # Play X stone on board.
  # Trigger game over callback if one player wins or if board is full after this move. =   # Trigger game over callback if one player wins or if board is full after this move.
  # fieldIndex ranges from 1 to 9 =   # fieldIndex ranges from 1 to 9
  # throws IllegalMoveException =   # throws IllegalMoveException
  def play_X fieldIndex =   def play_X fieldIndex
    if (@cells[fieldIndex-1] != Player::NONE) or (@next_player != Player::X) then raise IllegalMoveException end =     if (@cells[fieldIndex-1] != Player::NONE) or (@next_player != Player::X) then raise IllegalMoveException end
    @cells[fieldIndex-1] = Player::X =     @cells[fieldIndex-1] = Player::X
    @next_player = Player::O =     @next_player = Player::O
    check_end_of_game =     check_end_of_game
  end =   end
=
  # Play O stone on board. =   # Play O stone on board.
  # Trigger game over callback if one player wins or if board is full after this move. =   # Trigger game over callback if one player wins or if board is full after this move.
  # fieldIndex ranges from 1 to 9 =   # fieldIndex ranges from 1 to 9
  # throws IllegalMoveException =   # throws IllegalMoveException
  def play_O fieldIndex =   def play_O fieldIndex
    if (@cells[fieldIndex-1] != Player::NONE) or (@next_player != Player::O) then raise IllegalMoveException end =     if (@cells[fieldIndex-1] != Player::NONE) or (@next_player != Player::O) then raise IllegalMoveException end
    @cells[fieldIndex-1] = Player::O =     @cells[fieldIndex-1] = Player::O
    @next_player = Player::X =     @next_player = Player::X
    check_end_of_game =     check_end_of_game
  end =   end
=
  # Set callback object. =   # Set callback object.
  # Setting the callback object is optional i.e. game can be played and finished without it. =   # Setting the callback object is optional i.e. game can be played and finished without it.
  def set_game_over_callback callback =   def set_game_over_callback callback
    @game_over_callback = callback =     @game_over_callback = callback
  end =   end
=
  # Returns the next Player (X or O) to make a move. If game is already over, always return Player.None =   # Returns the next Player (X or O) to make a move. If game is already over, always return Player.None
  def who_is_next =   def who_is_next
    @next_player =     @next_player
  end =   end
=
  def check_end_of_game =   def check_end_of_game
    unused_fields = @cells.find_all{|cell| cell == Player::NONE} =     unused_fields = @cells.find_all{|cell| cell == Player::NONE}
    if player_won? Player::O =     if player_won? Player::O
<       @next_player = Player::NONE
      @game_over_callback.the_winner_is(Player::O) =       @game_over_callback.the_winner_is(Player::O)
    elsif player_won? Player::X =     elsif player_won? Player::X
<       @next_player = Player::NONE
      @game_over_callback.the_winner_is(Player::X) =       @game_over_callback.the_winner_is(Player::X)
    elsif unused_fields.empty? =     elsif unused_fields.empty?
      @next_player = Player::NONE =       @next_player = Player::NONE
      @game_over_callback.the_winner_is(Player::NONE) if @game_over_callback =       @game_over_callback.the_winner_is(Player::NONE) if @game_over_callback
    end =     end
  end =   end
=
  def player_won? player =   def player_won? player
    player_line = [player, player, player] =     player_line = [player, player, player]
    (line(0,2) == player_line) or (line(3,5) == player_line) or (line(6,8) == player_line) or (line(0,6) == player_line) or (line(1,7) == player_line) or (line(2,8) == player_line) or (line(0,8) == player_line) or (line(2,6) == player_line) =     (line(0,2) == player_line) or (line(3,5) == player_line) or (line(6,8) == player_line) or (line(0,6) == player_line) or (line(1,7) == player_line) or (line(2,8) == player_line) or (line(0,8) == player_line) or (line(2,6) == player_line)
  end =   end
=
  def line start_index, end_index =   def line start_index, end_index
    diff = end_index - start_index =     diff = end_index - start_index
    mid_index = start_index + (diff+1)/2 =     mid_index = start_index + (diff+1)/2
    [@cells[start_index], @cells[mid_index], @cells[end_index]] =     [@cells[start_index], @cells[mid_index], @cells[end_index]]
  end =   end
=
end = end
=
=
========== next file ========== = ========== next file ==========
=
require 'tic_tac_toe' = require 'tic_tac_toe'
=
describe TicTacToe do = describe TicTacToe do
=
  def print_line line =   def print_line line
    @printed_lines << line =     @printed_lines << line
  end =   end
=
  def board =   def board
    @game.print_board(self) =     @game.print_board(self)
    @printed_lines =     @printed_lines
  end =   end
=
  before (:each) do =   before (:each) do
    @game = TicTacToe.new =     @game = TicTacToe.new
    @printed_lines = [] =     @printed_lines = []
  end =   end
=
  it "should start with an empty board" do =   it "should start with an empty board" do
    board.should == ['---', '---', '---'] =     board.should == ['---', '---', '---']
  end =   end
=
  it "should start with player X" do =   it "should start with player X" do
    @game.who_is_next.should == Player::X =     @game.who_is_next.should == Player::X
  end =   end
=
  it "should prevent O to make first move" do =   it "should prevent O to make first move" do
    lambda{@game.play_O 1}.should raise_error =     lambda{@game.play_O 1}.should raise_error
  end =   end
=
  it "should let X make the first move" do =   it "should let X make the first move" do
    @game.play_X 1 =     @game.play_X 1
    board.should == ['X--', '---', '---'] =     board.should == ['X--', '---', '---']
  end =   end
=
  it "should let O make the second move" do =   it "should let O make the second move" do
    @game.play_X 5 =     @game.play_X 5
    @game.play_O 9 =     @game.play_O 9
    board.should == ['---', '-X-', '--O'] =     board.should == ['---', '-X-', '--O']
    @game.who_is_next.should == Player::X =     @game.who_is_next.should == Player::X
  end =   end
=
  it "should prevent X playing twice in a row" do =   it "should prevent X playing twice in a row" do
    @game.play_X 3 =     @game.play_X 3
    lambda{@game.play_X 4}.should raise_error =     lambda{@game.play_X 4}.should raise_error
  end =   end
=
  it "should prevent O playing twice in a row" do =   it "should prevent O playing twice in a row" do
    @game.play_X 1 =     @game.play_X 1
    @game.play_O 3 =     @game.play_O 3
    lambda{@game.play_O 4}.should raise_error =     lambda{@game.play_O 4}.should raise_error
  end =   end
=
=
  it "should prevent O from playing on an occupied field" do =   it "should prevent O from playing on an occupied field" do
    @game.play_X 4 =     @game.play_X 4
    lambda{@game.play_O 4}.should raise_error =     lambda{@game.play_O 4}.should raise_error
  end =   end
=
  it "should prevent X from playing on an occupied field" do =   it "should prevent X from playing on an occupied field" do
    @game.play_X 4 =     @game.play_X 4
    @game.play_O 3 =     @game.play_O 3
    lambda{@game.play_X 3}.should raise_error =     lambda{@game.play_X 3}.should raise_error
  end =   end
=
  it "should ensure that the game can be finished without game_over_callback explicitly set" do =   it "should ensure that the game can be finished without game_over_callback explicitly set" do
    @game.play_X 1 =     @game.play_X 1
    @game.play_O 4 =     @game.play_O 4
    @game.play_X 2 =     @game.play_X 2
    @game.play_O 5 =     @game.play_O 5
    @game.play_X 6 =     @game.play_X 6
    @game.play_O 3 =     @game.play_O 3
    @game.play_X 7 =     @game.play_X 7
    @game.play_O 8 =     @game.play_O 8
    @game.play_X 9 =     @game.play_X 9
    @game.who_is_next.should == Player::NONE =     @game.who_is_next.should == Player::NONE
  end =   end
=
end = end
=
describe TicTacToe, "Game Over" do = describe TicTacToe, "Game Over" do
=
  def the_winner_is winner =   def the_winner_is winner
    @winner = winner =     @winner = winner
  end =   end
=
  def play_game moves =   def play_game moves
    moves.each do |move| =     moves.each do |move|
      if @game.who_is_next == Player::X then @game.play_X(move) else @game.play_O(move) end =       if @game.who_is_next == Player::X then @game.play_X(move) else @game.play_O(move) end
    end =     end
    @winner.should == nil =     @winner.should == nil
  end =   end
=
  before (:each) do =   before (:each) do
    @game = TicTacToe.new =     @game = TicTacToe.new
    @game.set_game_over_callback(self) =     @game.set_game_over_callback(self)
    @winner = nil =     @winner = nil
  end =   end
=
  it "should detect no winner on full board" do =   it "should detect no winner on full board" do
    play_game [1, 4, 2, 5, 6, 3, 7, 8] =     play_game [1, 4, 2, 5, 6, 3, 7, 8]
    @game.play_X 9 =     @game.play_X 9
    @winner.should == Player::NONE =     @winner.should == Player::NONE
    @game.who_is_next == Player::NONE =     @game.who_is_next == Player::NONE
  end =   end
=
  it "should detect X winning in row 1" do =   it "should detect X winning in row 1" do
    play_game [1, 4, 2, 5] =     play_game [1, 4, 2, 5]
    @game.play_X 3 =     @game.play_X 3
    @winner.should == Player::X =     @winner.should == Player::X
    @game.who_is_next.should == Player::NONE =     @game.who_is_next.should == Player::NONE
  end =   end
=
=
end = end
...........F

1)
'TicTacToe Game Over should detect X winning in row 1' FAILED
expected: "-",
     got: "O" (using ==)
./tic_tac_toe_spec.rb:111:

Finished in 0.010654 seconds

12 examples, 1 failure
............

Finished in 0.009918 seconds

12 examples, 0 failures