tic_tac_toes 0.0.9 → 0.1.0

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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/bin/tic_tac_toes +2 -6
  4. data/lib/tic_tac_toes/command_line/menu.rb +3 -3
  5. data/lib/tic_tac_toes/command_line/runner.rb +6 -4
  6. data/lib/tic_tac_toes/core/game_state.rb +8 -0
  7. data/lib/tic_tac_toes/core/history.rb +1 -0
  8. data/lib/tic_tac_toes/core/move_strategies/easy_ai.rb +2 -4
  9. data/lib/tic_tac_toes/core/move_strategies/hard_ai.rb +27 -27
  10. data/lib/tic_tac_toes/core/move_strategies/human.rb +2 -2
  11. data/lib/tic_tac_toes/core/move_strategies/medium_ai.rb +2 -2
  12. data/lib/tic_tac_toes/core/player.rb +3 -3
  13. data/lib/tic_tac_toes/core/rules.rb +1 -1
  14. data/lib/tic_tac_toes/ui/adapter.rb +5 -5
  15. data/lib/tic_tac_toes/version.rb +1 -1
  16. data/spec/tic_tac_toes/command_line/menu_spec.rb +1 -3
  17. data/spec/tic_tac_toes/command_line/runner_spec.rb +0 -34
  18. data/spec/tic_tac_toes/core/game_state_spec.rb +54 -7
  19. data/spec/tic_tac_toes/core/history_spec.rb +4 -4
  20. data/spec/tic_tac_toes/core/move_strategies/easy_ai_spec.rb +14 -7
  21. data/spec/tic_tac_toes/core/move_strategies/hard_ai_spec.rb +43 -28
  22. data/spec/tic_tac_toes/core/move_strategies/human_spec.rb +9 -9
  23. data/spec/tic_tac_toes/core/move_strategies/medium_ai_spec.rb +14 -9
  24. data/spec/tic_tac_toes/core/player_spec.rb +17 -14
  25. data/spec/tic_tac_toes/core/rules_spec.rb +3 -2
  26. data/spec/tic_tac_toes/ui/adapter_spec.rb +9 -9
  27. metadata +2 -8
  28. data/lib/tic_tac_toes/core/board_factory.rb +0 -11
  29. data/lib/tic_tac_toes/core/game_state_factory.rb +0 -15
  30. data/spec/tic_tac_toes/core/board_factory_spec.rb +0 -13
  31. data/spec/tic_tac_toes/core/game_state_factory_spec.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 919ce75f7f8ad28d261cef9d2a9c7dc1cb0fdad7
4
- data.tar.gz: 676c42fa2f8acb3c7dc03d989ab40febdd9958ee
3
+ metadata.gz: 5b7b828672ca3ca2908fc62b6f7cf25622a16322
4
+ data.tar.gz: 704c24b5374e84549277e659401621ee7044ee75
5
5
  SHA512:
6
- metadata.gz: 8e94d802c318a795d6272a5341a2183248cf355109abf6022ce3fd725c2080987028bc1037fb3134fc1e31f321468b93c21e66e4ea3d663c98cd45a69164efef
7
- data.tar.gz: 7bd562a193cde805efb2d9ee5c12f4dc10b4e9fa78b6f1ccc622d1e4cc1c7b1438f5bf3adf523ee41ddab88362fdee97301c2f6c114ac40096fbbad4b7600342
6
+ metadata.gz: a9a5b96876e221e2b43c7e5996be97ea4e14af904017d6f7e5f64e3ae978ab08b71ef3f194ad25ffe690a785ca8a8168943c290b396c6c414a957dd042be566d
7
+ data.tar.gz: 14ff8a5d98dd83cadd2821a3321a758a716eb81783d617539777093c4eae479a5b7057fb5bb364a169169d089b58bd902a03352feb6f96c8bd5ef62e7133009f
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tic_tac_toes (0.0.9)
4
+ tic_tac_toes (0.1.0)
5
5
  pg
6
6
 
7
7
  GEM
data/bin/tic_tac_toes CHANGED
@@ -5,13 +5,11 @@ $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + '/../lib')
5
5
  require 'tic_tac_toes/command_line/prompt'
6
6
  require 'tic_tac_toes/core/io'
7
7
 
8
- require 'tic_tac_toes/core/board_factory'
9
8
  require 'tic_tac_toes/core/player_factory'
10
9
  require 'tic_tac_toes/command_line/menu'
11
10
 
12
11
  require 'tic_tac_toes/database/pg_wrapper'
13
12
  require 'tic_tac_toes/core/history'
14
- require 'tic_tac_toes/core/game_state_factory'
15
13
 
16
14
  require 'tic_tac_toes/command_line/runner'
17
15
 
@@ -20,12 +18,10 @@ database = "tic_tac_toes"
20
18
  prompt = TicTacToes::CommandLine::Prompt
21
19
  io = TicTacToes::Core::IO.new(prompt)
22
20
 
23
- board_factory = TicTacToes::Core::BoardFactory.new
24
21
  player_factory = TicTacToes::Core::PlayerFactory.new(io)
25
- menu = TicTacToes::CommandLine::Menu.new(io, board_factory, player_factory)
22
+ menu = TicTacToes::CommandLine::Menu.new(io, player_factory)
26
23
 
27
24
  database_wrapper = TicTacToes::Database::PGWrapper.new(database)
28
25
  history = TicTacToes::Core::History.new(database_wrapper)
29
- game_state_factory = TicTacToes::Core::GameStateFactory.new(history)
30
26
 
