erics_tic_tac_toe 0.1.0 → 0.5.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.
@@ -0,0 +1,19 @@
1
+ require 'test_helper'
2
+
3
+ class GamePresentorTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ @game = TicTacToe::Game.new
6
+ @presentor = TicTacToe::GamePresentor.new(@game)
7
+ end
8
+
9
+ def test_grid
10
+ assert_equal [%w(1 2 3), %w(4 5 6), %w(7 8 9)], @presentor.grid
11
+ end
12
+
13
+ def test_grid_with_moves
14
+ @game = TicTacToe::Game.new([['x', nil, nil], [nil, nil, nil], [nil, nil, nil]])
15
+ @presentor = TicTacToe::GamePresentor.new(@game)
16
+
17
+ assert_equal [%w(x 2 3), %w(4 5 6), %w(7 8 9)], @presentor.grid
18
+ end
19
+ end
data/test/game_test.rb ADDED
@@ -0,0 +1,79 @@
1
+ require 'test_helper'
2
+
3
+
4
+ class GameTest < MiniTest::Unit::TestCase
5
+
6
+ class PlayerMock
7
+ def initialize(move)
8
+ @move = move
9
+ end
10
+
11
+ def get_move(_board)
12
+ move = @move
13
+ @move = nil
14
+ move
15
+ end
16
+
17
+ def has_next_move?
18
+ !!@move
19
+ end
20
+
21
+ def letter
22
+ 'x'
23
+ end
24
+ end
25
+
26
+ def test_grid
27
+ game = TicTacToe::Game.new
28
+
29
+ assert_equal game.grid, empty_grid
30
+ end
31
+
32
+ def test_an_empty_game_is_not_solved
33
+ game = TicTacToe::Game.new
34
+
35
+ refute game.solved?
36
+ end
37
+
38
+ def test_a_full_grid_is_cats
39
+ # x o x
40
+ # x o x
41
+ # o x o
42
+ game = TicTacToe::Game.new([%w(x o x), %w(x o x), %w(o x o)])
43
+
44
+ assert game.cats?
45
+ end
46
+
47
+ def test_winner
48
+ game = TicTacToe::Game.new([%w(x x x), [nil,nil,nil], [nil, nil, nil]],
49
+ PlayerMock.new(nil), PlayerMock.new(nil))
50
+
51
+ assert_equal 'x', game.winner
52
+ end
53
+
54
+ def test_setting_a_players_move
55
+ game = TicTacToe::Game.new(empty_grid, PlayerMock.new('1'), PlayerMock.new(nil))
56
+ game.start
57
+
58
+ assert_equal 'x', game.board.get_cell(0, 0)
59
+ end
60
+
61
+ def test_set_move_via_computer
62
+ game = TicTacToe::Game.new(empty_grid, PlayerMock.new([0,0]), PlayerMock.new([2,2]))
63
+ game.start
64
+
65
+ assert_equal 7, game.board.empty_positions.size
66
+ end
67
+
68
+ def test_skip_a_players_turn
69
+ game = TicTacToe::Game.new(empty_grid, PlayerMock.new([0,0]), PlayerMock.new(nil))
70
+ game.start
71
+
72
+ assert game.board.only_one?
73
+ end
74
+
75
+
76
+ def empty_grid(size=3)
77
+ Array.new(size) { Array.new(size) { nil } }
78
+ end
79
+ end
@@ -0,0 +1,12 @@
1
+ require 'test_helper'
2
+
3
+ class PlayerPresenterTest < MiniTest::Unit::TestCase
4
+ def setup
5
+ @player_mock = OpenStruct.new(letter: 'x', type: 'mock')
6
+ end
7
+
8
+ def test_move_json
9
+ assert_equal({letter: 'x', type: 'mock', move: '1'}.to_json,
10
+ TicTacToe::PlayerPresenter.new(@player_mock).move_json('1'))
11
+ end
12
+ end
@@ -0,0 +1,57 @@
1
+ require 'test_helper'
2
+
3
+ class BoardMock; end
4
+
5
+ class PlayerTest < MiniTest::Unit::TestCase
6
+
7
+ def test_build_human_player
8
+ assert_equal TicTacToe::HumanPlayer,
9
+ TicTacToe::Player.build('type' => 'human').class
10
+ end
11
+
12
+ def test_build_computer_player
13
+ assert_equal TicTacToe::ComputerPlayer,
14
+ TicTacToe::Player.build('type' => 'computer').class
15
+ end
16
+ end
17
+
18
+ module SharedPlayerTests
19
+ def test_gets_move
20
+ assert_equal [0, 0], @player.get_move(BoardMock.new)
21
+ end
22
+
23
+ def test_letter
24
+ assert_equal 'x', @player.letter
25
+ end
26
+
27
+ def test_has_next_move
28
+ assert @player.has_next_move?
29
+ end
30
+
31
+ def test_has_type
32
+ refute @player.type.nil?
33
+ end
34
+
35
+ end
36
+
37
+ class HumanPlayerTest < MiniTest::Unit::TestCase
38
+ def setup
39
+ @player = TicTacToe::HumanPlayer.new('letter' => "x", 'move' => [0,0])
40
+ end
41
+
42
+ include SharedPlayerTests
43
+ end
44
+
45
+
46
+ class ComputerPlayerTest < MiniTest::Unit::TestCase
47
+ class SolverMock
48
+ def solve
49
+ [0, 0]
50
+ end
51
+ end
52
+ def setup
53
+ @player = TicTacToe::ComputerPlayer.new({'letter' => "x"}, SolverMock)
54
+ end
55
+
56
+ include SharedPlayerTests
57
+ end
@@ -0,0 +1,53 @@
1
+ require 'test_helper'
2
+
3
+ class PotentialStateTest < MiniTest::Unit::TestCase
4
+ def test_solved
5
+ set_grid([ ['x', 'x', 'x'], [nil, nil, nil], [nil, nil, nil] ])
6
+ assert @state.solved?
7
+ end
8
+
9
+ def test_fork_exists
10
+ set_grid([ ['x', nil, 'x'], [nil, 'x', nil], [nil, nil, nil] ])
11
+ assert @state.fork_exists?
12
+
13
+ set_grid([ ['x', nil, 'x'], [nil, 'o', nil], ['x', nil, nil] ])
14
+ assert @state.fork_exists?
15
+
16
+ set_grid([ ['o', nil, 'o'], [nil, 'x', nil], ['o', nil, nil] ])
17
+ refute @state.fork_exists?
18
+
19
+ end
20
+
21
+ def test_forking_positions
22
+ # o | 2 | x |
23
+ # 4 | o | 6 |
24
+ # x | 8 | 9 |
25
+ set_grid([ ['o', nil, 'x'], [nil, 'o', nil], ['x', nil, nil] ])
26
+ assert @state.forking_positions.size == 1
27
+ end
28
+
29
+ def test_can_win_next_turn
30
+ set_grid([[ 'x', nil, 'x'], [nil, nil, nil], [nil, nil, nil] ])
31
+ assert @state.can_win_next_turn?
32
+
33
+ # 1 | 2 | x
34
+ # 4 | o | 6
35
+ # 7 | x | x
36
+ set_grid([[ nil, nil, 'x'], [nil, 'o', nil], [nil, 'x', 'x'] ])
37
+ assert @state.can_win_next_turn?
38
+
39
+ # o | 2 | x |
40
+ # 4 | o | 6 |
41
+ # x | 8 | 9 |
42
+ set_grid([ ['o', nil, 'x'], [nil, 'o', nil], ['x', nil, nil] ])
43
+ refute @state.can_win_next_turn?
44
+ end
45
+
46
+ private
47
+
48
+ def set_grid(grid)
49
+ board = TicTacToe::Board.new
50
+ board.grid = grid
51
+ @state = TicTacToe::ThreeByThree::PotentialState.new(board, 'x')
52
+ end
53
+ end
data/test/solver_test.rb CHANGED
@@ -1,131 +1,140 @@
1
1
  require 'test_helper'
