ttt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+ gem "bundler-bouncer" , "~> 0.1.2"
3
+ gem "rspec" , "~> 2.6.0"
4
+ gem "cucumber" , "~> 1.0.2"
5
+ gem "pry" , "~> 0.9.3"
6
+ gem "rake" , "~> 0.9.2"
@@ -0,0 +1,51 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ builder (3.0.0)
5
+ bundler-bouncer (0.1.2)
6
+ coderay (0.9.8)
7
+ cucumber (1.0.2)
8
+ builder (>= 2.1.2)
9
+ diff-lcs (>= 1.1.2)
10
+ gherkin (~> 2.4.5)
11
+ json (>= 1.4.6)
12
+ term-ansicolor (>= 1.0.5)
13
+ diff-lcs (1.1.2)
14
+ gherkin (2.4.5)
15
+ json (>= 1.4.6)
16
+ gherkin (2.4.5-java)
17
+ json (>= 1.4.6)
18
+ json (1.5.3)
19
+ json (1.5.3-java)
20
+ method_source (0.6.0)
21
+ ruby_parser (>= 2.0.5)
22
+ pry (0.9.3)
23
+ coderay (>= 0.9.8)
24
+ method_source (>= 0.6.0)
25
+ ruby_parser (>= 2.0.5)
26
+ slop (~> 1.9.0)
27
+ rake (0.9.2)
28
+ rspec (2.6.0)
29
+ rspec-core (~> 2.6.0)
30
+ rspec-expectations (~> 2.6.0)
31
+ rspec-mocks (~> 2.6.0)
32
+ rspec-core (2.6.4)
33
+ rspec-expectations (2.6.0)
34
+ diff-lcs (~> 1.1.2)
35
+ rspec-mocks (2.6.0)
36
+ ruby_parser (2.0.6)
37
+ sexp_processor (~> 3.0)
38
+ sexp_processor (3.0.5)
39
+ slop (1.9.1)
40
+ term-ansicolor (1.0.6)
41
+
42
+ PLATFORMS
43
+ java
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ bundler-bouncer (~> 0.1.2)
48
+ cucumber (~> 1.0.2)
49
+ pry (~> 0.9.3)
50
+ rake (~> 0.9.2)
51
+ rspec (~> 2.6.0)
@@ -0,0 +1,8 @@
1
+ Copyright (c) 2011 Joshua Cheek
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
+
@@ -0,0 +1,324 @@
1
+ require 'bundler/bouncer'
2
+
3
+ # load the lib
4
+ task :environment do
5
+ $:.unshift File.expand_path('../lib', __FILE__)
6
+ require 'ttt'
7
+ require 'ttt/computer_player'
8
+ end
9
+
10
+
11
+
12
+ # commonly invoked interfaces
13
+ desc 'Open a pry console with the app loaded'
14
+ task :console do
15
+ sh 'pry -I lib -r ttt -r ttt/computer_player'
16
+ end
17
+
18
+ desc 'Run Cucumber against the features'
19
+ task :cuke do
20
+ sh 'cucumber'
21
+ end
22
+
23
+ desc 'run RSpec against the specification'
24
+ task :spec do
25
+ sh 'rspec spec --colour'
26
+ end
27
+ task :rspec => :spec # synonym
28
+
29
+
30
+
31
+ # handle gem construction
32
+ require "rubygems/package_task"
33
+ spec = Gem::Specification.new do |s|
34
+ # informatoin
35
+ s.name = "ttt"
36
+ s.version = "1.0.0"
37
+ s.summary = "Tic Tac Toe lib + binary"
38
+ s.description = "TTT is a Tic Tac Toe lib, as well as a CLI and GUI to play it."
39
+ s.author = "Joshua J Cheek"
40
+ s.email = "josh.cheek@gmail.com"
41
+ s.homepage = "https://github.com/JoshCheek/ttt"
42
+
43
+ # additional files
44
+ s.files = %w(Gemfile Gemfile.lock MIT-License.md Rakefile Readme.md) + Dir.glob("{bin,spec,features,lib}/**/*")
45
+ s.executables = FileList["bin/**"].map { |f| File.basename(f) }
46
+ s.require_paths = ["lib"]
47
+
48
+ # dependencies
49
+ s.add_development_dependency "bundler-bouncer" , "~> 0.1.2"
50
+ s.add_development_dependency "rspec" , "~> 2.6.0"
51
+ s.add_development_dependency "cucumber" , "~> 1.0.2"
52
+ s.add_development_dependency "pry" , "~> 0.9.3"
53
+ s.add_development_dependency "rake" , "~> 0.9.2"
54
+ end
55
+
56
+ task :package => :gemspec
57
+ Gem::PackageTask.new(spec) do |pkg|
58
+ pkg.gem_spec = spec
59
+ end
60
+
61
+ desc "Build the gemspec file #{spec.name}.gemspec"
62
+ task :gemspec do
63
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
64
+ File.open(file, "w") {|f| f << spec.to_ruby }
65
+ end
66
+
67
+
68
+
69
+
70
+
71
+ # Various command line utilities constructed while building the gem
72
+ namespace :script do
73
+
74
+ # cache the boards b/c computing them takes a long time
75
+ # these were generated with the compute_congruent task below
76
+ def self.all_boards_cached
77
+ %w[
78
+ 000000000 100000000 120000000 121000000 121200000 121210000 121212000 121212100 121212010 121212210
79
+ 121212211 121210200 121211200 121211220 121211221 121211202 121211212 121210210 121210212 121210201
80
+ 121210020 121211020 121211022 121210021 121210002 121211002 121210102 121210012 121201000 121221000
81
+ 121221100 121221120 121221102 121221112 121221010 121221210 121221211 121221012 121221001 121201200
82
+ 121201210 121201212 121201201 121201020 121201120 121201122 121201021 121201002 121201102 121201012
83
+ 121200100 121220100 121220101 121222101 121202100 121202101 121202121 121212121 121200102 121200010
84
+ 121220010 121220011 121220211 121202010 121200210 121200211 121200012 121200001 121220001 121200201
85
+ 121200021 121020000 121120000 121120200 121121200 121121220 121121202 121121212 121120210 121120212
86
+ 121120201 121120221 121120020 121120002 121120102 121120012 121020100 121020120 121020102 121020112
87
+ 121020010 121020210 121000200 121100200 121100220 121110220 121110222 121101220 121101222 121100221
88
+ 121100202 121110202 121101202 121100212 121010200 121010220 121011220 121010221 121010202 121010212
89
+ 121001200 121001220 121001221 121000210 121000212 121000201 121000221 121000020 121100020 121010020
90
+ 121000120 120100000 122100000 122110000 122112000 122112100 122112010 122112210 122112211 122112012
91
+ 122112001 122110200 122111200 122110210 122110212 122111212 122110201 122110020 122111020 122110120
92
+ 122110021 122110002 122111002 122110102 122110012 122101000 122121000 122121100 122121010 122121210
93
+ 122121012 122121112 122121001 122121201 122121021 122101200 122101210 122101212 122101201 122101221
94
+ 122111221 122101020 122101120 122101021 122101002 122101102 122101012 122100100 122100010 122120010
95
+ 122120110 122120011 122122011 122122111 122120211 122102010 122102110 122102011 122102211 122100210
96
+ 122100211 122100012 122100112 122100001 122120001 122120101 122102001 122102101 122100201 122100021
97
+ 120120000 120121000 120121200 120121210 120121212 120121201 120121020 120121002 120121102 120121012
98
+ 120120100 120120010 120122010 120122110 120122011 120122211 120120210 120120211 120120012 120120112
99
+ 120120001 120122001 120122101 120120201 120120021 120102000 120112000 120112200 120112210 120112212
100
+ 120112201 120112020 120112120 120112021 120112002 120112102 120112012 120102100 120102010 120102210
101
+ 120102211 120102012 120102112 120102001 120102201 120102021 120100200 120110200 120110220 120111220
102
+ 120110221 120110202 120111202 120110212 120101200 120101220 120101202 120101212 120100210 120100212
103
+ 120100201 120100221 120100020 120110020 120110022 120111022 120101020 120101022 120100120 120100021
104
+ 120100002 120110002 120101002 120100102 120100012 120010000 122010000 122011000 122211000 122211010
105
+ 122211210 122211211 122211012 122211001 122011200 122011210 122011212 122011201 122011020 122011120
106
+ 122011122 122111122 122011002 122011102 122011012 122010100 122012100 122012110 122012112 122012101
107
+ 122010120 122010102 122010112 122010010 122210010 122210011 122012010 122010210 122010211 122010012
108
+ 122010001 120210000 120211000 120211020 120211002 120211012 120210001 120012000 120012100 120012120
109
+ 120012102 120012112 120012010 120012210 120012012 120012001 120010200 120011200 120011220 120011202
110
+ 120011212 120010210 120010212 120010201 120010020 120011020 120011022 120010120 120010021 120010002
111
+ 120011002 120010102 120010012 120001000 122001000 122001100 122021100 122021110 122021112 122021101
112
+ 122001120 122001102 122001112 122001010 122201010 122201011 122221011 122201211 122021010 122021011
113
+ 122021211 122001210 122001211 122001012 122001001 122201001 122021001 122001201 120201000 120201010
114
+ 120221010 120221011 120201012 120201001 120221001 120201201 120021000 120021100 120021120 120021102
115
+ 120021112 120021010 120021210 120021211 120021012 120021001 120021201 120001200 120001210 120001212
116
+ 120001201 120001020 120001120 120001002 120001102 120001012 120000100 122000100 122000110 122020110
117
+ 122020111 122002110 122000112 122000101 122020101 122002101 120020100 120020110 120022110 120020112
118
+ 120020101 120022101 120002100 120002110 120002112 120002101 120000120 120000102 120000112 120000010
119
+ 122000010 122000011 122020011 122000211 120020010 120020011 120020211 120002010 120000210 120000211
120
+ 120000012 120000001 122000001 120200001 120020001 120002001 120000201 120000021 102000000 112000000
121
+ 112020000 112120000 112122000 112122100 112122010 112122210 112122012 112120200 112120020 112121020
122
+ 112121022 112120002 112121002 112121202 112120102 112120012 112021000 112021200 112021020 112021002
123
+ 112021102 112021012 112021212 112020100 112022100 112022110 112022112 112020102 112020112 112020010
124
+ 112022010 112020210 112020012 112020001 112020201 112002000 112102000 112102200 112112200 112112220
125
+ 112112202 112102210 112102212 112102020 112112020 112112022 112102002 112012000 112012200 112012210
126
+ 112012020 112012002 112002100 112002102 112002010 112002210 112002012 112000200 112100200 112100202
127
+ 112110202 112101202 112010200 112010220 112011220 112011222 112010202 112011202 112010212 112001200
128
+ 112001220 112001202 112001212 112000210 112000212 112000201 112000020 112100020 112100022 112110022
129
+ 112101022 112010020 112010022 112011022 112001020 112001022 112000002 112100002 112010002 112001002
130
+ 112000102 112000012 102100000 102120000 102121000 102121020 102121002 102121102 102121012 102120100
131
+ 102120010 102122010 102122110 102120012 102120001 102102000 102112000 102112020 102112002 102102100
132
+ 102102010 102102012 102100020 102110020 102110022 102111022 102101020 102101022 102100002 102110002
133
+ 102101002 102100102 102100012 102010000 102012000 102012100 102012102 102012010 102012210 102012012
134
+ 102010200 102011200 102011202 102011212 102010201 102010020 102011020 102011022 102010002 102011002
135
+ 102010102 102010012 102001000 102021000 102021100 102021102 102021010 102021210 102021012 102001200
136
+ 102001210 102001212 102001020 102001002 102001102 102001012 102000100 102020100 102020110 102022110
137
+ 102020101 102002100 102002110 102000102 102000010 102020010 102002010 102000012 102000001 102020001
138
+ 102000201 100020000 110020000 110022000 110122000 110122020 110122002 110122012 110022100 110022010
139
+ 110022012 110020020 111020020 110021020 110021022 110020002 110120002 110021002 110020012 101020000
140
+ 101020020 100021000 100021020 100021002 100021012 100020001 100002000 110002000 110002020 110102020
141
+ 110102022 110112022 110012020 110012022 110002002 110102002 110012002 110002012 100102000 100102002
142
+ 100112002 100102012 100012000 100012020 100012002 100012012 100002100 100002010 100002012 100000002
143
+ 110000002 100010002 100001002 010000000 210000000 210100000 212100000 212110000 212112000 212112010
144
+ 212110200 212111200 212110020 212111020 212110002 212110012 212101000 212121000 212121010 212121210
145
+ 212101200 212101210 212101212 212111212 212101020 212100010 212120010 212102010 212100012 210120000
146
+ 210121000 210121020 210121002 210102000 210112000 210112020 210112002 210112012 210102010 210102012
147
+ 210100002 210110002 210101002 210010000 212010000 212010010 210210000 210211000 210211200 210211020
148
+ 210211002 210210010 210012000 210012010 210010200 210011200 210011220 210011202 210010210 210010020
149
+ 210011020 210010002 210011002 210010012 210001000 210201000 210201010 210221010 210201210 210021000
150
+ 210021010 210021210 210001200 210001210 210001020 210001002 210000010 212000010 210200010 210020010
151
+ 210002010 210000210 210000012 010200000 010210000 010212000 010212010 010210200 010211200 010211220
152
+ 010210020 010211020 010210002 010201000 010221000 010221010 010201200 010201020 010200010 010220010
153
+ 010202010 010020000 010120000 010120002 010020010 010000200 010010200 010010220 010010202 010001200
154
+ 010000020 010010020 000010000 200010000 020010000
155
+ ]
156
+ end
157
+
158
+
159
+ desc 'Lists the congruent boards (from cache)'
160
+ task :congruent => :environment do
161
+ all_boards_cached.each { |board| puts board }
162
+ end
163
+
164
+
165
+ desc 'Computes all congruent boards (takes a while)'
166
+ task :compute_congruent => :environment do
167
+ def self.all_boards(game=TTT::Game.new, boards=[])
168
+ return if boards.any? { |prev_board| TTT::Game.congruent? prev_board, game.board }
169
+ boards << game.board
170
+ game.available_moves.each do |position|
171
+ next_game = game.pristine_mark position
172
+ all_boards next_game, boards
173
+ end
174
+ boards
175
+ end
176
+ all_boards.each { |board| puts board }
177
+ end
178
+
179
+
180
+ desc 'Show a 9-digit formatted board in tic-tac-toe format'
181
+ task :show => :environment do
182
+ raise 'pass argument board=000000000' unless ENV['board']
183
+ puts TTT::Game.new(ENV['board']).board(:ttt)
184
+ end
185
+
186
+
187
+ desc 'An ultra-simple CLI to play the game'
188
+ task :play => :environment do
189
+ game = TTT::Game.new ENV['board'] || '000000000'
190
+ comp = TTT::ComputerPlayer.new(game)
191
+ until game.over?
192
+ comp.take_turn
193
+ puts game.board(:ttt)
194
+ break if game.over?
195
+ game.mark $stdin.gets.to_i
196
+ end
197
+ puts "The game is over.", (game.tie? ? "No one wins" : "Player #{game.winner} wins.")
198
+ end
199
+
200
+
201
+ desc 'Computes some stats that were listed on Wikipedia to ensure numbers are correct'
202
+ task :stats => :environment do
203
+ boards = all_boards_cached.select { |board| TTT::Game.new(board).over? } # according to http://en.wikipedia.org/wiki/Tic-tac-toe
204
+ puts "#{all_boards_cached.size} unique boards (congruent classes)" # no expectation, size is 765
205
+ puts "#{boards.size} unique end states" # expect 138 (true)
206
+ puts "#{boards.select { |board| TTT::Game.new(board).winner == 1 }.size} unique ways for player 1 to win" # expect 91 (true)
207
+ puts "#{boards.select { |board| TTT::Game.new(board).winner == 2 }.size} unique ways for player 2 to win" # expect 44 (true)
208
+ puts "#{boards.select { |board| TTT::Game.new(board).tie? }.size} unique ways to tie" # expect 3 (true)
209
+ end
210
+
211
+
212
+ desc 'Computes ratings for each board for each player'
213
+ task :ratings => :environment do
214
+ require 'pp'
215
+ boards = {}
216
+ all_boards_cached.each do |board|
217
+ tree = TTT::Rating.new board
218
+ boards[board] = {
219
+ 1 => tree.rating_for(1),
220
+ 2 => tree.rating_for(2),
221
+ }
222
+ end
223
+ pp boards
224
+ end
225
+
226
+
227
+ desc 'CLI to explore the game tree'
228
+ task :explore => :environment do
229
+ module TTT
230
+ class Game
231
+ # overwrite how board(:ttt) displays so that we can get something condensed enough to display
232
+ # many on the same line (colour allows us to distinguish the boardspace without using any
233
+ # more characters than are required to display the markings themselves)
234
+ def board(format=nil)
235
+ return @board unless format
236
+ @board.gsub(/[^12]/, ' ').scan(/.../).map { |line| "\e[44m#{line}\e[0m" }.join("\n")
237
+ end
238
+ end
239
+ end
240
+
241
+ module TTT
242
+ class ExploreTree
243
+
244
+ attr_accessor :tree
245
+
246
+ def initialize
247
+ self.tree = TTT::Rating.new
248
+ end
249
+
250
+ def explore!
251
+ loop do
252
+ display_adjacent commands, tree
253
+ print '> '
254
+ handle_command $stdin.gets
255
+ end
256
+ end
257
+
258
+ def display_adjacent(*displayables)
259
+ line_sets = []
260
+ queues = displayables.map { |displayable| displayable.to_s.split "\n" }
261
+ until queues.all?(&:empty?)
262
+ line_sets << [] # add a set for current line
263
+ queues.each { |queue| line_sets.last << queue.shift unless queue.empty? }
264
+ end
265
+ puts line_sets.map { |set| set.join ' ' }.join("\n")
266
+ end
267
+
268
+ def commands
269
+ "------------------\n"\
270
+ "| COMMANDS |\n"\
271
+ "|----------------|\n"\
272
+ "| move position |\n"\
273
+ "| show [depth] |\n"\
274
+ "| back |\n"\
275
+ "| rate player |\n"\
276
+ "| exit |\n"\
277
+ "------------------\n"
278
+ end
279
+
280
+ def handle_command(command)
281
+ args = command.split
282
+ command = args.shift
283
+ send command, *args if command
284
+ rescue => e
285
+ puts "Invalid command (#{e.message})"
286
+ end
287
+
288
+ def move(n)
289
+ self.tree = tree.children[n.to_i] || raise("No child at #{n.inspect}")
290
+ end
291
+
292
+ def colour_if_complete(tree)
293
+ return tree.to_s unless tree.game.over?
294
+ tree.to_s.split("\n").map { |line| "\e[33;1m#{line}\e[0m" }.join("\n")
295
+ end
296
+
297
+ def show(depth=1)
298
+ crnt = [tree]
299
+ (depth.to_i-1).times do
300
+ crnt = crnt.map { |node| node.children.values }.flatten # descend to next depth
301
+ end
302
+ display_adjacent(*crnt.map(&method(:colour_if_complete)))
303
+ end
304
+
305
+ def back
306
+ self.tree = tree.parent || raise("Can't go back, this is where we started")
307
+ end
308
+
309
+ def rate(player_number)
310
+ puts tree.rating_for(player_number.to_i)
311
+ end
312
+
313
+ def exit
314
+ Kernel.exit
315
+ end
316
+ end
317
+ end
318
+
319
+ TTT::ExploreTree.new.explore!
320
+ end
321
+
322
+ end
323
+
324
+
@@ -0,0 +1,35 @@
1
+ T(ic) T(ac) T(oe)
2
+ =================
3
+
4
+ TTT is a Tic Tac Toe lib, as well and two interfaces to play it.
5
+
6
+
7
+ Features
8
+ --------
9
+
10
+ * An aggressive computer player that will never lose
11
+ * A command line interface that will work in any terminal
12
+ * A GUI interface written with [Limelight][limelight]
13
+
14
+
15
+ Requirements
16
+ ------------
17
+
18
+ * FIXME (list of requirements)
19
+
20
+
21
+ Install
22
+ -------
23
+
24
+ * `gem install ttt`
25
+ * `gem install limelight` (you must be on jruby)
26
+
27
+
28
+ Author
29
+ ------
30
+
31
+ [Josh Cheek](joshcheek)
32
+
33
+
34
+ [limelight]: http://limelight.8thlight.com/
35
+ [joshcheek]: http://joshcheek.com/
data/bin/ttt ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'ttt'
4
+ require 'ttt/binary'
5
+
6
+ TTT::Binary.new ARGV
@@ -0,0 +1,39 @@
1
+ Feature: Binary
2
+ In order to interact with the interfaces
3
+ As a player
4
+ I want a binary where I can specify how I should be able to interact
5
+
6
+ Scenario: It is a binary
7
+ Given I look at bin/ttt
8
+ Then I see it is executable
9
+
10
+ Scenario: -h
11
+ Given I pass the it "-h" on the command line
12
+ Then it should display /^Usage/
13
+
14
+ Scenario: --help
15
+ Given I pass the it "--help" on the command line
16
+ Then it should display /^Usage/
17
+
18
+ Scenario: no input displays help
19
+ Given I pass the it "" on the command line
20
+ Then it should display /^Usage/
21
+
22
+ Scenario: -i without an arg
23
+ Given I pass the it "-i" on the command line
24
+ Then it should print /missing argument: -i/ to stderr
25
+ And it should exit with code of 1
26
+
27
+
28
+ Scenario: --interface, without an arg
29
+ Given I pass the it "--interface" on the command line
30
+ Then it should print /missing argument: --interface/ to stderr
31
+ And it should exit with code of 1
32
+
33
+ Scenario: -i cli
34
+ Given I pass the it "-i cli" on the command line
35
+ Then it should welcome me to Tic Tac Toe
36
+
37
+ Scenario: -i not_real_interface
38
+ Given I pass the it "-i not_real_interface" on the command line
39
+ Then it should print /"not_real_interface" is not a valid interface/ to stderr