31
- TicTacToes::CommandLine::Runner.new(io, menu, game_state_factory).run
27
+ TicTacToes::CommandLine::Runner.new(io, menu, history).run
@@ -1,16 +1,16 @@
1
+ require 'tic_tac_toes/core/board'
1
2
  require 'tic_tac_toes/core/rules'
2
3
 
3
4
  module TicTacToes
4
5
  module CommandLine
5
6
  class Menu
6
- def initialize(io, board_factory, player_factory)
7
+ def initialize(io, player_factory)
7
8
  @io = io
8
- @board_factory = board_factory
9
9
  @player_factory = player_factory
10
10
  end
11
11
 
12
12
  def get_board
13
- @board_factory.generate_board(get_row_size)
13
+ TicTacToes::Core::Board.new(row_size: get_row_size)
14
14
  end
15
15
 
16
16
  def get_players
@@ -1,15 +1,17 @@
1
+ require 'tic_tac_toes/core/game_state'
2
+
1
3
  module TicTacToes
2
4
  module CommandLine
3
5
  class Runner
4
- def initialize(io, menu, game_state_factory)
6
+ def initialize(io, menu, history)
5
7
  @io = io
6
8
  @menu = menu
7
- @game_state_factory = game_state_factory
9
+ @history = history
8
10
  end
9
11
 
10
12
  def run
11
13
  board, players = @menu.get_board, @menu.get_players
12
- game_state = @game_state_factory.generate_game_state(board, players)
14
+ game_state = TicTacToes::Core::GameState.new(board, players, @history)
13
15
 
14
16
  take_turn(game_state) until game_state.game_over?
15
17
  end_game(game_state)
@@ -21,7 +23,7 @@ module TicTacToes
21
23
  @io.draw_board(game_state.board)
22
24
  @io.thinking_notification if game_state.current_player.needs_to_think
23
25
 
24
- move = game_state.current_player.place_and_return_move(game_state.board, game_state.players)
26
+ move = game_state.current_player.place_and_return_move(game_state)
25
27
  game_state.turn_over(move)
26
28
  end
27
29
 
@@ -14,10 +14,18 @@ module TicTacToes
14
14
  @history.record_board_size(@board.size)
15
15
  end
16
16
 
17
+ def place_move(space)
18
+ @board.place(current_player, space)
19
+ end
20
+
17
21
  def current_player
18
22
  @players.first
19
23
  end
20
24
 
25
+ def next_player
26
+ @players[1]
27
+ end
28
+
21
29
  def turn_over(move)
22
30
  @history.record_move(move)
23
31
  @players.rotate!
@@ -18,6 +18,7 @@ module TicTacToes
18
18
 
19
19
  def record_winner(winner)
20
20
  winner = "Draw" if winner.nil?
21
+ winner = winner.token unless winner.is_a? String
21
22
  @winner = winner
22
23
  end
23
24
 
@@ -1,11 +1,9 @@
1
- require 'tic_tac_toes/core/board'
2
-
3
1
  module TicTacToes
4
2
  module Core
5
3
  module MoveStrategies
6
4
  module EasyAI
7
- def self.move(board, players)
8
- board.open_spaces.sample
5
+ def self.move(game_state)
6
+ game_state.board.open_spaces.sample
9
7
  end
10
8
  end
11
9
  end
@@ -4,14 +4,18 @@ module TicTacToes
4
4
  module Core
5
5
  module MoveStrategies
6
6
  module HardAI
7
- def self.move(board, players)
7
+ ALPHA, BETA = -1, 1
8
+
9
+ def self.move(game_state)
10
+ board = game_state.board
11
+
8
12
  return 0 if nine_board_first_move?(board)
9
13
  return second_move(board) if nine_board_second_move?(board)
10
14
 
11
15
  open_spaces = Hash[board.open_spaces.map { |space| [space, nil] }]
12
16
 
13
17
  open_spaces.each do |space, score|
14
- score = minimax(generate_board(players.first, space, board), :min, players)
18
+ score = minimax(generate_game_state(space, game_state), :min, ALPHA, BETA)
15
19
  open_spaces[space] = score
16
20
  end
17
21
 
@@ -21,43 +25,39 @@ module TicTacToes
21
25
 
22
26
  private
23
27
 
24
- def self.minimax(board, current_player, players)
25
- return score(board, players) if Rules.game_over?(board, players)
28
+ def self.minimax(game_state, current_player, alpha, beta)
29
+ return score(game_state) if game_state.game_over?
26
30
 
27
31
  if current_player == :max
28
- best_score = -1
29
- board.open_spaces.each do |space|
30
- score = minimax(generate_board(players.first, space, board), :min, players)
31
- best_score = [best_score, score].max
32
+ game_state.board.open_spaces.each do |space|
33
+ alpha = [alpha, minimax(generate_game_state(space, game_state), :min, alpha, beta)].max
34
+ break if beta <= alpha
32
35
  end
33
- best_score
34
-
36
+ alpha
35
37
  elsif current_player == :min
36
- best_score = 1
37
- board.open_spaces.each do |space|
38
- score = minimax(generate_board(players.last, space, board), :max, players)
39
- best_score = [best_score, score].min
38
+ game_state.board.open_spaces.each do |space|
39
+ beta = [beta, minimax(generate_game_state(space, game_state), :max, alpha, beta)].min
40
+ break if beta <= alpha
40
41
  end
41
- best_score
42
+ beta
42
43
  end
43
44
  end
44
45
 
