tic_tac_toes 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/Gemfile.lock +1 -1
 - data/Rakefile +2 -2
 - data/bin/tic_tac_toes +18 -22
 - data/lib/tic_tac_toes/command_line/menu.rb +55 -0
 - data/lib/tic_tac_toes/command_line/prompt.rb +29 -0
 - data/lib/tic_tac_toes/command_line/runner.rb +38 -0
 - data/lib/tic_tac_toes/core/board.rb +64 -0
 - data/lib/tic_tac_toes/core/board_factory.rb +11 -0
 - data/lib/tic_tac_toes/core/game_state.rb +40 -0
 - data/lib/tic_tac_toes/core/game_state_factory.rb +15 -0
 - data/lib/tic_tac_toes/core/history.rb +29 -0
 - data/lib/tic_tac_toes/core/io.rb +93 -0
 - data/lib/tic_tac_toes/core/move_strategies/easy_ai.rb +13 -0
 - data/lib/tic_tac_toes/core/move_strategies/hard_ai.rb +89 -0
 - data/lib/tic_tac_toes/core/move_strategies/human.rb +20 -0
 - data/lib/tic_tac_toes/core/move_strategies/medium_ai.rb +15 -0
 - data/lib/tic_tac_toes/core/player.rb +22 -0
 - data/lib/tic_tac_toes/core/player_factory.rb +30 -0
 - data/lib/tic_tac_toes/core/rules.rb +66 -0
 - data/lib/tic_tac_toes/core/strings.rb +106 -0
 - data/lib/tic_tac_toes/database/pg_wrapper.rb +74 -0
 - data/lib/tic_tac_toes/ui/adapter.rb +96 -0
 - data/lib/tic_tac_toes/ui/serializer.rb +0 -0
 - data/lib/tic_tac_toes/version.rb +1 -1
 - data/spec/{command_line → tic_tac_toes/command_line}/menu_spec.rb +14 -14
 - data/spec/{command_line → tic_tac_toes/command_line}/runner_spec.rb +7 -10
 - data/spec/tic_tac_toes/{board_factory_spec.rb → core/board_factory_spec.rb} +3 -3
 - data/spec/tic_tac_toes/{board_spec.rb → core/board_spec.rb} +23 -23
 - data/spec/tic_tac_toes/{game_state_factory_spec.rb → core/game_state_factory_spec.rb} +3 -3
 - data/spec/tic_tac_toes/core/game_state_spec.rb +129 -0
 - data/spec/tic_tac_toes/{history_spec.rb → core/history_spec.rb} +3 -3
 - data/spec/tic_tac_toes/{io_spec.rb → core/io_spec.rb} +7 -7
 - data/spec/tic_tac_toes/core/move_strategies/easy_ai_spec.rb +19 -0
 - data/spec/tic_tac_toes/core/move_strategies/hard_ai_spec.rb +121 -0
 - data/spec/tic_tac_toes/{move_strategies → core/move_strategies}/human_spec.rb +3 -3
 - data/spec/tic_tac_toes/core/move_strategies/medium_ai_spec.rb +21 -0
 - data/spec/tic_tac_toes/{player_factory_spec.rb → core/player_factory_spec.rb} +7 -7
 - data/spec/tic_tac_toes/{player_spec.rb → core/player_spec.rb} +5 -5
 - data/spec/tic_tac_toes/{rules_spec.rb → core/rules_spec.rb} +34 -34
 - data/spec/tic_tac_toes/{strings_spec.rb → core/strings_spec.rb} +8 -8
 - data/spec/{database → tic_tac_toes/database}/pg_wrapper_spec.rb +3 -3
 - data/spec/tic_tac_toes/test_board_generator.rb +17 -0
 - data/spec/{ui → tic_tac_toes/ui}/adapter_spec.rb +13 -13
 - data/spec/tic_tac_toes/ui/serializer_spec.rb +0 -0
 - metadata +64 -61
 - data/lib/command_line/menu.rb +0 -53
 - data/lib/command_line/prompt.rb +0 -27
 - data/lib/command_line/runner.rb +0 -37
 - data/lib/database/pg_wrapper.rb +0 -72
 - data/lib/tic_tac_toes/board.rb +0 -62
 - data/lib/tic_tac_toes/board_factory.rb +0 -9
 - data/lib/tic_tac_toes/game_state.rb +0 -27
 - data/lib/tic_tac_toes/game_state_factory.rb +0 -14
 - data/lib/tic_tac_toes/history.rb +0 -27
 - data/lib/tic_tac_toes/io.rb +0 -91
 - data/lib/tic_tac_toes/move_strategies/easy_ai.rb +0 -11
 - data/lib/tic_tac_toes/move_strategies/hard_ai.rb +0 -87
 - data/lib/tic_tac_toes/move_strategies/human.rb +0 -18
 - data/lib/tic_tac_toes/move_strategies/medium_ai.rb +0 -13
 - data/lib/tic_tac_toes/player.rb +0 -20
 - data/lib/tic_tac_toes/player_factory.rb +0 -28
 - data/lib/tic_tac_toes/rules.rb +0 -64
 - data/lib/tic_tac_toes/strings.rb +0 -104
 - data/lib/ui/adapter.rb +0 -94
 - data/spec/test_board_generator.rb +0 -15
 - data/spec/tic_tac_toes/game_state_spec.rb +0 -66
 - data/spec/tic_tac_toes/move_strategies/easy_ai_spec.rb +0 -19
 - data/spec/tic_tac_toes/move_strategies/hard_ai_spec.rb +0 -121
 - data/spec/tic_tac_toes/move_strategies/medium_ai_spec.rb +0 -21
 - /data/lib/{tasks → tic_tac_toes/tasks}/destroy_databases.rake +0 -0
 - /data/lib/{tasks → tic_tac_toes/tasks}/set_up_databases.rake +0 -0
 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'tic_tac_toes/core/player_factory'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module TicTacToes
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Core
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Rules
         
     | 