2
2
 
3
- class SolverTest < MiniTest::Unit::TestCase
4
- def setup
5
- TicTacToe::Solver.instance_eval { attr_accessor :strategy } # For testing
6
- @solver = TicTacToe::Solver.new(TicTacToe::Board.new, 'x')
7
- @solver.strategy.class.instance_eval { attr_accessor :implementation } # For testing
8
- @solver.strategy.implementation.class.instance_eval { attr_accessor :board } # For testing
3
+ module SharedSolverTests
4
+ def test_win_horizontally
5
+ refute @board.solved?
6
+
7
+ # Will win, when there is a winning move
8
+ set_grid([[ 'x', 'x', nil],
9
+ [ 'o', 'o', nil],
10
+ [ 'x', 'o', nil] ])
11
+ refute @board.solved? # Is not solved yet
12
+ solve!
13
+ assert !!@board.solved?, "Could not solve the board" # Solved it
14
+ assert_equal 'x', @board.get_cell(2, 0) # Placed the right letter
9
15
  end
10
16
 
11
- def test_win_next_move
12
- refute board.solved?
17
+ def test_wins_across
13
18
 
14
- # Will win, when there is a winning move
15
- set_grid([[ 'x', 'x', nil], [nil, nil, nil], [nil, nil, nil] ])
16
- refute board.solved? # Is not solved yet
17
- @solver.next_move!
18
- assert board.solved? # Solved it
19
- assert_equal 'x', board.get_cell(2, 0) # Placed the right letter
20
-
21
- set_grid([[ 'x', nil, nil], [nil, 'x', nil], [nil, nil, nil] ])
22
- refute board.solved?
23
- @solver.next_move!
24
- assert board.solved?
25
- assert_equal 'x', board.get_cell(2, 2)
19
+ set_grid([[ 'x', 'o', nil],
20
+ [ nil, 'x', nil],
21
+ [ 'o', nil, nil] ])
22
+ refute @board.solved?
23
+ solve!
24
+ assert !!@board.solved?
25
+ assert_equal 'x', @board.get_cell(2, 2)
26
+ end
27
+
28
+ def test_wins_vertically
26
29
 