45
- def self.generate_board(player, space, board)
46
- new_board = Marshal.load(Marshal.dump(board))
47
- new_board.place(player, space)
48
- new_board
46
+ def self.generate_game_state(space, game_state)
47
+ new_game_state = Marshal.load(Marshal.dump(game_state))
48
+ new_game_state.place_move(space)
49
+ new_game_state.turn_over([])
50
+ new_game_state
49
51
  end
50
52
 
51
- def self.score(board, players)
52
- own_token, opponent_token = players.first.token, players.last.token
53
-
54
- case Rules.determine_winner(board, players)
55
- when own_token
53
+ def self.score(game_state)
54
+ winner = game_state.determine_winner
55
+ if winner.nil?
56
+ 0
57
+ elsif winner.move_strategy == self
56
58
  1
57
- when opponent_token
58
- -1
59
59
  else
60
- 0
60
+ -1
61
61
  end
62
62
  end
63
63
 
@@ -6,13 +6,13 @@ module TicTacToes
6
6
  @io = io
7
7
  end
8
8
 
9
- def move(board, players)
9
+ def move(game_state)
10
10
  @io.move_solicitation
11
11
 
12
12
  Integer(@io.solicit_input)
13
13
  rescue ArgumentError
14
14
  @io.not_an_integer_error
15
- move(board, players)
15
+ move(game_state)
16
16
  end
17
17
  end
18
18
  end
@@ -5,9 +5,9 @@ module TicTacToes
5
5
  module Core
6
6
  module MoveStrategies
7
7
  module MediumAI
8
- def self.move(board, players)
8
+ def self.move(game_state)
9
9
  move_strategy = [EasyAI, HardAI, HardAI].sample
10
- move_strategy.move(board, players)
10
+ move_strategy.move(game_state)
11
11
  end
12
12
  end
13
13
  end
@@ -10,10 +10,10 @@ module TicTacToes
10
10
  @io = io
11
11
  end
12
12
 
13
- def place_and_return_move(board, players)
13
+ def place_and_return_move(game_state)
14
14
  loop do
15
- space = @move_strategy.move(board, players)
16
- break [@token, space] if board.place(self, space)
15
+ space = @move_strategy.move(game_state)
16
+ break [@token, space] if game_state.place_move(space)
17
17
  @io.invalid_move_error
18
18
  end
19
19
  end
@@ -32,7 +32,7 @@ module TicTacToes
32
32
 
33
33
  players.each do |player|
34
34
  player_has_won = win?(board, player)
35
- winner = player.token if player_has_won
35
+ winner = player if player_has_won
36
36
  end
37
37
 
38
38
  winner
@@ -4,7 +4,7 @@ module TicTacToes
4
4
  def self.make_move(game_state, move, listener)
5
5
  unless move.nil?
6
6
  move = move.to_i
7
- game_state.board.place(game_state.current_player, move)
7
+ game_state.place_move(move)
8
8
  end
9
9
 
10
10
  if game_state.game_over?
@@ -13,7 +13,7 @@ module TicTacToes
13
13
  end
14
14
 
15
15
  game_state.turn_over(move)
16
- game_state.current_player.place_and_return_move(game_state.board, game_state.players)
16
+ game_state.current_player.place_and_return_move(game_state)
17
17
 
18
18
  if game_state.game_over?
19
19
  tell_listener_game_is_over(game_state, listener)
@@ -26,13 +26,13 @@ module TicTacToes
26
26
  private
27
27
 
28
28
  def self.tell_listener_game_is_over(game_state, listener)
29
- winning_token = game_state.determine_winner
29
+ winning_player = game_state.determine_winner
30
30
 
31
- case winning_token
31
+ case winning_player
32
32
  when nil
33
33
  listener.game_ended_in_draw(game_state)
34
34
  else
35
- listener.game_ended_in_winner(game_state, winning_token)
35
+ listener.game_ended_in_winner(game_state, winning_player.token)
36
36
  end
37
37
  end
38
38
  end
@@ -1,3 +1,3 @@
1
1
  module TicTacToes
2
- VERSION = "0.0.9"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -1,5 +1,4 @@
1
1
  require 'tic_tac_toes/command_line/menu'
2
- require 'tic_tac_toes/core/board_factory'
3
2
  require 'tic_tac_toes/core/player_factory'
4
3
  require 'tic_tac_toes/core/io'
5
4
  require 'tic_tac_toes/core/move_strategies/human'
@@ -7,9 +6,8 @@ require 'tic_tac_toes/core/move_strategies/medium_ai'
7
6
 
8
7
  describe TicTacToes::CommandLine::Menu do
9
8
  let(:io) { double }
10
- let(:board_factory) { TicTacToes::Core::BoardFactory.new }
11
9
  let(:player_factory) { TicTacToes::Core::PlayerFactory.new(io) }
12
- let(:menu) { TicTacToes::CommandLine::Menu.new(io, board_factory, player_factory) }
10
+ let(:menu) { TicTacToes::CommandLine::Menu.new(io, player_factory) }
13
11
 
14
12
  describe '#get_board' do
15
13
  let(:invalid_row_size) { 11 }
@@ -1,38 +1,4 @@
1
1
  require 'tic_tac_toes/command_line/runner'
2
2
 
3
3
  describe TicTacToes::CommandLine::Runner do