| 
      
 6 
     | 
    
         
            +
                  ROW_SIZE_RANGE = (2..10)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def self.row_size_valid?(row_size)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    row_size.between?(ROW_SIZE_RANGE.min, ROW_SIZE_RANGE.max)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def self.token_valid?(token, taken_tokens)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    correct_length = token.length == 1
         
     | 
| 
      
 14 
     | 
    
         
            +
                    untaken = !taken_tokens.include?(token)
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    correct_length && untaken
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def self.difficulty_valid?(difficulty)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    PlayerFactory::AIS.include? difficulty
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def self.game_over?(board, players)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    winner = !determine_winner(board, players).nil?
         
     | 
| 
      
 25 
     | 
    
         
            +
                    tie = board.full?
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    winner || tie
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def self.determine_winner(board, players)
         
     | 
| 
      
 31 
     | 
    
         
            +
                    winner = nil
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    players.each do |player|
         
     | 
| 
      
 34 
     | 
    
         
            +
                      player_has_won = win?(board, player)
         
     | 
| 
      
 35 
     | 
    
         
            +
                      winner = player.token if player_has_won
         
     | 
| 
      
 36 
     | 
    
         
            +
                    end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    winner
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  def self.win?(board, player)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    diagonal_win?(board, player) ||
         
     | 
| 
      
 43 
     | 
    
         
            +
                    horizontal_win?(board, player) ||
         
     | 
| 
      
 44 
     | 
    
         
            +
                    vertical_win?(board, player)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  private
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def self.diagonal_win?(board, player)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    set_win?(board.diagonals, player)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def self.horizontal_win?(board, player)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    set_win?(board.rows, player)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  def self.vertical_win?(board, player)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    set_win?(board.columns, player)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  def self.set_win?(sets, player)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    sets.any? { |set| set.all? { |space| space.token == player.token unless space.nil?  } }
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,106 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'tic_tac_toes/command_line/prompt'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'tic_tac_toes/core/rules'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module TicTacToes
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Core
         
     | 
| 
      
 6 
     | 
    
         
            +
                module Strings
         
     | 
| 
      
 7 
     | 
    
         
            +
                  smallest_row_size = Rules::ROW_SIZE_RANGE.min
         
     | 
| 
      
 8 
     | 
    
         
            +
                  largest_row_size = Rules::ROW_SIZE_RANGE.max
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  NOT_AN_INTEGER           =  "Input must be an integer"
         
     | 
| 
      
 11 
     | 
    
         
            +
                  INVALID_ROW_SIZE         =  "Input must be between #{smallest_row_size} and #{largest_row_size}"
         
     | 
| 
      
 12 
     | 
    
         
            +
                  INVALID_TOKEN            =  "Input must be a single, untaken character"
         
     | 
| 
      
 13 
     | 
    
         
            +
                  INVALID_DIFFICULTY       =  "Input must be a valid difficulty"
         
     | 
| 
      
 14 
     | 
    
         
            +
                  INVALID_MOVE             =  "Input must be a space that is on the board and untaken"
         
     | 
| 
      
 15 
     | 
    
         
            +
                  ROW_SIZE_SOLICITATION    =  "Pick row size of board:"
         
     | 
| 
      
 16 
     | 
    
         
            +
                  DIFFICULTY_SOLICITATION  =  "Pick difficulty (easy, medium, hard):"
         
     | 
| 
      
 17 
     | 
    
         
            +
                  MOVE_SOLICITATION        =  "Pick a space:"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  THINKING                 =  "Thinking..."
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  def self.token_solicitation(player)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    "Pick #{player} token:"
         
     | 
| 
      
 22 
     | 
    
         
            +
                  end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                  def self.game_over_notification(winner)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    "#{winner} wins!"
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def self.board(board)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    board_string = ""
         
     | 
| 
      
 30 
     | 
    
         
            +
                    board.rows.each_with_index do |row, index|
         
     | 
| 
      
 31 
     | 
    
         
            +
                      row_start_index = (index * board.row_size).to_i
         
     | 
| 
      
 32 
     | 
    
         
            +
                      at_last_row = index == board.row_size - 1
         
     | 
| 
      
 33 
     | 
    
         
            +
                      board_string << "\n"
         
     | 
| 
      
 34 
     | 
    
         
            +
                      board_string << row(row, row_start_index, board.size)
         
     | 
| 
      
 35 
     | 
    
         
            +
                      board_string << "\n"
         
     | 
| 
      
 36 
     | 
    
         
            +
                      board_string << horizontal_divider(board.row_size, board.size) unless at_last_row
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    board_string << "\n"
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  private
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  def self.row(row, row_start_index, board_size)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    row_array = []
         
     | 
| 
      
 45 
     | 
    
         
            +
                    row.each_with_index do |space, index|
         
     | 
| 
      
 46 
     | 
    
         
            +
                      if space.nil?
         
     | 
| 
      
 47 
     | 
    
         
            +
                        board_index = index + row_start_index
         
     | 
| 
      
 48 
     | 
    
         
            +
                        row_array << empty_space(board_index, board_size)
         
     | 
| 
      
 49 
     | 
    
         
            +
                      else
         
     | 
| 
      
 50 
     | 
    
         
            +
                        row_array << token(space, board_size)
         
     | 
| 
      
 51 
     | 
    
         
            +
                      end
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                    row_array.join("|")
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  def self.empty_space(board_index, board_size)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    if space_needs_buffer?(board_index, board_size)
         
     | 