27
- set_grid([[ 'x', nil, nil], [nil, nil, nil], ['x', nil, nil] ])
28
- refute board.solved?
29
- @solver.next_move!
30
- assert board.solved?
31
- assert_equal 'x', board.get_cell(0, 1)
30
+ set_grid([[ 'x', nil, 'o'],
31
+ [nil, 'o', 'o'],
32
+ [ 'x', nil, nil] ])
33
+ refute @board.solved?
34
+ solve!
35
+ assert !!@board.solved?
36
+ assert_equal 'x', @board.get_cell(0, 1)
37
+ end
38
+
39
+ def test_does_not_win_unsolvable_board
32
40
 
33
41
  # Can not win when there is no winning move
34
42
  set_grid([[ 'o', nil, nil], [nil, nil, nil], ['o', nil, nil] ])
35
- refute board.solved?
36
- @solver.next_move!
37
- refute board.solved?
43
+ refute @board.solved?
44
+ solve!
45
+ refute @board.solved?
38
46
  end
39
47
 
40
- def test_block_next_move
41
- refute board.solved?
48
+ def test_blocks_horizontally
49
+ refute @board.solved?
42
50
 
43
51
  # Will block when the opponent is about to win
44
- set_grid([ ['o', 'o', nil], [nil, nil,nil], [nil,nil,nil]])
45
- refute board.solved?
46
- @solver.next_move!
47
- refute board.solved?
52
+ set_grid([ ['o', 'o', nil],
53
+ [nil, 'x', nil],
54
+ [nil, 'x', nil]])
55
+ refute @board.solved?
56
+ solve!
57
+ refute @board.solved?
48
58
  # Placed the right letter at the right place
49
- assert_equal 'x', board.get_cell(2, 0)
59
+ assert_equal 'x', @board.get_cell(2, 0)
50
60
  end
51
61
 
62
+ def test_blocks_vertically
63
+
64
+ set_grid([ ['x', 'o', nil],
65
+ [nil, nil, nil],
66
+ [nil, 'o', nil]])
67
+ solve!
68
+ assert_equal 'x', @board.get_cell(1, 1)
69
+ end
52
70
 
53
- def test_fork_next_move
54
- refute board.solved?
71
+
72
+ def test_forks_in_middle
73
+ refute @board.solved?
55
74
 
56
75
  # Place letter in place where next turn is an automatic win