4
- describe '#run' do
5
- let(:game_state) { double(board: 'board', players: 'players', game_over: true) }
6
-
7
- let(:io) { double(draw_board: true,
8
- thinking_notification: true,
9
- game_over_notification: true) }
10
- let(:menu) { double(get_board: true,
11
- get_players: true) }
12
- let(:game_state) { double(board: 'board',
13
- players: 'players',
14
- game_over: true,
15
- game_over?: true,
16
- determine_winner: true) }
17
- let(:game_state_factory) { double(generate_game_state: game_state) }
18
-
19
- let(:runner) { TicTacToes::CommandLine::Runner.new(io, menu, game_state_factory) }
20
-
21
- it 'gets an initial game state' do
22
- expect(game_state_factory).to receive(:generate_game_state)
23
- runner.run
24
- end
25
-
26
- it 'takes turns until the game is over' do
27
- allow(game_state).to receive(:game_over?).and_return(false, true)
28
-
29
- expect(runner).to receive(:take_turn).once
30
- runner.run
31
- end
32
-
33
- it 'ends the game when the game is over' do
34
- expect(runner).to receive(:end_game)
35
- runner.run
36
- end
37
- end
38
4
  end
@@ -13,6 +13,41 @@ describe TicTacToes::Core::GameState do
13
13
  end
14
14
  end
15
15
 
16
+ describe '#place_move' do
17
+ let(:x) { double("human player", token: "x") }
18
+ let(:o) { double("computer player", token: "o") }
19
+ let(:players) { [x, o] }
20
+
21
+ let(:history) { double(record_board_size: true) }
22
+
23
+ it 'returns nil if the board’s space is not nil' do
24
+ board = TicTacToes::TestBoardGenerator.generate([ x, nil, nil,
25
+ nil, nil, nil,
26
+ nil, nil, nil])
27
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
28
+
29
+ expect(game_state.place_move(0)).to eq(nil)
30
+ end
31
+
32
+ it 'returns nil if it isn’t in its board’s spaces range' do
33
+ board = TicTacToes::TestBoardGenerator.generate([nil, nil, nil,
34
+ nil, nil, nil,
35
+ nil, nil, nil])
36
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
37
+ expect(game_state.place_move(9)).to eq(nil)
38
+ end
39
+
40
+ it 'places the current player at the space if the space is nil and in the board’s spaces range' do
41
+ board = TicTacToes::TestBoardGenerator.generate([nil, nil, nil,
42
+ nil, nil, nil,
43
+ nil, nil, nil])
44
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
45
+
46
+ game_state.place_move(0)
47
+ expect(game_state.board.space(0)).to eq(game_state.current_player)
48
+ end
49
+ end
50
+
16
51
  describe '#current_player' do
17
52
  it 'returns the first item of its players array' do
18
53
  history = double(record_board_size: true)
@@ -24,6 +59,17 @@ describe TicTacToes::Core::GameState do
24
59
  end
25
60
  end
26
61
 
62
+ describe '#next_player' do
63
+ it 'returns the second item of its players array' do
64
+ history = double(record_board_size: true)
65
+ players = ['first_player', 'second_player']
66
+ game_state = TicTacToes::Core::GameState.new('board', players, history)
67
+
68
+ next_player = game_state.next_player
69
+ expect(next_player).to eq('second_player')
70
+ end
71
+ end
72
+
27
73
  describe '#turn_over' do
28
74
  it 'records the last move' do
29
75
  move = double
@@ -47,21 +93,21 @@ describe TicTacToes::Core::GameState do
47
93
 
48
94
  describe '#game_over' do
49
95
  it 'records the winner' do
50
- winner = double
96
+ winning_player = double
51
97
  history = double(record_board_size: true, persist: true)
52
98
  game_state = TicTacToes::Core::GameState.new('board', 'players', history)
53
99
 
54
- expect(history).to receive(:record_winner).with(winner)
55
- game_state.game_over(winner)
100
+ expect(history).to receive(:record_winner).with(winning_player)
101
+ game_state.game_over(winning_player)
56
102
  end
57
103
 
58
104
  it 'persists its history' do
59
- winner = double
105
+ winning_player = double(token: 'x')
60
106
  history = double(record_board_size: true, record_winner: true)
61
107
  game_state = TicTacToes::Core::GameState.new('board', 'players', history)
62
108
 
63
109
  expect(history).to receive(:persist)
64
- game_state.game_over(winner)
110
+ game_state.game_over(winning_player)
65
111
  end
66
112
  end
67
113
 
@@ -76,6 +122,7 @@ describe TicTacToes::Core::GameState do
76
122
  board = TicTacToes::TestBoardGenerator.generate([x, x, o,
77
123
  o, o, nil,
78
124
  x, o, x])
125
+
79
126
  game_state = TicTacToes::Core::GameState.new(board, players, history)
80
127
  expect(game_state.game_over?).to be false
81
128
  end
@@ -113,7 +160,8 @@ describe TicTacToes::Core::GameState do
113
160
  winning_token = "o"
114
161
  game_state = TicTacToes::Core::GameState.new(board, players, history)
115
162
 
116
- expect(game_state.determine_winner).to eql(winning_token)
163
+ winning_player = game_state.determine_winner
164
+ expect(winning_player.token).to eq(winning_token)
117
165
  end
118
166
 
119
167
  it "returns nil if there is not a winner" do
@@ -125,5 +173,4 @@ describe TicTacToes::Core::GameState do
125
173
  expect(game_state.determine_winner).to be_nil
126
174
  end
127
175
  end
128
-
129
176
  end
@@ -23,11 +23,11 @@ describe TicTacToes::Core::History do
23
23
  end
24
24
 
25
25
  describe '#record_winner' do
26
- it "records the passed winner" do
27
- token = "O"
26
+ it "records the token of the passed winner" do
27
+ winner = double(token: 'O')
28
28
 
29
- history.record_winner(token)
30
- expect(history.winner).to eq(token)
29
+ history.record_winner(winner)
30
+ expect(history.winner).to eq(winner.token)
31
31
  end