| 
      
 58 
     | 
    
         
            +
                      "[ #{board_index}]"
         
     | 
| 
      
 59 
     | 
    
         
            +
                    else
         
     | 
| 
      
 60 
     | 
    
         
            +
                      "[#{board_index}]"
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  def self.token(space, board_size)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    token = get_colored_token(space)
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    if double_digit_board?(board_size)
         
     | 
| 
      
 68 
     | 
    
         
            +
                      "  #{token} "
         
     | 
| 
      
 69 
     | 
    
         
            +
                    else
         
     | 
| 
      
 70 
     | 
    
         
            +
                      " #{token} "
         
     | 
| 
      
 71 
     | 
    
         
            +
                    end
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  def self.get_colored_token(player)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    if player.needs_to_think
         
     | 
| 
      
 76 
     | 
    
         
            +
                      return CommandLine::Prompt.red(player.token)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    else
         
     | 
| 
      
 78 
     | 
    
         
            +
                      return CommandLine::Prompt.blue(player.token)
         
     | 
| 
      
 79 
     | 
    
         
            +
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  def self.space_needs_buffer?(board_index, board_size)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    is_double_digit_board = double_digit_board?(board_size)
         
     | 
| 
      
 84 
     | 
    
         
            +
                    is_single_digit_space = board_index < 10
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                    is_double_digit_board && is_single_digit_space
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                  def self.double_digit_board?(board_size)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    board_size > 10
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  def self.horizontal_divider(row_size, board_size)
         
     | 
| 
      
 94 
     | 
    
         
            +
                    horizontal_divider = ""
         
     | 
| 
      
 95 
     | 
    
         
            +
                    divider_unit = "-"
         
     | 
| 
      
 96 
     | 
    
         
            +
                    divider_units_per_space = double_digit_board?(board_size) ? 5 : 4
         
     | 
| 
      
 97 
     | 
    
         
            +
                    extra_units_per_row = 1
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                    raw_length = (row_size * divider_units_per_space).to_i
         
     | 
| 
      
 100 
     | 
    
         
            +
                    truncated_length = raw_length - extra_units_per_row
         
     | 
| 
      
 101 
     | 
    
         
            +
                    truncated_length.times { horizontal_divider << divider_unit }
         
     | 
| 
      
 102 
     | 
    
         
            +
                    horizontal_divider
         
     | 
| 
      
 103 
     | 
    
         
            +
                  end
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
              end
         
     | 
| 
      
 106 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,74 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'pg'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'tic_tac_toes/core/history'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module TicTacToes
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Database
         
     | 
| 
      
 6 
     | 
    
         
            +
                class PGWrapper
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(database)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @database = database
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def record_game_history(history)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    connection = establish_connection
         
     | 
| 
      
 13 
     | 
    
         
            +
                    record_board_size_and_winner(history, connection)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    record_moves(history, connection)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  def read_game_histories
         
     | 
| 
      
 18 
     | 
    
         
            +
                    connection = establish_connection
         
     | 
| 
      
 19 
     | 
    
         
            +
                    games_result = connection.exec("SELECT * FROM games")
         
     | 
| 
      
 20 
     | 
    
         
            +
                    games = []
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    games_result.each_row do |row|
         
     | 
| 
      
 23 
     | 
    
         
            +
                      game_id = row[0]
         
     | 
| 
      
 24 
     | 
    
         
            +
                      board_size = row[1].to_i
         
     | 
| 
      
 25 
     | 
    
         
            +
                      winner = row[2]
         
     | 
| 
      
 26 
     | 
    
         
            +
                      moves_result = connection.exec("SELECT * FROM moves WHERE game = #{game_id}")
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                      history = Core::History.new(self)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                      moves_result.each_row do |row|
         
     | 
| 
      
 31 
     | 
    
         
            +
                        token, space = row[2], row[3].to_i
         
     | 
| 
      
 32 
     | 
    
         
            +
                        history.record_move([token, space])
         
     | 
| 
      
 33 
     | 
    
         
            +
                      end
         
     | 
| 
      
 34 
     | 
    
         
            +
                      history.record_board_size(board_size)
         
     | 
| 
      
 35 
     | 
    
         
            +
                      history.record_winner(winner)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                      games << history
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                    games
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  private
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  def establish_connection
         
     | 
| 
      
 46 
     | 
    
         
            +
                    PG.connect(dbname: @database)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def record_board_size_and_winner(history, connection)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    connection.exec("INSERT INTO games (board_size, winner) VALUES (
         
     | 
| 
      
 51 
     | 
    
         
            +
                      #{history.board_size},
         
     | 
| 
      
 52 
     | 
    
         
            +
                      '#{history.winner}')")
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def record_moves(history, connection)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    game_id = read_game_id(connection)
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    history.moves.each_with_index do |move, index|
         
     | 
| 
      
 59 
     | 
    
         
            +
                      move_number = index + 1
         
     | 