57
- set_grid([ ['x', nil, nil], [nil, 'o',nil], [nil,nil,'x']])
58
- refute board.solved?
59
- @solver.next_move!
60
- refute board.solved?
61
- assert_equal 'x', board.get_cell(2, 0)
76
+ set_grid([ ['x', 'o', 'x'],
77
+ [nil, nil, 'o'],
78
+ [nil, nil, nil]])
79
+ refute @board.solved?
80
+ solve!
81
+ refute @board.solved?
82
+ correct_cells = @board.get_cell(1, 1) || @board.get_cell(0, 2)
83
+ assert !!correct_cells
84
+ end
85
+
86
+ def test_forks_in_corner
62
87
 
63
88
  # 1 | 2 | x
64
89
  # 4 | o | 6
65
90
  # 7 | x | 9 <--
66
- set_grid([ [nil, nil, 'x'], [nil, 'o',nil], [nil,'x',nil]])
67
- refute board.solved?
68
- @solver.next_move!
69
- refute board.solved?
70
- assert_equal 'x', board.get_cell(2, 2)
91
+ set_grid([ [nil, 'o', 'x'],
92
+ [nil, 'o', nil],
93
+ [nil, 'x', nil]])
94
+ refute @board.solved?
95
+ solve!
96
+ refute @board.solved?
97
+ assert_equal 'x', @board.get_cell(2, 2)
71
98
  end
72
99
 
73
- def test_block_fork_next_move
74
- # Will block an opponent if they will have a change to fork next turn
75
-
76
- # 1 | 2 | o
77
- # 4 | x | 6
78
- # 7 | o | 9 <--
79
- set_grid([ [nil, nil, 'o'], [nil, 'x',nil], [nil,'o',nil]])
80
- refute board.solved?
81
- @solver.next_move!
82
- refute board.solved?
83
- assert_equal 'x', board.get_cell(2, 2)
84
-
100
+ def test_does_not_create_fork
85
101
  # 1 | 2 | o
86
102
  # 4 | x | 6
87
103
  # o | 8 | 9
88
- set_grid([[nil,nil,'o'],[nil,'x',nil],['o',nil,nil]])
89
- @solver.next_move!
90
- assert_nil board.get_cell(2,2)
91
- assert_nil board.get_cell(0,0)
104
+ set_grid([[nil,nil,'o'],
105
+ [nil,'x',nil],
106
+ ['o',nil,nil]])
107
+ solve!
108
+ assert_nil @board.get_cell(2,2)
109
+ assert_nil @board.get_cell(0,0)
110
+ end
111
+
112
+ def test_does_not_create_fork_in_the_future
92
113
 
93
114
  # x | 2 | 3
94
115
  # 4 | o | 6
95
116
  # 7 | 8 | o
96
117
  set_grid([ ['x',nil,nil], [nil,'o',nil], [nil,nil,'o'] ])
97
- @solver.next_move!
98
- assert_nil board.get_cell(1, 0)
99
- assert_nil board.get_cell(0, 1)
100
- assert_nil board.get_cell(2, 1)
101
- assert_nil board.get_cell(1, 2)
118
+ solve!
119
+ assert_nil @board.get_cell(1, 0)
120
+ assert_nil @board.get_cell(0, 1)
121
+ assert_nil @board.get_cell(2, 1)
122
+ assert_nil @board.get_cell(1, 2)
102
123
  end
103
124
 
104
- def test_center
105
- # Will play in the center if its an empty board
106
- assert board.empty?
107
- @solver.next_move!
108
- assert_equal 'x', board.center_cell
109
- assert board.only_one?
125
+ def test_does_not_create_fork_in_the_far_future
126
+ set_grid([['o', nil, nil], [nil, nil, nil], [nil, nil, nil]])
127
+ solve!
128
+ assert_nil @board.get_cell(0, 2)
110
129
  end
111
-
112
- def test_oposite_corner
113
- # 1 | 2 | o
114
- # 4 | x | 6
115
- # 7 | 8 | 9
116
- set_grid([[nil,nil,'o'],[nil,'x',nil],[nil,nil,nil]])
117
- @solver.next_move!
118
- assert_equal 'x', board.get_cell(0, 2)
119
130
 