32
32
  end
33
33
 
@@ -1,18 +1,25 @@
1
1
  require 'tic_tac_toes/test_board_generator'
2
+ require 'tic_tac_toes/core/game_state'
2
3
  require 'tic_tac_toes/core/move_strategies/easy_ai'
4
+ require 'tic_tac_toes/core/player_factory'
3
5
 
4
6
  describe TicTacToes::Core::MoveStrategies::EasyAI do
5
7
  describe '#move' do
6
- let(:players) { double("players") }
7
- let(:easy_ai) { TicTacToes::Core::MoveStrategies::EasyAI }
8
-
9
8
  it "returns a randomly-selected valid move" do
10
- board = TicTacToes::TestBoardGenerator.generate([ :O, nil, nil,
11
- nil, :X, nil,
12
- nil, :X, nil])
9
+ player_factory = TicTacToes::Core::PlayerFactory.new('unused_io')
10
+ x = player_factory.generate_human_player('x')
11
+ o = player_factory.generate_computer_player('o', :hard)
12
+ players = [x, o]
13
+
14
+ board = TicTacToes::TestBoardGenerator.generate([ o, nil, nil,
15
+ nil, x, nil,
16
+ nil, x, nil])
17
+
18
+ history = double(record_board_size: true)
19
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
13
20
  valid_moves = [1, 2, 3, 5, 6, 8]
14
21
 
15
- move = easy_ai.move(board, players)
22
+ move = TicTacToes::Core::MoveStrategies::EasyAI.move(game_state)
16
23
  expect(valid_moves).to include(move)
17
24
  end
18
25
  end
@@ -1,129 +1,144 @@
1
1
  require 'tic_tac_toes/test_board_generator'
2
2
  require 'tic_tac_toes/core/move_strategies/hard_ai'
3
+ require 'tic_tac_toes/core/game_state'
3
4
  require 'tic_tac_toes/core/player'
4
5
 
5
6
  describe TicTacToes::Core::MoveStrategies::HardAI do
7
+ let(:alpha) { TicTacToes::Core::MoveStrategies::HardAI::ALPHA }
8
+ let(:beta) { TicTacToes::Core::MoveStrategies::HardAI::BETA }
6
9
  let(:hard_ai) { TicTacToes::Core::MoveStrategies::HardAI }
7
10
  let(:x) { TicTacToes::Core::Player.new("human", "x", false, "io") }
8
11
  let(:o) { TicTacToes::Core::Player.new(hard_ai, "o", true, "io") }
9
12
  let(:players) { [o, x] }
10
-
13
+ let(:history) { TicTacToes::UI::NullHistory.new }
11
14
 
12
15
  describe '#move' do
13
- it "returns the best move" do
16
+ it 'returns the best move' do
14
17
  board = TicTacToes::TestBoardGenerator.generate([x, nil, nil,
15
18
  o, o, nil,
16
19
  x, nil, x])
20
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
17
21
  best_move = 5
18
22
 
19
- expect(hard_ai.move(board, players)).to eql(best_move)
23
+ expect(hard_ai.move(game_state)).to eql(best_move)
20
24
  end
21
25
 
22
- context "when playing on a 3x3 board" do
26
+ context 'when playing on a 3x3 board' do
23
27
  it 'returns 0 when making the first move' do
24
28
  board = TicTacToes::TestBoardGenerator.generate([nil, nil, nil,
25
29
  nil, nil, nil,
26
30
  nil, nil, nil])
31
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
27
32
 
28
- expect(hard_ai.move(board, players)).to eq(0)
33
+ expect(hard_ai.move(game_state)).to eq(0)
29
34
  end
30
35
 
31
- it "returns 4 when the opponent’s first move was a corner" do
36
+ it 'returns 4 when the opponent’s first move was a corner' do
32
37
  board = TicTacToes::TestBoardGenerator.generate([nil, nil, nil,
33
38
  nil, nil, nil,
34
39
  nil, nil, x])
40
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
35
41
 
36
- expect(hard_ai.move(board, players)).to eq(4)
42
+ expect(hard_ai.move(game_state)).to eq(4)
37
43
  end
38
44
 
39
- it "returns 4 when the opponent’s first move was an edge" do
45
+ it 'returns 4 when the opponent’s first move was an edge' do
40
46
  board = TicTacToes::TestBoardGenerator.generate([nil, nil, nil,
41
47
  nil, nil, x,
42
48
  nil, nil, nil])
49
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
43
50
 
44
- expect(hard_ai.move(board, players)).to eq(4)
51
+ expect(hard_ai.move(game_state)).to eq(4)
45
52
  end
46
53
 
47
- it "returns 0 when the opponent’s first move was the center" do
54
+ it 'returns 0 when the opponent’s first move was the center' do
48
55
  board = TicTacToes::TestBoardGenerator.generate([nil, nil, nil,
49
56
  nil, x, nil,
50
57
  nil, nil, nil])
58
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
51
59
 
52
- expect(hard_ai.move(board, players)).to eq(0)
60
+ expect(hard_ai.move(game_state)).to eq(0)
53
61
  end
54
62
  end
55
63
  end
56
64
 
57
65
 
58
66
  describe '#minimax' do
59
- it "returns the correct score for a pre-win board" do
67
+ it 'returns the correct score for a pre-win board' do
60
68
  board = TicTacToes::TestBoardGenerator.generate([x, nil, nil,
61
69
  o, o, nil,
62
70
  x, nil, x])
71
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
63
72
  win_score = 1
64
73
 