| 
      
 60 
     | 
    
         
            +
                      connection.exec("INSERT INTO moves (game, number, token, space) VALUES (
         
     | 
| 
      
 61 
     | 
    
         
            +
                        #{game_id},
         
     | 
| 
      
 62 
     | 
    
         
            +
                        #{move_number},
         
     | 
| 
      
 63 
     | 
    
         
            +
                        '#{move.first}',
         
     | 
| 
      
 64 
     | 
    
         
            +
                        #{move.last})")
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  def read_game_id(connection)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    result = connection.exec("SELECT currval(pg_get_serial_sequence('games','id'))")
         
     | 
| 
      
 70 
     | 
    
         
            +
                    result.getvalue(0,0)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
                end
         
     | 
| 
      
 73 
     | 
    
         
            +
              end
         
     | 
| 
      
 74 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,96 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'tic_tac_toes/core/board'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'tic_tac_toes/core/game_state'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'tic_tac_toes/core/player_factory'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'tic_tac_toes/core/rules'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module TicTacToes
         
     | 
| 
      
 7 
     | 
    
         
            +
              module UI
         
     | 
| 
      
 8 
     | 
    
         
            +
                module Adapter
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def self.new_board_structure
         
     | 
| 
      
 10 
     | 
    
         
            +
                    board = Core::Board.new
         
     | 
| 
      
 11 
     | 
    
         
            +
                    board.spaces
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def self.make_move(board_structure, move, listener)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    move = move.to_i
         
     | 
| 
      
 16 
     | 
    
         
            +
                    game_state = game_state_from_board_structure(board_structure)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    human_player = game_state.players.first
         
     | 
| 
      
 19 
     | 
    
         
            +
                    game_state.board.place(human_player, move)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    if Core::Rules.game_over?(game_state.board, game_state.players)
         
     | 
| 
      
 22 
     | 
    
         
            +
                      return listener.game_is_over(game_state_to_board_structure(game_state), "Game over")
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    game_state.turn_over(move)
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    computer_player = game_state.players.first
         
     | 
| 
      
 28 
     | 
    
         
            +
                    computer_player.place_and_return_move(game_state.board, game_state.players)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    if Core::Rules.game_over?(game_state.board, game_state.players)
         
     | 
| 
      
 31 
     | 
    
         
            +
                      return listener.game_is_over(game_state_to_board_structure(game_state), "Game over")
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    listener.move_was_valid(game_state_to_board_structure(game_state))
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  def self.game_state_from_board_structure(board_structure)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    player_factory = Core::PlayerFactory.new('unused_io')
         
     | 
| 
      
 39 
     | 
    
         
            +
                    human_player = player_factory.generate_human_player('X')
         
     | 
| 
      
 40 
     | 
    
         
            +
                    computer_player = player_factory.generate_computer_player('O', :hard)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    players = [human_player, computer_player]
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    board_structure_with_players = replace_tokens_with_players(board_structure, human_player, computer_player)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    board = board_from_structure(board_structure_with_players)
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    Core::GameState.new(board, players, NullHistory.new)
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def self.game_state_to_board_structure(game_state)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    structure_with_players = game_state.board.spaces
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                    replace_players_with_tokens(structure_with_players)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  private
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  def self.replace_tokens_with_players(board_structure, human_player, computer_player)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    board_structure.map do |space|
         
     | 
| 
      
 59 
     | 
    
         
            +
                      case space
         
     | 
| 
      
 60 
     | 
    
         
            +
                      when 'X'
         
     | 
| 
      
 61 
     | 
    
         
            +
                        human_player
         
     | 
| 
      
 62 
     | 
    
         
            +
                      when 'O'
         
     | 
| 
      
 63 
     | 
    
         
            +
                        computer_player
         
     | 
| 
      
 64 
     | 
    
         
            +
                      else
         
     | 
| 
      
 65 
     | 
    
         
            +
                        nil
         
     | 
| 
      
 66 
     | 
    
         
            +
                      end
         
     | 
| 
      
 67 
     | 
    
         
            +
                    end
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  def self.replace_players_with_tokens(board_structure)
         
     | 
| 
      
 71 
     | 
    
         
            +
                    board_structure.map { |space| space.token unless space.nil? }
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                  def self.board_from_structure(board_structure)
         
     | 
| 
      
 75 
     | 
    
         
            +
                    row_size = Math.sqrt(board_structure.count).to_i
         
     | 
| 
      
 76 
     | 
    
         
            +
                    board = Core::Board.new(row_size: row_size)
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                    board_structure.each_with_index do |player, index|
         
     | 
| 
      
 79 
     | 
    
         
            +
                      board.place(player, index)
         
     | 
| 
      
 80 
     | 
    
         
            +
                    end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                    board
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                class NullHistory
         
     | 
| 
      
 87 
     | 
    
         
            +
                  def record_board_size(size)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  def record_move(move)
         
     | 
| 
      
 92 
     | 
    
         
            +
                    nil
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
            end
         
     | 
| 
         
            File without changes
         
     | 
    
        data/lib/tic_tac_toes/version.rb
    CHANGED
    
    
| 
         @@ -1,15 +1,15 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'command_line/menu'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'tic_tac_toes/board_factory'
         
     | 
| 
       3 
     | 
    
         
            -
            require 'tic_tac_toes/player_factory'
         
     | 
| 
       4 
     | 
    
         
            -
            require 'tic_tac_toes/io'
         
     | 
| 
       5 
     | 
    
         
            -
            require 'tic_tac_toes/move_strategies/human'
         
     | 
| 
       6 
     | 
    
         
            -
            require 'tic_tac_toes/move_strategies/medium_ai'
         
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
            describe CommandLine::Menu do
         
     | 
| 
      
 1 
     | 
    
         
            +
            require 'tic_tac_toes/command_line/menu'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'tic_tac_toes/core/board_factory'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'tic_tac_toes/core/player_factory'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'tic_tac_toes/core/io'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'tic_tac_toes/core/move_strategies/human'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'tic_tac_toes/core/move_strategies/medium_ai'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            describe TicTacToes::CommandLine::Menu do
         
     | 
| 
       9 
9 
     | 
    
         
             
              let(:io) { double }
         
     | 
| 
       10 
     | 
    
         
            -
              let(:board_factory) { TicTacToes::BoardFactory.new }
         
     | 
| 
       11 
     | 
    
         
            -
              let(:player_factory) { TicTacToes::PlayerFactory.new(io) }
         
     | 
| 
       12 
     | 
    
         
            -
              let(:menu) { CommandLine::Menu.new(io, board_factory, player_factory) }
         
     | 
| 
      
 10 
     | 
    
         
            +
              let(:board_factory) { TicTacToes::Core::BoardFactory.new }
         
     | 
| 
      
 11 
     | 
    
         
            +
              let(:player_factory) { TicTacToes::Core::PlayerFactory.new(io) }
         
     | 
| 
      
 12 
     | 
    
         
            +
              let(:menu) { TicTacToes::CommandLine::Menu.new(io, board_factory, player_factory) }
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
              describe '#get_board' do
         
     | 
| 
       15 
15 
     | 
    
         
             
                let(:invalid_row_size) { 11 }
         
     | 
| 
         @@ -40,8 +40,8 @@ describe CommandLine::Menu do 
     | 
|
| 
       40 
40 
     | 
    
         
             
                  allow(io).to receive(:get_difficulty).and_return(difficulty)
         
     | 
| 
       41 
41 
     | 
    
         | 
| 
       42 
42 
     | 
    
         
             
                  human_player, computer_player = menu.get_players
         
     | 
| 
       43 
     | 
    
         
            -
                  expect(human_player.move_strategy).to be_a TicTacToes::MoveStrategies::Human
         
     | 
| 
       44 
     | 
    
         
            -
                  expect(computer_player.move_strategy).to eq(TicTacToes::MoveStrategies::MediumAI)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  expect(human_player.move_strategy).to be_a TicTacToes::Core::MoveStrategies::Human
         
     | 
| 
      
 44 
     | 
    
         
            +
                  expect(computer_player.move_strategy).to eq(TicTacToes::Core::MoveStrategies::MediumAI)
         
     | 
| 
       45 
45 
     | 
    
         
             
                end
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
       47 
47 
     | 
    
         
             
                context 'when given an invalid (non-single-character) token' do
         
     | 
| 
         @@ -92,7 +92,7 @@ describe CommandLine::Menu do 
     | 
|
| 
       92 
92 
     | 
    
         
             
                    allow(io).to receive(:get_difficulty).and_return(invalid_difficulty, valid_difficulty)
         
     | 
| 
       93 
93 
     | 
    
         | 
| 
       94 
94 
     | 
    
         
             
                    computer_player = menu.get_players.last
         
     | 
| 
       95 
     | 
    
         
            -
                    expect(computer_player.move_strategy).to eq(TicTacToes::MoveStrategies::MediumAI)
         
     | 
| 
      
 95 
     | 
    
         
            +
                    expect(computer_player.move_strategy).to eq(TicTacToes::Core::MoveStrategies::MediumAI)
         
     | 
| 
       96 
96 
     | 
    
         
             
                  end
         
     | 
| 
       97 
97 
     | 
    
         
             
                end
         
     | 
| 
       98 
98 
     | 
    
         
             
              end
         
     | 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'command_line/runner'
         
     | 
| 
      
 1 
     | 
    
         
            +
            require 'tic_tac_toes/command_line/runner'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            describe CommandLine::Runner do
         
     | 
| 
      
 3 
     | 
    
         
            +
            describe TicTacToes::CommandLine::Runner do
         
     | 
| 
       4 
4 
     | 
    
         
             
              describe '#run' do
         
     | 
| 
       5 
5 
     | 
    
         
             
                let(:game_state) { double(board: 'board', players: 'players', game_over: true) }
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
         @@ -11,15 +11,12 @@ describe CommandLine::Runner do 
     | 
|
| 
       11 
11 
     | 
    
         
             
                                                   get_players: true) }
         
     | 
