xo 1.0.0 → 1.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.
@@ -4,10 +4,10 @@ module XO
4
4
 
5
5
  describe Evaluator do
6
6
 
7
- describe ".analyze" do
7
+ let (:evaluator) { Evaluator.instance }
8
+ let (:grid) { Grid.new }
8
9
 
9
- let (:evaluator) { Evaluator.instance }
10
- let (:grid) { Grid.new }
10
+ describe "#analyze" do
11
11
 
12
12
  describe "standard play" do
13
13
 
@@ -210,6 +210,11 @@ module XO
210
210
  positions: [[1, 3], [2, 2], [3, 1]]
211
211
  }]
212
212
  }
213
+
214
+ evaluator.analyze(grid, Grid::X).must_equal result
215
+
216
+ result[:type] = :loser
217
+ evaluator.analyze(grid, Grid::O).must_equal result
213
218
  end
214
219
  end
215
220
 
@@ -227,13 +232,6 @@ module XO
227
232
  end
228
233
  end
229
234
 
230
- describe "invalid token input" do
231
-
232
- it "raises ArgumentError" do
233
- proc { evaluator.analyze(Grid.new, :not_a_token) }.must_raise ArgumentError
234
- end
235
- end
236
-
237
235
  describe "invalid grid input" do
238
236
 
239
237
  it "returns too many moves ahead" do
@@ -256,6 +254,23 @@ module XO
256
254
  evaluator.analyze(grid, Grid::O).must_equal result
257
255
  end
258
256
  end
257
+
258
+ describe "invalid token input" do
259
+
260
+ it "raises ArgumentError" do
261
+ proc { evaluator.analyze(Grid.new, :not_a_token) }.must_raise ArgumentError
262
+ end
263
+ end
264
+ end
265
+
266
+ describe ".xos" do
267
+
268
+ it "returns the number of x's and o's" do
269
+ xs, os = Evaluator.xos(Grid.new('xoxxo'))
270
+
271
+ xs.must_equal 3
272
+ os.must_equal 2
273
+ end
259
274
  end
260
275
  end
261
276
  end
@@ -4,8 +4,6 @@ module XO::AI
4
4
 
5
5
  describe GeometricGrid do
6
6
 
7
- let(:grid) { GeometricGrid.new }
8
-
9
7
  describe "#rotate" do
10
8
 
11
9
  let (:grid) { GeometricGrid.new("xoooxxoxo") }
@@ -81,7 +79,7 @@ module XO::AI
81
79
  end
82
80
  end
83
81
 
84
- describe "equality" do
82
+ describe "#==" do
85
83
 
86
84
  it "is reflexive, symmetric and transitive" do
87
85
  a = GeometricGrid.new('x')
@@ -4,6 +4,8 @@ module XO
4
4
 
5
5
  describe Grid do
6
6
 
7
+ let(:grid) { Grid.new }
8
+
7
9
  it "defines X" do
8
10
  Grid::X.must_equal :x
9
11
  end
@@ -12,6 +14,10 @@ module XO
12
14
  Grid::O.must_equal :o
13
15
  end
14
16
 
17
+ it "defines EMPTY" do
18
+ Grid::EMPTY.must_equal :e
19
+ end
20
+
15
21
  it "has 3 rows" do
16
22
  Grid::ROWS.must_equal 3
17
23
  end
@@ -56,8 +62,8 @@ module XO
56
62
  end
57
63
 
58
64
  it "returns false otherwise" do
59
- [nil, :e, '', ' '].each do
60
- Grid.is_token?(:e).must_equal false
65
+ [nil, :e, '', ' '].each do |val|
66
+ Grid.is_token?(val).must_equal false
61
67
  end
62
68
  end
63
69
  end
@@ -77,16 +83,11 @@ module XO
77
83
  end
78
84
  end
79
85
 
80
- let(:grid) { Grid.new }
81
-
82
- describe "an initial grid" do
86
+ describe ".new" do
83
87
 
84
- it "is empty" do
88
+ it "is returns an empty grid when given no arguments" do
85
89
  grid.empty?.must_equal true
86
90
  end
87
- end
88
-
89
- describe "#new" do
90
91
 
91
92
  it "can create a grid from a string" do
92
93
  grid = Grid.new('xox o')
@@ -154,14 +155,14 @@ module XO
154
155
  end
155
156
 
156
157
  it "returns false when at least one position doesn't have a token" do
157
- grid[1, 3] = :e
158
+ grid[1, 3] = Grid::EMPTY
158
159
  grid.full?.must_equal false
159
160
  end
160
161
  end
161
162
 
162
163
  describe "#[]=" do
163
164
 