65
- expect(hard_ai.minimax(board, :max, players)).to eql(win_score)
74
+ expect(hard_ai.minimax(game_state, :max, alpha, beta)).to eql(win_score)
66
75
  end
67
76
 
68
- it "returns the correct score for a pre-loss board" do
77
+ it 'returns the correct score for a pre-loss board' do
69
78
  board = TicTacToes::TestBoardGenerator.generate([ o, o, x,
70
79
  nil, nil, nil,
71
80
  x, nil, x])
81
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
72
82
  loss_score = -1
73
83
 
74
- expect(hard_ai.minimax(board, :max, players)).to eql(loss_score)
84
+ expect(hard_ai.minimax(game_state, :max, alpha, beta)).to eql(loss_score)
75
85
  end
76
86
 
77
- it "returns the correct score for a pre-draw board" do
87
+ it 'returns the correct score for a pre-draw board' do
78
88
  board = TicTacToes::TestBoardGenerator.generate([x, x, o,
79
89
  o, nil, x,
80
90
  x, o, x])
91
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
81
92
  draw_score = 0
82
93
 
83
- expect(hard_ai.minimax(board, :max, players)).to eql(draw_score)
94
+ expect(hard_ai.minimax(game_state, :max, alpha, beta)).to eql(draw_score)
84
95
  end
85
96
  end
86
97
 
87
98
 
88
- describe '#generate_board' do
89
- it "returns a board based on a token, a space, and an existing board" do
90
- token, space = o, 3
99
+ describe '#generate_game_state' do
100
+ it 'returns a game state based on a space and an existing game state' do
101
+ space, token = 3, 'o'
91
102
  board = TicTacToes::TestBoardGenerator.generate([ x, nil, nil,
92
103
  nil, o, nil,
93
104
  x, nil, nil])
105
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
94
106
 
95
- new_board = hard_ai.generate_board(token, space, board)
96
- expect(new_board.space(space)).to eql(token)
107
+ new_game_state = hard_ai.generate_game_state(space, game_state)
108
+ expect(new_game_state.board.space(space).token).to eq(token)
97
109
  end
98
110
  end
99
111
 
100
112
 
101
113
  describe '#score' do
102
- it "returns the correct score when HardAI has won" do
114
+ it 'returns the correct score when HardAI has won' do
103
115
  board = TicTacToes::TestBoardGenerator.generate([ o, nil, nil,
104
116
  nil, o, nil,
105
117
  nil, nil, o])
118
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
106
119
  win_score = 1
107
120
 
108
- expect(hard_ai.score(board, players)).to eql(win_score)
121
+ expect(hard_ai.score(game_state)).to eq(win_score)
109
122
  end
110
123
 
111
- it "returns the correct score when no one has won" do
124
+ it 'returns the correct score when no one has won' do
112
125
  board = TicTacToes::TestBoardGenerator.generate([o, o, x,
113
126
  x, x, o,
114
127
  o, x, o])
128
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
115
129
  draw_score = 0
116
130
 
117
- expect(hard_ai.score(board, players)).to eql(draw_score)
131
+ expect(hard_ai.score(game_state)).to eql(draw_score)
118
132
  end
119
133
 
120
- it "returns the correct score when the opponent has won" do
134
+ it 'returns the correct score when the opponent has won' do
121
135
  board = TicTacToes::TestBoardGenerator.generate([ x, nil, nil,
122
136
  nil, x, nil,
123
137
  nil, nil, x])
138
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
124
139
  loss_score = -1
125
140
 
126
- expect(hard_ai.score(board, players)).to eql(loss_score)
141
+ expect(hard_ai.score(game_state)).to eql(loss_score)
127
142
  end
128
143
  end
129
144
  end
@@ -2,29 +2,29 @@ require 'tic_tac_toes/core/move_strategies/human'
2
2
 
3
3
  describe TicTacToes::Core::MoveStrategies::Human do
4
4
  describe '#move' do
5
- let(:io) { double("io", solicit_input: 0, move_solicitation: true, not_an_integer_error: true) }
5
+ let(:io) { double('io', solicit_input: 0, move_solicitation: true, not_an_integer_error: true) }
6
6
  let(:human) { TicTacToes::Core::MoveStrategies::Human.new(io) }
7
7
 
8
- it "displays a move solicitation" do
8
+ it 'displays a move solicitation' do
9
9
  expect(io).to receive(:move_solicitation)
10
- human.move("board", "players")
10
+ human.move('game_state')
11
11
  end
12
12
 
13
13
  context 'when given not-integer-like input' do
14
- let(:not_integer_like) { "string" }
15
- let(:integer_like) { "100" }
14
+ let(:not_integer_like) { 'string' }
15
+ let(:integer_like) { '100' }
16
16
 
17
- it "displays a not an integer error" do
17
+ it 'displays a not an integer error' do
18
18
  allow(io).to receive(:solicit_input).and_return(not_integer_like, integer_like)
19
19
 
20
20
  expect(io).to receive(:not_an_integer_error).once
21
- human.move("board", "players")
21
+ human.move('game_state')
22
22
  end
23
23
 
24
- it "only returns a move (converted to integer) once it gets integer-like input" do
24
+ it 'only returns a move (converted to integer) once it gets integer-like input' do
25
25
  allow(io).to receive(:solicit_input).and_return(not_integer_like, integer_like)
26
26
 
27
- expect(human.move("board", "players")).to eq(100)
27
+ expect(human.move('game_state')).to eq(100)
28
28
  end
29
29
  end
30
30
  end
@@ -1,20 +1,25 @@
1
1
  require 'tic_tac_toes/test_board_generator'