120
- set_grid([['o',nil,nil],[nil,'x',nil],[nil,nil,nil]])
121
- @solver.next_move!
122
- assert_equal 'x', board.get_cell(2, 2)
123
- end
124
131
 
125
132
  def test_empty_corner
126
133
  set_grid([[nil,nil,nil],[nil,'o',nil],[nil,nil,nil]])
127
- @solver.next_move!
128
- assert_equal 'x', board.get_cell(0,0)
134
+ solve!
135
+ played_corner = @board.get_cell(0,0) || @board.get_cell(0,2) ||
136
+ @board.get_cell(2,0) || @board.get_cell(2,2)
137
+ assert played_corner
129
138
  end
130
139
 
131
140
  def test_any_empty_position
@@ -133,17 +142,84 @@ class SolverTest < MiniTest::Unit::TestCase
133
142
  # o | x | 6
134
143
  # x | o | x
135
144
  set_grid([['o', 'x', 'o'],['o','x',nil],['x','o','x']])
136
- @solver.next_move!
137
- assert_equal 'x', board.get_cell(2, 1)
145
+ solve!
146
+ assert_equal 'x', @board.get_cell(2, 1)
147
+ end
148
+
149
+ def test_raises_exception_on_full_board
150
+ set_grid([%w(x o x), %w(o o x), %w(o x o)])
151
+ assert_raises(RuntimeError) { solve! }
138
152
  end
139
153
 
140
154
  private
141
155
 
142
156
  def set_grid(grid)
143
- board.instance_variable_set("@grid", grid)
157
+ @board.grid = grid
144
158
  end
145
159
 
146
- def board
147
- @solver.strategy.implementation.board
160
+ def solve!
161
+ @board.play_at(*@solver.solve, 'x')
162
+ end
163
+ end
164
+
165
+ class MinimaxSolverTest < MiniTest::Unit::TestCase
166
+ def setup
167
+ @board = TicTacToe::Board.new
168
+ @solver = TicTacToe::MinimaxStrategy.new(@board, 'x')
169
+ end
170
+
171
+ include SharedSolverTests
172
+ end
173
+
174
+ class ThreeByThreeSolverTest < MiniTest::Unit::TestCase
175
+ def setup
176
+ @board = TicTacToe::Board.new
177
+ @solver = TicTacToe::ThreeByThreeStrategy.new(@board, 'x')
178
+ end
179
+
180
+ include SharedSolverTests
181
+
182
+ def test_block_fork
183
+ set_grid([['o', nil, nil], ['x', 'o', nil], [nil, nil, 'x']])
184
+ solve!
185
+ assert_equal 'x', @board.get_cell(1, 0)
186
+ end
187
+
188
+ def test_center
189
+ # Will play in the center if its an empty @board
190
+ assert @board.empty?
191
+ solve!
192
+ assert_equal 'x', @board.get_cell(@board.size/2, @board.size/2)
193
+ assert @board.only_one?
194
+ end
195
+
196
+ def test_opposite_corner
197
+ # 1 | 2 | o
198
+ # 4 | x | 6
199
+ # 7 | 8 | 9
200
+ set_grid([[nil,nil,'o'],[nil,'x',nil],[nil,nil,nil]])
201
+ solve!
202
+ assert_equal 'x', @board.get_cell(0, 2)
203
+
204
+ set_grid([['o',nil,nil],[nil,'x',nil],[nil,nil,nil]])
205
+ solve!
206
+ assert_equal 'x', @board.get_cell(2, 2)
207
+ end
208
+
209
+ def test_corners
210
+ set_grid([['x', nil, nil], [nil, 'o', nil], [nil, nil, nil]])
211
+ solve!
212
+ assert_equal 'x', @board.get_cell(2,0)
213
+
214
+ set_grid([['x', 'o', 'x'], [nil, 'o', nil], ['o', 'x', nil]])
215
+ solve!
216
+ assert_equal 'x', @board.get_cell(2, 2)
217
+
218
+ # x | o | x
219
+ # o | o | x
220
+ # | x | o
221
+ set_grid([['x', 'o', 'x'], ['o', 'o', 'x'], [nil, 'x', 'o']])
222
+ solve!
223
+ assert_equal 'x', @board.get_cell(0, 2)
148
224
  end
149
225
  end