erics_tic_tac_toe 0.1.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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