2
+ require 'tic_tac_toes/core/game_state'
2
3
  require 'tic_tac_toes/core/move_strategies/medium_ai'
4
+ require 'tic_tac_toes/core/player_factory'
3
5
 
4
6
  describe TicTacToes::Core::MoveStrategies::MediumAI do
5
- let(:medium_ai) { TicTacToes::Core::MoveStrategies::MediumAI }
6
- let(:x) { TicTacToes::Core::Player.new("human", "x", false, "io") }
7
- let(:o) { TicTacToes::Core::Player.new(medium_ai, "o", true, "io") }
8
- let(:players) { [x, o] }
9
-
10
7
  describe '#move' do
11
8
  it "returns a valid move (based on either EasyAI or HardAI)" do
12
- board = TicTacToes::TestBoardGenerator.generate([ o, o, x,
13
- nil, x, nil,
14
- nil, x, nil])
9
+ player_factory = TicTacToes::Core::PlayerFactory.new('unused_io')
10
+ x = player_factory.generate_human_player('x')
11
+ o = player_factory.generate_computer_player('o', :hard)
12
+ players = [x, o]
13
+
14
+ board = TicTacToes::TestBoardGenerator.generate([ o, o, x,
15
+ nil, x, nil,
16
+ nil, x, nil])
17
+
18
+ history = TicTacToes::UI::NullHistory.new
19
+ game_state = TicTacToes::Core::GameState.new(board, players, history)
15
20
  valid_moves = [3, 5, 6, 8]
16
21
 
17
- move = medium_ai.move(board, players)
22
+ move = TicTacToes::Core::MoveStrategies::MediumAI.move(game_state)
18
23
  expect(valid_moves).to include(move)
19
24
  end
20
25
  end
@@ -2,31 +2,34 @@ require 'tic_tac_toes/core/board'
2
2
  require 'tic_tac_toes/core/player'
3
3
 
4
4
  describe TicTacToes::Core::Player do
5
- let(:board) { TicTacToes::Core::Board.new(row_size: 3) }
6
- let(:io) { double("io", invalid_move_error: true) }
7
- let(:players) { double("players") }
5
+ let(:board) { TicTacToes::Core::Board.new(row_size: 3) }
8
6
 
9
- describe '#place_and_return_move' do
10
- let(:move_strategy) { double("move_strategy") }
11
- let(:token) { "X" }
12
- let(:needs_to_think) { false }
7
+ let(:io) { double(invalid_move_error: true) }
8
+
9
+ let(:move_strategy) { double }
10
+ let(:x) { TicTacToes::Core::Player.new(move_strategy, 'x', false, io) }
11
+ let(:o) { double }
12
+ let(:players) { [x, o] }
13
+
14
+ let(:history) { double(record_board_size: true) }
13
15
 
14
- let(:player) { TicTacToes::Core::Player.new(move_strategy, token, needs_to_think, io) }
15
-
16
- it "only places/returns a move once it receives a valid move" do
16
+ let(:game_state) { TicTacToes::Core::GameState.new(board, players, history) }
17
+
18
+ describe '#place_and_return_move' do
19
+ it 'only places/returns a move once it receives a valid move' do
17
20
  invalid_space, valid_space = 9, 0
18
21
  allow(move_strategy).to receive(:move).and_return(invalid_space, valid_space)
19
22
 
20
- player.place_and_return_move(board, players)
21
- expect(board.space(valid_space)).to eq(player)
23
+ x.place_and_return_move(game_state)
24
+ expect(board.space(valid_space)).to eq(x)
22
25
  end
23
26
 
24
- it "displays an invalid move error when given an invalid move" do
27
+ it 'displays an invalid move error when given an invalid move' do
25
28
  invalid_space, valid_space = 9, 0
26
29
  allow(move_strategy).to receive(:move).and_return(invalid_space, valid_space)
27
30
 
28
31
  expect(io).to receive(:invalid_move_error)
29
- player.place_and_return_move(board, players)
32
+ x.place_and_return_move(game_state)
30
33
  end
31
34
  end
32
35
  end
@@ -86,13 +86,14 @@ describe TicTacToes::Core::Rules do
86
86
 
87
87
 
88
88
  describe '#determine_winner' do
89
- it "returns the winning token when there is a winner" do
89
+ it "returns the winning player when there is a winner" do
90
90
  board = TicTacToes::TestBoardGenerator.generate([ o, nil, nil,
91
91
  nil, o, nil,
92
92
  nil, nil, o])
93
93
  winning_token = "o"
94
94
 
95
- expect(rules.determine_winner(board, players)).to eql(winning_token)
95
+ winning_player = rules.determine_winner(board, players)
96
+ expect(winning_player.token).to eql(winning_token)
96
97
  end
97
98
 
98
99
  it "returns nil if there is not a winner" do
@@ -5,12 +5,12 @@ require 'tic_tac_toes/ui/adapter'
5
5
 
6
6
  describe TicTacToes::UI::Adapter do
7
7
  describe '#make_move' do
8
- let(:history) { double(record_board_size: true,
9
- record_move: true) }
8
+ let(:history) { TicTacToes::UI::NullHistory.new }
9
+
10
10
  player_factory = TicTacToes::Core::PlayerFactory.new('unused_io')
11
- let(:x) { player_factory.generate_human_player('x') }
12
- let(:o) { player_factory.generate_computer_player('o', :hard) }
13
- let(:players) { [x, o] }
11
+ let(:x) { player_factory.generate_human_player('x') }
12
+ let(:o) { player_factory.generate_computer_player('o', :hard) }
13
+ let(:players) { [x, o] }
14
14
 