| 
       12 
12 
     | 
    
         
             
                let(:game_state)          { double(board: 'board',
         
     | 
| 
       13 
13 
     | 
    
         
             
                                                   players: 'players',
         
     | 
| 
       14 
     | 
    
         
            -
                                                   game_over: true 
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
                let(:rules)               { double(game_over?: true,
         
     | 
| 
      
 14 
     | 
    
         
            +
                                                   game_over: true,
         
     | 
| 
      
 15 
     | 
    
         
            +
                                                   game_over?: true,
         
     | 
| 
       17 
16 
     | 
    
         
             
                                                   determine_winner: true) }
         
     | 
| 
      
 17 
     | 
    
         
            +
                let(:game_state_factory)  { double(generate_game_state: game_state) }
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
                let(:runner) 
     | 
| 
       20 
     | 
    
         
            -
                                                                    menu,
         
     | 
| 
       21 
     | 
    
         
            -
                                                                    game_state_factory,
         
     | 
| 
       22 
     | 
    
         
            -
                                                                    rules) }
         
     | 
| 
      
 19 
     | 
    
         
            +
                let(:runner) { TicTacToes::CommandLine::Runner.new(io, menu, game_state_factory) }
         
     | 
| 
       23 
20 
     | 
    
         | 
| 
       24 
21 
     | 
    
         
             
                it 'gets an initial game state' do
         
     | 
| 
       25 
22 
     | 
    
         
             
                  expect(game_state_factory).to receive(:generate_game_state)
         
     | 
| 
         @@ -27,7 +24,7 @@ describe CommandLine::Runner do 
     | 
|
| 
       27 
24 
     | 
    
         
             
                end
         
     | 
| 
       28 
25 
     | 
    
         | 
| 
       29 
26 
     | 
    
         
             
                it 'takes turns until the game is over' do
         
     | 