164
- it "sets a position to X, O or :e" do
165
+ it "sets a position to X, O or EMPTY" do
165
166
  grid[2, 1] = Grid::X
166
167
  grid[2, 1].must_equal Grid::X
167
168
 
@@ -169,7 +170,7 @@ module XO
169
170
  grid[2, 2].must_equal Grid::O
170
171
 
171
172
  grid[2, 3] = :anything_else
172
- grid[2, 3].must_equal :e
173
+ grid[2, 3].must_equal Grid::EMPTY
173
174
  end
174
175
 
175
176
  it "raises IndexError when the position is off the grid" do
@@ -211,10 +212,6 @@ module XO
211
212
 
212
213
  grid.empty?.must_equal true
213
214
  end
214
-
215
- it "returns self" do
216
- grid.clear.must_be_same_as grid
217
- end
218
215
  end
219
216
 
220
217
  describe "#each" do
data/xo.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.author = 'Dwayne R. Crooks'
10
10
  spec.email = ['me@dwaynecrooks.com']
11
11
  spec.summary = %q{A Ruby library for Tic-tac-toe.}
12
- spec.description = %q{A Ruby library that can be used to develop Tic-tac-toe game clients.}
12
+ spec.description = %q{A Ruby library for Tic-tac-toe that can be used to develop Tic-tac-toe game clients and servers.}
13
13
  spec.homepage = 'https://github.com/dwayne/xo'
14
14
  spec.license = 'MIT'
15
15
 
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.required_ruby_version = '>= 1.9.3'
22
22
 
23
+ spec.add_runtime_dependency 'state_design_pattern', '~> 0.0.2'
24
+
23
25
  spec.add_development_dependency 'rake', '~> 10.3'
24
26
  spec.add_development_dependency 'minitest', '~> 5.3'
25
27
  spec.add_development_dependency 'coveralls', '~> 0.7'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dwayne R. Crooks
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-05-08 00:00:00.000000000 Z
11
+ date: 2014-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: state_design_pattern
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.0.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.0.2
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rake
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -80,7 +94,8 @@ dependencies:
80
94
  - - ~>
81
95
  - !ruby/object:Gem::Version
82
96
  version: '0.8'
83
- description: A Ruby library that can be used to develop Tic-tac-toe game clients.
97
+ description: A Ruby library for Tic-tac-toe that can be used to develop Tic-tac-toe
98
+ game clients and servers.
84
99
  email:
85
100
  - me@dwaynecrooks.com
86
101
  executables:
@@ -97,19 +112,26 @@ files:
97
112
  - Rakefile
98
113
  - bin/xo
99
114
  - lib/xo.rb
100
- - lib/xo/ai.rb
101
115
  - lib/xo/ai/geometric_grid.rb
116
+ - lib/xo/ai/max_player.rb
117
+ - lib/xo/ai/min_player.rb
102
118
  - lib/xo/ai/minimax.rb
119
+ - lib/xo/ai/player.rb
103
120
  - lib/xo/engine.rb
121
+ - lib/xo/engine/game_context.rb
122
+ - lib/xo/engine/game_over.rb
123
+ - lib/xo/engine/game_state.rb
124
+ - lib/xo/engine/init.rb
125
+ - lib/xo/engine/playing.rb
104
126
  - lib/xo/evaluator.rb
105
127
  - lib/xo/grid.rb
106
128
  - lib/xo/version.rb
129
+ - spec/engine_spec.rb
130
+ - spec/evaluator_spec.rb
131
+ - spec/geometric_grid_spec.rb
132
+ - spec/grid_spec.rb
133
+ - spec/minimax_spec.rb
107
134
  - spec/spec_helper.rb
108
- - spec/xo/ai/geometric_grid_spec.rb
109
- - spec/xo/ai/minimax_spec.rb
110
- - spec/xo/engine_spec.rb
111
- - spec/xo/evaluator_spec.rb
112
- - spec/xo/grid_spec.rb
113
135
  - xo.gemspec
114
136
  homepage: https://github.com/dwayne/xo
115
137
  licenses:
@@ -136,10 +158,10 @@ signing_key:
136
158
  specification_version: 4
137
159
  summary: A Ruby library for Tic-tac-toe.
138
160
  test_files:
161
+ - spec/engine_spec.rb
162
+ - spec/evaluator_spec.rb
163
+ - spec/geometric_grid_spec.rb
164
+ - spec/grid_spec.rb
165
+ - spec/minimax_spec.rb
139
166
  - spec/spec_helper.rb
140
- - spec/xo/ai/geometric_grid_spec.rb
141
- - spec/xo/ai/minimax_spec.rb
142
- - spec/xo/engine_spec.rb
143
- - spec/xo/evaluator_spec.rb
144
- - spec/xo/grid_spec.rb
145
167
  has_rdoc:
@@ -1,2 +0,0 @@
1
- require 'xo/ai/geometric_grid'
2
- require 'xo/ai/minimax'
@@ -1,331 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module XO
4
-
5
- describe Engine do
6
-
7
- let (:engine) { Engine.new }
8
-
9
- describe "its initial state" do
10
-
11
- it "is in the :init state" do
12
- engine.state.must_equal :init
13
- end
14
-
15
- it "is nobody's turn" do
16
- engine.turn.must_equal :nobody
17
- end
18
-
19
- it "has an empty grid" do
20
- engine.grid.empty?.must_equal true
21
- end
22
-
23
- it "has last event set to :new" do
24
- event = engine.last_event
25
-
26
- event[:name].must_equal :new
27
- end
28
-
29
- describe "#next_turn" do
30
-
31
- it "returns :nobody" do
32
- engine.next_turn.must_equal :nobody
33
- end
34
- end
35
- end
36
-
37
- describe "#grid" do
38
-
39
- it "returns a copy of the underlying grid" do
40
- grid = engine.grid
41
-
42
- grid[1, 1] = Grid::X
43
-
44
- engine.grid.open?(1, 1).must_equal true
45
- end
46
- end
47
-
48
- describe "how the state machine works" do
49
-
50
- describe "state :init" do
51
-
52
- it "is in state :init" do
53
- engine.state.must_equal :init
54
- end
55
-
56
- describe "#start" do
57
-
58
- before { engine.start(Grid::X) }
59
-
60
- it "changes state to :playing" do
61
- engine.state.must_equal :playing
62
- end
63
-
64
- it "sets turn to the value passed in" do
65
- engine.turn.must_equal Grid::X
66
- end
67
-
68
- it "starts with an empty grid" do
69
- engine.grid.empty?.must_equal true
70
- end
71
-
72
- it "sets last event" do
73
- event = engine.last_event
74
-
75
- event[:name].must_equal :game_started
76
- end
77
-
78
- describe "#next_turn" do
79
-
80
- it "returns O" do
81
- engine.next_turn.must_equal Grid::O
82
- end
83
- end
84
- end
85
-
86
- it "only allows #start to be called" do
87
- proc { engine.stop }.must_raise Engine::IllegalStateError
88
- proc { engine.play(1, 1) }.must_raise Engine::IllegalStateError
89
- proc { engine.continue_playing(Grid::X) }.must_raise Engine::IllegalStateError
90
- end
91
- end
92
-
93
- describe "state :playing" do
94
-
95
- before do
96
- engine.start(Grid::X)
97
- end
98
-
99
- it "is in state :playing" do
100
- engine.state.must_equal :playing
101
- end
102
-
103
- describe "#play" do
104
-
105
- describe "valid moves" do
106
-
107
- describe "when given (1, 1)" do
108
-
109
- before { engine.play(1, 1) }
110
-
111
- it "remains in state :playing" do
112
- engine.state.must_equal :playing
113
- end
114
-
115
- it "sets turn to O" do
116
- engine.turn.must_equal Grid::O
117
- end
118
-
119
- it "updates the grid at that position" do
120
- engine.grid[1, 1].must_equal Grid::X
121
- end
122
-
123
- it "sets last event" do
124
- event = engine.last_event
125
-
126
- event[:name].must_equal :next_turn
127
- event[:last_move][:turn].must_equal Grid::X
128
- event[:last_move][:r].must_equal 1
129
- event[:last_move][:c].must_equal 1
130
- end
131
- end
132
-
133
- describe "when the next move results in the game being over" do
134
-
135
- describe "winning" do
136
-
137
- before do
138
- engine.play(1, 1).play(2, 1).play(1, 2).play(2, 2).play(1, 3)
139
- end
140
-
141
- it "changes state to :game_over" do
142
- engine.state.must_equal :game_over
143
- end
144
-
145
- it "sets turn to the winner" do
146
- engine.turn.must_equal Grid::X
147
- end
148
-
149
- it "leaves the grid unchanged" do
150
- engine.grid.inspect.must_equal 'xxxoo '
151
- end
152
-
153
- it "sets last event" do
154
- event = engine.last_event
155
-
156
- event[:name].must_equal :game_over
157
- event[:type].must_equal :winner
158
- event[:last_move][:turn].must_equal Grid::X
159
- event[:last_move][:r].must_equal 1
160
- event[:last_move][:c].must_equal 3
161
- event[:details].must_equal [
162
- { where: :row, index: 1, positions: [[1, 1], [1, 2], [1, 3]] }
163
- ]
164
- end
165
- end
166
-
167
- describe "squashed" do
168
-
169
- before do
170
- engine.play(1, 1).play(1, 2).play(1, 3).play(2, 2).play(3, 2).play(2, 1).play(2, 3).play(3, 3).play(3, 1)
171
- end
172
-
173
- it "changes state to :game_over" do
174
- engine.state.must_equal :game_over
175
- end
176
-
177
- it "leaves turn set to the last one played" do
178
- engine.turn.must_equal Grid::X
179
- end
180
-
181
- it "leaves the grid unchanged" do
182
- engine.grid.inspect.must_equal 'xoxooxxxo'
183
- end
184
-
185
- it "sets last event" do
186
- event = engine.last_event
187
-
188
- event[:name].must_equal :game_over
189
- event[:type].must_equal :squashed
190
- event[:last_move][:turn].must_equal Grid::X
191
- event[:last_move][:r].must_equal 3
192
- event[:last_move][:c].must_equal 1
193
- end
194
- end
195
- end
196
- end
197
-
198
- describe "invalid moves" do
199
-
200
- describe "when given (0, 0)" do
201
-
202
- it "sets last event to out of bounds" do
203
- event = engine.play(0, 0).last_event
204
-
205
- event[:name].must_equal :invalid_move
206
- event[:type].must_equal :out_of_bounds
207
- end
208
- end
209
-
210
- describe "when given (1, 1) and it already has a token there" do
211
-
212
- it "sets last event to occupied" do
213
- event = engine.play(1, 1).play(1, 1).last_event
214
-
215
- event[:name].must_equal :invalid_move
216
- event[:type].must_equal :occupied
217
- end
218
- end
219
- end
220
- end
221
-
222
- describe "#stop" do
223
-
224
- before { engine.stop }
225
-
226
- it "changes state to :init" do
227
- engine.state.must_equal :init
228
- end
229
-
230
- it "sets turn to :nobody" do
231
- engine.turn.must_equal :nobody
232
- end
233
-
234
- it "clears the grid" do
235
- engine.grid.empty?.must_equal true
236
- end
237
-
238
- it "sets last event" do
239
- event = engine.last_event
240
-
241
- event[:name].must_equal :game_stopped
242
- end
243
- end
244
-
245
- it "only allows #play and #stop to be called" do
246
- proc { engine.start(Grid::O) }.must_raise Engine::IllegalStateError
247
- proc { engine.continue_playing(Grid::O) }.must_raise Engine::IllegalStateError
248
- end
249
- end
250
-
251
- describe "state :game_over" do
252
-
253
- before do
254
- engine
255
- .start(Grid::O)
256
- .play(1, 1).play(2, 1).play(1, 2).play(2, 2).play(1, 3)
257
- end
258
-
259
- it "is in state :game_over" do
260
- engine.state.must_equal :game_over
261
- end
262
-
263
- describe "#continue_playing" do
264
-
265
- before { engine.continue_playing(Grid::O) }
266
-
267
- it "changes state to :playing" do
268
- engine.state.must_equal :playing
269
- end
270
-
271
- it "sets turn to O" do
272
- engine.turn.must_equal Grid::O
273
- end
274
-
275
- it "clears the grid" do
276
- engine.grid.empty?.must_equal true
277
- end
278
-
279
- it "sets last event" do
280
- event = engine.last_event
281
-
282
- event[:name].must_equal :game_started
283
- event[:type].must_equal :continue_playing
284
- end
285
- end
286
-
287
- describe "#stop" do
288
-
289
- before { engine.stop }
290
-
291
- it "changes state to :init" do
292
- engine.state.must_equal :init
293
- end
294
-
295
- it "sets turn to :nobody" do
296
- engine.turn.must_equal :nobody
297
- end
298
-
299
- it "clears the grid" do
300
- engine.grid.empty?.must_equal true
301
- end
302
-
303
- it "sets last event" do
304
- event = engine.last_event
305
-
306
- event[:name].must_equal :game_stopped
307
- end
308
- end
309
-
310
- it "only allows #continue_playing and #stop to be called" do
311
- proc { engine.start(Grid::X) }.must_raise Engine::IllegalStateError
312
- proc { engine.play(1, 1) }.must_raise Engine::IllegalStateError
313
- end
314
- end
315
- end
316
-
317
- describe "#start with invalid input" do
318
-
319
- it "raises an ArgumentError" do
320
- proc { engine.start(:invalid_input) }.must_raise ArgumentError
321
- end
322
- end
323
-
324
- describe "#continue_playing with invalid input" do
325
-
326
- it "raises an ArgumentError" do
327
- proc { engine.continue_playing(:invalid_input) }.must_raise ArgumentError
328
- end
329
- end
330
- end
331
- end