15
15
  context 'when the game is still in progress' do
16
16
  it 'sends its listener #moves_were_made with the updated game state' do
@@ -25,8 +25,8 @@ describe TicTacToes::UI::Adapter do
25
25
  TicTacToes::UI::Adapter.make_move(game_state, move, listener)
26
26
 
27
27
  first_move, second_move = 2, 1
28
- game_state.board.place(x, first_move)
29
- game_state.board.place(o, second_move)
28
+ board.place(x, first_move)
29
+ board.place(o, second_move)
30
30
  expect(listener).to have_received(:moves_were_made).with(game_state)
31
31
  end
32
32
  end
@@ -44,7 +44,7 @@ describe TicTacToes::UI::Adapter do
44
44
  allow(listener).to receive(:game_ended_in_winner)
45
45
  TicTacToes::UI::Adapter.make_move(game_state, move, listener)
46
46
 
47
- game_state.board.place(x, 2)
47
+ board.place(x, 2)
48
48
  expect(listener).to have_received(:game_ended_in_winner).with(game_state, winning_token)
49
49
  end
50
50
  end
@@ -61,7 +61,7 @@ describe TicTacToes::UI::Adapter do
61
61
  allow(listener).to receive(:game_ended_in_draw)
62
62
  TicTacToes::UI::Adapter.make_move(game_state, move, listener)
63
63
 
64
- game_state.board.place(x, 8)
64
+ board.place(x, 8)
65
65
  expect(listener).to have_received(:game_ended_in_draw).with(game_state)
66
66
  end
67
67
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tic_tac_toes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Spatafora
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-29 00:00:00.000000000 Z
11
+ date: 2014-07-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -90,9 +90,7 @@ files:
90
90
  - lib/tic_tac_toes/command_line/prompt.rb
91
91
  - lib/tic_tac_toes/command_line/runner.rb
92
92
  - lib/tic_tac_toes/core/board.rb
93
- - lib/tic_tac_toes/core/board_factory.rb
94
93
  - lib/tic_tac_toes/core/game_state.rb
95
- - lib/tic_tac_toes/core/game_state_factory.rb
96
94
  - lib/tic_tac_toes/core/history.rb
97
95
  - lib/tic_tac_toes/core/io.rb
98
96
  - lib/tic_tac_toes/core/move_strategies/easy_ai.rb
@@ -111,9 +109,7 @@ files:
111
109
  - lib/tic_tac_toes/version.rb
112
110
  - spec/tic_tac_toes/command_line/menu_spec.rb
113
111
  - spec/tic_tac_toes/command_line/runner_spec.rb
114
- - spec/tic_tac_toes/core/board_factory_spec.rb
115
112
  - spec/tic_tac_toes/core/board_spec.rb
116
- - spec/tic_tac_toes/core/game_state_factory_spec.rb
117
113
  - spec/tic_tac_toes/core/game_state_spec.rb
118
114
  - spec/tic_tac_toes/core/history_spec.rb
119
115
  - spec/tic_tac_toes/core/io_spec.rb
@@ -157,9 +153,7 @@ summary: The game Tic-tac-toe, featuring an unbeatable AI
157
153
  test_files:
158
154
  - spec/tic_tac_toes/command_line/menu_spec.rb
159
155
  - spec/tic_tac_toes/command_line/runner_spec.rb
160
- - spec/tic_tac_toes/core/board_factory_spec.rb
161
156
  - spec/tic_tac_toes/core/board_spec.rb
162
- - spec/tic_tac_toes/core/game_state_factory_spec.rb
163
157
  - spec/tic_tac_toes/core/game_state_spec.rb
164
158
  - spec/tic_tac_toes/core/history_spec.rb
165
159
  - spec/tic_tac_toes/core/io_spec.rb
@@ -1,11 +0,0 @@
1
- require 'tic_tac_toes/core/board'
2
-
3
- module TicTacToes
4
- module Core
5
- class BoardFactory
6
- def generate_board(row_size)
7
- Board.new(row_size: row_size)
8
- end
9
- end
10
- end
11
- end
@@ -1,15 +0,0 @@
1
- require 'tic_tac_toes/core/game_state'
2
-
3
- module TicTacToes
4
- module Core
5
- class GameStateFactory
6
- def initialize(history)
7
- @history = history
8
- end
9
-
10
- def generate_game_state(board, players)
11
- GameState.new(board, players, @history)
12
- end
13
- end
14
- end
15
- end
@@ -1,13 +0,0 @@
1
- require 'tic_tac_toes/core/board_factory'
2
-
3
- describe TicTacToes::Core::BoardFactory do
4
- describe '#generate_board' do
5
- it 'returns a board object generated from a row size' do
6
- board_factory = TicTacToes::Core::BoardFactory.new
7
- row_size = 5
8
-
9
- board = board_factory.generate_board(row_size)
10
- expect(board.row_size).to eq(row_size)
11
- end
12
- end
13
- end
@@ -1,15 +0,0 @@
1
- require 'tic_tac_toes/core/game_state_factory'
2
-
3
- describe TicTacToes::Core::GameStateFactory do
4
- describe '#generate_game_state' do
5
- it 'returns a game state object generated from a board object and player array' do
6
- history = double(record_board_size: true)
7
- game_state_factory = TicTacToes::Core::GameStateFactory.new(history)
8
- board, players = double(size: 3), double
9
-
10
- game_state = game_state_factory.generate_game_state(board, players)
11
- expect(game_state.board).to eq(board)
12
- expect(game_state.players).to eq(players)
13
- end
14
- end
15
- end