| 
       30 
     | 
    
         
            -
                  allow( 
     | 
| 
      
 27 
     | 
    
         
            +
                  allow(game_state).to receive(:game_over?).and_return(false, true)
         
     | 
| 
       31 
28 
     | 
    
         | 
| 
       32 
29 
     | 
    
         
             
                  expect(runner).to receive(:take_turn).once
         
     | 
| 
       33 
30 
     | 
    
         
             
                  runner.run
         
     | 
| 
         @@ -1,9 +1,9 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'tic_tac_toes/board_factory'
         
     | 
| 
      
 1 
     | 
    
         
            +
            require 'tic_tac_toes/core/board_factory'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            describe TicTacToes::BoardFactory do
         
     | 
| 
      
 3 
     | 
    
         
            +
            describe TicTacToes::Core::BoardFactory do
         
     | 
| 
       4 
4 
     | 
    
         
             
              describe '#generate_board' do
         
     | 
| 
       5 
5 
     | 
    
         
             
                it 'returns a board object generated from a row size' do
         
     | 
| 
       6 
     | 
    
         
            -
                  board_factory = TicTacToes::BoardFactory.new
         
     | 
| 
      
 6 
     | 
    
         
            +
                  board_factory = TicTacToes::Core::BoardFactory.new
         
     | 
| 
       7 
7 
     | 
    
         
             
                  row_size = 5
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
                  board = board_factory.generate_board(row_size)
         
     | 
| 
         @@ -1,9 +1,9 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'test_board_generator'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'tic_tac_toes/board'
         
     | 
| 
      
 1 
     | 
    
         
            +
            require 'tic_tac_toes/test_board_generator'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'tic_tac_toes/core/board'
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
            describe TicTacToes::Board do
         
     | 
| 
      
 4 
     | 
    
         
            +
            describe TicTacToes::Core::Board do
         
     | 
| 
       5 
5 
     | 
    
         
             
              describe '#place (and #space)' do
         
     | 
| 
       6 
     | 
    
         
            -
                let(:board) { TicTacToes::Board.new(row_size: 3) }
         
     | 
| 
      
 6 
     | 
    
         
            +
                let(:board) { TicTacToes::Core::Board.new(row_size: 3) }
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                it "returns nil if the space is not nil" do
         
     | 
| 
       9 
9 
     | 
    
         
             
                  first_token, second_token = :X, :O
         
     | 
| 
         @@ -28,9 +28,9 @@ describe TicTacToes::Board do 
     | 
|
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         
             
              describe '#open_spaces' do
         
     | 
| 
       30 
30 
     | 
    
         
             
                it "returns an array of the board's nil spaces" do
         
     | 
| 
       31 
     | 
    
         
            -
                  board = TestBoardGenerator.generate([:X, :O, nil,
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
      
 31 
     | 
    
         
            +
                  board = TicTacToes::TestBoardGenerator.generate([:X, :O, nil,
         
     | 
| 
      
 32 
     | 
    
         
            +
                                                                   :O, :O,  :X,
         
     | 
| 
      
 33 
     | 
    
         
            +
                                                                   :X, :X, nil])
         
     | 
| 
       34 
34 
     | 
    
         
             
                  open_spaces = [2, 8]
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
36 
     | 
    
         
             
                  expect(board.open_spaces).to eql(open_spaces)
         
     | 
| 
         @@ -40,10 +40,10 @@ describe TicTacToes::Board do 
     | 
|
| 
       40 
40 
     | 
    
         | 
| 
       41 
41 
     | 
    
         
             
              describe '#rows' do
         
     | 
| 
       42 
42 
     | 
    
         
             
                it "returns an array of row arrays based on the board's spaces" do
         
     | 
| 
       43 
     | 
    
         
            -
                  board = TestBoardGenerator.generate([ :X,  :X,  :X,  :X,
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
      
 43 
     | 
    
         
            +
                  board = TicTacToes::TestBoardGenerator.generate([ :X,  :X,  :X,  :X,
         
     | 
| 
      
 44 
     | 
    
         
            +
                                                                   nil, nil, nil, nil,
         
     | 
| 
      
 45 
     | 
    
         
            +
                                                                   nil, nil, nil, nil,
         
     | 
| 
      
 46 
     | 
    
         
            +
                                                                   nil, nil, nil, nil])
         
     | 
| 
       47 
47 
     | 
    
         
             
                  first_row = [:X, :X, :X, :X]
         
     | 
| 
       48 
48 
     | 
    
         | 
| 
       49 
49 
     | 
    
         
             
                  expect(board.rows.size).to eq(4)
         
     | 
| 
         @@ -54,10 +54,10 @@ describe TicTacToes::Board do 
     | 
|
| 
       54 
54 
     | 
    
         | 
| 
       55 
55 
     | 
    
         
             
              describe '#columns' do
         
     | 
| 
       56 
56 
     | 
    
         
             
                it "returns an array of column arrays based on the board's spaces" do
         
     | 
| 
       57 
     | 
    
         
            -
                  board = TestBoardGenerator.generate([:X, nil, nil, nil,
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
      
 57 
     | 
    
         
            +
                  board = TicTacToes::TestBoardGenerator.generate([:X, nil, nil, nil,
         
     | 
| 
      
 58 
     | 
    
         
            +
                                                                   :X, nil, nil, nil,
         
     | 
| 
      
 59 
     | 
    
         
            +
                                                                   :X, nil, nil, nil,
         
     | 
| 
      
 60 
     | 
    
         
            +
                                                                   :X, nil, nil, nil])
         
     | 
| 
       61 
61 
     | 
    
         
             
                  first_column = [:X, :X, :X, :X]
         
     | 
| 
       62 
62 
     | 
    
         | 
| 
       63 
63 
     | 
    
         
             
                  expect(board.columns.size).to eq(4)
         
     | 
| 
         @@ -68,10 +68,10 @@ describe TicTacToes::Board do 
     | 
|
| 
       68 
68 
     | 
    
         | 
| 
       69 
69 
     | 
    
         
             
              describe '#diagonals' do
         
     | 
| 
       70 
70 
     | 
    
         
             
                it "returns an array of diagonal arrays based on the board's spaces" do
         
     | 
| 
       71 
     | 
    
         
            -
                  board = TestBoardGenerator.generate([ :X, nil, nil,  :O,
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
      
 71 
     | 
    
         
            +
                  board = TicTacToes::TestBoardGenerator.generate([ :X, nil, nil,  :O,
         
     | 
| 
      
 72 
     | 
    
         
            +
                                                                   nil,  :X,  :O, nil,
         
     | 
| 
      
 73 
     | 
    
         
            +
                                                                   nil,  :O,  :X, nil,
         
     | 
| 
      
 74 
     | 
    
         
            +
                                                                    :O, nil, nil,  :X])
         
     | 
| 
       75 
75 
     | 
    
         
             
                  back_diagonal  = [:X, :X, :X, :X]
         
     | 
| 
       76 
76 
     | 
    
         
             
                  front_diagonal = [:O, :O, :O, :O]
         
     | 
| 
       77 
77 
     | 
    
         | 
| 
         @@ -84,14 +84,14 @@ describe TicTacToes::Board do 
     | 
|
| 
       84 
84 
     | 
    
         | 
| 
       85 
85 
     | 
    
         
             
              describe '#full?' do
         
     | 
| 
       86 
86 
     | 
    
         
             
                it "returns false if any spaces are still nil" do
         
     | 
| 
       87 
     | 
    
         
            -
                  board = TicTacToes::Board.new
         
     | 
| 
      
 87 
     | 
    
         
            +
                  board = TicTacToes::Core::Board.new
         
     | 
| 
       88 
88 
     | 
    
         
             
                  expect(board.full?).to be false
         
     | 
| 
       89 
89 
     | 
    
         
             
                end
         
     | 
| 
       90 
90 
     | 
    
         | 
| 
       91 
91 
     | 
    
         
             
                it "returns true if all spaces are non-nil" do
         
     | 
| 
       92 
     | 
    
         
            -
                  board = TestBoardGenerator.generate([:X, :O, :X,
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
      
 92 
     | 
    
         
            +
                  board = TicTacToes::TestBoardGenerator.generate([:X, :O, :X,
         
     | 
| 
      
 93 
     | 
    
         
            +
                                                                   :O, :X, :O,
         
     | 
| 
      
 94 
     | 
    
         
            +
                                                                   :X, :O, :X])
         
     | 
| 
       95 
95 
     | 
    
         | 
| 
       96 
96 
     | 
    
         
             
                  expect(board.full?).to be true
         
     | 
| 
       97 
97 
     | 
    
         
             
                end
         
     | 
| 
         @@ -1,10 +1,10 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'tic_tac_toes/game_state_factory'
         
     | 
| 
      
 1 
     | 
    
         
            +
            require 'tic_tac_toes/core/game_state_factory'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            describe TicTacToes::GameStateFactory do
         
     | 
| 
      
 3 
     | 
    
         
            +
            describe TicTacToes::Core::GameStateFactory do
         
     | 
| 
       4 
4 
     | 
    
         
             
              describe '#generate_game_state' do
         
     | 
| 
       5 
5 
     | 
    
         
             
                it 'returns a game state object generated from a board object and player array' do
         
     | 
| 
       6 
6 
     | 
    
         
             
                  history = double(record_board_size: true)
         
     | 
| 
       7 
     | 
    
         
            -
                  game_state_factory = TicTacToes::GameStateFactory.new(history)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  game_state_factory = TicTacToes::Core::GameStateFactory.new(history)
         
     | 
| 
       8 
8 
     | 
    
         
             
                  board, players = double(size: 3), double
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
                  game_state = game_state_factory.generate_game_state(board, players)
         
     | 
| 
         @@ -0,0 +1,129 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'tic_tac_toes/core/game_state'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'tic_tac_toes/test_board_generator'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            describe TicTacToes::Core::GameState do
         
     | 
| 
      
 5 
     | 
    
         
            +
              describe '@initialize' do
         
     | 
| 
      
 6 
     | 
    
         
            +
                it "records its board's size" do
         
     | 
| 
      
 7 
     | 
    
         
            +
                  size = 5
         
     | 
| 
      
 8 
     | 
    
         
            +
                  board = double(size: size)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  history = double
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  expect(history).to receive(:record_board_size).with(size)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  TicTacToes::Core::GameState.new(board, 'players', history)
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              describe '#current_player' do
         
     | 
| 
      
 17 
     | 
    
         
            +
                it 'returns the first item of its players array' do
         
     | 
| 
      
 18 
     | 
    
         
            +
                  history = double(record_board_size: true)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  players = ['first_player', 'second_player']
         
     | 
| 
      
 20 
     | 
    
         
            +
                  game_state = TicTacToes::Core::GameState.new('board', players, history)
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  current_player = game_state.current_player
         
     | 
| 
      
 23 
     | 
    
         
            +
                  expect(current_player).to eq('first_player')
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              describe '#turn_over' do
         
     | 
| 
      
 28 
     | 
    
         
            +
                it 'records the last move' do
         
     | 
| 
      
 29 
     | 
    
         
            +
                  move = double
         
     | 
| 
      
 30 
     | 
    
         
            +
                  players = double(rotate!: true)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  history = double(record_board_size: true)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  game_state = TicTacToes::Core::GameState.new('board', players, history)
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                  expect(history).to receive(:record_move).with(move)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  game_state.turn_over(move)
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                it 'rotates its player array' do
         
     | 
| 
      
 39 
     | 
    
         
            +
                  players = ['first_player', 'second_player']
         
     | 
| 
      
 40 
     | 
    
         
            +
                  history = double(record_board_size: true, record_move: true)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  game_state = TicTacToes::Core::GameState.new('board', players, history)
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  game_state.turn_over('move')
         
     | 
| 
      
 44 
     | 
    
         
            +
                  expect(game_state.current_player).to eq('second_player')
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
              describe '#game_over' do
         
     | 
| 
      
 49 
     | 
    
         
            +
                it 'records the winner' do
         
     | 
| 
      
 50 
     | 
    
         
            +
                  winner = double
         
     | 
| 
      
 51 
     | 
    
         
            +
                  history = double(record_board_size: true, persist: true)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  game_state = TicTacToes::Core::GameState.new('board', 'players', history)
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  expect(history).to receive(:record_winner).with(winner)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  game_state.game_over(winner)
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                it 'persists its history' do
         
     | 
| 
      
 59 
     | 
    
         
            +
                  winner = double
         
     | 
| 
      
 60 
     | 
    
         
            +
                  history = double(record_board_size: true, record_winner: true)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  game_state = TicTacToes::Core::GameState.new('board', 'players', history)
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  expect(history).to receive(:persist)
         
     | 
| 
      
 64 
     | 
    
         
            +
                  game_state.game_over(winner)
         
     | 
| 
      
 65 
     | 
    
         
            +
                end
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              describe '#game_over?' do
         
     | 
| 
      
 69 
     | 
    
         
            +
                let(:x) { double("human player", token: "x") }
         
     | 
| 
      
 70 
     | 
    
         
            +
                let(:o) { double("computer player", token: "o") }
         
     | 
| 
      
 71 
     | 
    
         
            +
                let(:players) { [x, o] }
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                let(:history) { double(record_board_size: true) }
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                it "returns false if there is not yet a winner and the board is not full" do
         
     | 
| 
      
 76 
     | 
    
         
            +
                  board = TicTacToes::TestBoardGenerator.generate([x, x,   o,
         
     | 
| 
      
 77 
     | 
    
         
            +
                                                                   o, o, nil,
         
     | 
| 
      
 78 
     | 
    
         
            +
                                                                   x, o,   x])
         
     | 
| 
      
 79 
     | 
    
         
            +
                  game_state = TicTacToes::Core::GameState.new(board, players, history)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  expect(game_state.game_over?).to be false
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                it "returns true if any player has won" do
         
     | 
| 
      
 84 
     | 
    
         
            +
                  board = TicTacToes::TestBoardGenerator.generate([  x, nil, nil,
         
     | 
| 
      
 85 
     | 
    
         
            +
                                                                   nil,   x, nil,
         
     | 
| 
      
 86 
     | 
    
         
            +
                                                                   nil, nil,   x])
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                  game_state = TicTacToes::Core::GameState.new(board, players, history)
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                  expect(game_state.game_over?).to be true
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                it "returns true if the board is full" do
         
     | 
| 
      
 94 
     | 
    
         
            +
                  board = TicTacToes::TestBoardGenerator.generate([x, x, o,
         
     | 
| 
      
 95 
     | 
    
         
            +
                                                                   o, o, x,
         
     | 
| 
      
 96 
     | 
    
         
            +
                                                                   x, o, x])
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                  game_state = TicTacToes::Core::GameState.new(board, players, history)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  expect(game_state.game_over?).to be true
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
              end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
              describe '#determine_winner' do
         
     | 
| 
      
 104 
     | 
    
         
            +
                let(:x) { double("human player", token: "x") }
         
     | 
| 
      
 105 
     | 
    
         
            +
                let(:o) { double("computer player", token: "o") }
         
     | 
| 
      
 106 
     | 
    
         
            +
                let(:players) { [x, o] }
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                let(:history) { double(record_board_size: true) }
         
     | 
| 
      
 109 
     | 
    
         
            +
                it "returns the winning token when there is a winner" do
         
     | 
| 
      
 110 
     | 
    
         
            +
                  board = TicTacToes::TestBoardGenerator.generate([  o, nil, nil,
         
     | 
| 
      
 111 
     | 
    
         
            +
                                                                   nil,   o, nil,
         
     | 
| 
      
 112 
     | 
    
         
            +
                                                                   nil, nil,   o])
         
     | 
| 
      
 113 
     | 
    
         
            +
                  winning_token = "o"
         
     | 
| 
      
 114 
     | 
    
         
            +
                  game_state = TicTacToes::Core::GameState.new(board, players, history)
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  expect(game_state.determine_winner).to eql(winning_token)
         
     | 
| 
      
 117 
     | 
    
         
            +
                end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                it "returns nil if there is not a winner" do
         
     | 
| 
      
 120 
     | 
    
         
            +
                  board = TicTacToes::TestBoardGenerator.generate([x, x,   o,
         
     | 
| 
      
 121 
     | 
    
         
            +
                                                                   o, o, nil,
         
     | 
| 
      
 122 
     | 
    
         
            +
                                                                   x, o,   x])
         
     | 
| 
      
 123 
     | 
    
         
            +
                  game_state = TicTacToes::Core::GameState.new(board, players, history)
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                  expect(game_state.determine_winner).to be_nil
         
     | 
| 
      
 126 
     | 
    
         
            +
                end
         
     | 
| 
      
 127 
     | 
    
         
            +
              end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
            end
         
     |