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.
@@ -1,197 +0,0 @@
1
- # Brute Force Implementation for the Three by Three Strategy
2
- # This implementation uses loops to try a change all of the board values and
3
- # checking the result
4
- #
5
- # For example, win! is implemented by trying every cell, and checking if
6
- # it was a winning solution
7
- module TicTacToe
8
- class BruteForceImplementation
9
-
10
- def initialize(board, letter)
11
- @board, @letter = board, letter
12
- end
13
-
14
- # Try placing letter at every available position
15
- # If the board is solved, do that
16
- def win!
17
- each_position do |x, y|
18
- temp_board = @board.clone
19
- temp_board.play_at(x, y, @letter)
20
- if temp_board.solved?
21
- @board.play_at(x, y, @letter)
22
- return true
23
- end
24
- end
25
- false
26
- end
27
-
28
- # Try placing the opponent's letter at every available position
29
- # If the board is solved, block them at that position
30
- def block!(board = @board, letter = @letter)
31
- each_position do |x, y|
32
- temp_board = board.clone
33
- temp_board.play_at(x, y, other_player(letter))
34
- if temp_board.solved?
35
- board.play_at(x, y, letter)
36
- return true
37
- end
38
- end
39
- false
40
- end
41
-
42
- # Try placing the letter at every position.
43
- # If there are now two winning solutions for next turn, go there
44
- def fork!
45
- each_forking_position do |x, y|
46
- @board.play_at(x, y, @letter)
47
- return true
48
- end
49
- false
50
- end
51
-
52
- # Try placing the opponent's letter at every position.
53
- # If there are now two winning solutions for next turn, block them there
54
- def block_fork!(board = @board)
55
- each_forking_position(other_player) do |x, y|
56
- temp_board = board.clone
57
- temp_board.play_at(x,y,@letter)
58
- # Search for the elusive double fork
59
- each_forking_position(other_player, temp_board) do |x, y|
60
- return force_a_block
61
- end
62
- board.play_at(x, y, @letter)
63
- return true
64
- end
65
- false
66
- end
67
-
68
- def center!
69
- return false if @board.center_cell
70
-
71
- @board.center_cell = @letter
72
- true
73
- end
74
-
75
- # Cycle through all of the corners looking for the opponent's letter
76
- # If one is found, place letter at the opposite corner
77
- def oposite_corner!
78
- @board.corners.each_with_index do |corner, index|
79
- if corner == other_player
80
- case index
81
- when 0 # Top Left
82
- next if @board.get_cell(Board::SIZE-1, Board::SIZE-1)
83
- @board.play_at(Board::SIZE-1, Board::SIZE-1, @letter)
84
- return true
85
- when 1 # Top Right
86
- next if @board.get_cell(0, Board::SIZE-1)
87
- @board.play_at(0, Board::SIZE-1, @letter)
88
- return true
89
- when 2 # Bottom Right
90
- next if @board.get_cell(0, 0)
91
- @board.play_at(0, 0, @letter)
92
- return true
93
- when 3 # Bottom Left
94
- next if @board.get_cell(Board::SIZE-1, 0)
95
- @board.play_at(Board::SIZE-1, 0, @letter)
96
- return true
97
- else
98
- raise Exception.new("Board#corners returned more than 4")
99
- end
100
- end
101
- end
102
- false
103
- end
104
-
105
- # Cycle though all of the corners, until one is found that is empty
106
- def empty_corner!
107
- @board.corners.each_with_index do |corner, index|
108
- unless corner
109
- case index
110
- when 0 # Top Left
111
- @board.play_at(0, 0, @letter)
112
- return true
113
- when 1 # Top Right
114
- @board.play_at(Board::SIZE-1, 0, @letter)
115
- return true
116
- when 2 # Bottom Right
117
- @board.play_at(Board::SIZE-1, Board::SIZE-1, @letter)
118
- return true
119
- when 3 # Bottom Left
120
- @board.play_at(0, Board::SIZE-1, @letter)
121
- return true
122
- else
123
- raise Exception.new("Board#corners returned more than 4")
124
- end
125
- end
126
- end
127
- false
128
- end
129
-
130
- # Place letter at a random empty cell, at this point it should only be sides left
131
- def empty_side!
132
- @board.any_empty_position do |x, y|
133
- @board.play_at(x, y, @letter)
134
- return true
135
- end
136
- false
137
- end
138
-
139
- private
140
-
141
- def other_player(letter = @letter)
142
- letter == X ? O : X
143
- end
144
-
145
- def fork_exsits?(x, y, letter = @letter, board = @board)
146
- (count = can_win_next_turn?(x, y, letter, board)) && count >= 2
147
- end
148
-
149
- def each_position(&block)
150
- Board::SIZE.times do |y|
151
- Board::SIZE.times do |x|
152
- yield(x, y)
153
- end
154
- end
155
- end
156
-
157
- def each_forking_position(letter = @letter, board = @board, &block)
158
- each_position do |x, y|
159
- if fork_exsits?(x, y, letter, board)
160
- yield(x, y)
161
- end
162
- end
163
- end
164
-
165
- def can_win_next_turn?(x, y, letter = @letter, board = @board)
166
- count = 0
167
- temp_board = board.clone
168
- temp_board.play_at(x,y,letter)
169
- each_position do |x, y|
170
- inner_loop_board = temp_board.clone
171
- inner_loop_board.play_at(x, y, letter)
172
- count += 1 if inner_loop_board.solved?
173
- end
174
- return count == 0 ? false : count
175
- end
176
-
177
- def force_a_block
178
- # Force them to block without creating another fork
179
- each_position do |x, y|
180
- if can_win_next_turn?(x, y)
181
- temp_board = @board.clone
182
- temp_board.play_at(x, y, @letter)
183
- raise Exception.new("Couldn't force a block") unless block!(temp_board, other_player)
184
- # Did I just create another fork with that block?
185
- if fork_exsits?(x, y, other_player, temp_board)
186
- next
187
- else
188
- @board.play_at(x, y, @letter)
189
- return true
190
- end
191
- end
192
- end
193
- raise Exception.new("No position found to block the fork")
194
- end
195
-
196
- end
197
- end
@@ -1,30 +0,0 @@
1
- require_relative 'threebythree_implementations/brute_force_implementation'
2
-
3
- module TicTacToe
4
- class ThreebythreeStrategy
5
- def initialize(board, letter, implementation=BruteForceImplementation)
6
- @implementation = implementation.new(board, letter)
7
- end
8
-
9
- # The strategy is from the Wikipedia article on Tic-Tac-Toe
10
- # 1) Try to win
11
- # 2) Try to block if they're about to win
12
- # 3) Try to fork so you'll win next turn
13
- # 4) Try to block their fork so they will not win next turn
14
- # 5) Take the center if its not already taken
15
- # 6) Play the opposite corner of your opponent
16
- # 7) Play in an empty corner
17
- # 8) Play in an empty side
18
- def solve!
19
- return if @implementation.win!
20
- return if @implementation.block!
21
- return if @implementation.fork!
22
- return if @implementation.block_fork!
23
- return if @implementation.center!
24
- return if @implementation.oposite_corner!
25
- return if @implementation.empty_corner!
26
- return if @implementation.empty_side!
27
- raise Exception.new("No possible moves to play!")
28
- end
29
- end
30
- end
@@ -1,25 +0,0 @@
1
- require 'test_helper'
2
-
3
- class BruteForceImplementationTest < MiniTest::Unit::TestCase
4
- def setup
5
- TicTacToe::BruteForceImplementation.instance_eval { attr_accessor :board }
6
- @implementation = TicTacToe::BruteForceImplementation.new(TicTacToe::Board.new, 'x')
7
- end
8
-
9
- def test_can_win_next_turn
10
- # Test private method that it returns the correct amount of winning moves
11
-
12
- set_grid([[ 'x', nil, nil], [nil, nil, nil], [nil, nil, nil] ])
13
- assert_equal 1, @implementation.send(:can_win_next_turn?, 0, 1)
14
-
15
- # 1 | 2 | x
16
- # 4 | o | 6
17
- # 7 | x | 9
18
- set_grid([[ nil, nil, 'x'], [nil, 'o', nil], [nil, 'x', nil] ])
19
- assert_equal 2, @implementation.send(:can_win_next_turn?, 2, 2)
20
- end
21
-
22
- def set_grid(grid)
23
- @implementation.board.instance_variable_set("@grid", grid)
24
- end
25
- end