boggle 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in trails.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011 Kirk Scheibelhut <kjs@scheibo.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person ob-
4
+ taining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without restric-
6
+ tion, including without limitation the rights to use, copy, modi-
7
+ fy, merge, publish, distribute, sublicense, and/or sell copies of
8
+ the Software, and to permit persons to whom the Software is fur-
9
+ nished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
16
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN-
17
+ FRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ # Boggle
2
+
3
+ Very dirty program to let you play 4x4 boggle and learn new words. Has the following options:
4
+
5
+ opts = Trollop::options do
6
+ opt :define, "Show a definition dump at the end", :short => 'd'
7
+ opt :interactive, "Continue printing games", :short => 'i'
8
+ opt :dictionary, "Change the default dictionary", :default => 'dicts/just_words.dict'
9
+ opt :board, "Give a string as input to be the board", :default => ""
10
+ end
11
+
12
+ Where `--interactive` has yet to be implemented, but the idea behind if would be that instead of loading up a dict each time you could continue to play with the same dictionary. Has no timer going on right now, just use a watch to time the three minutes. Use 'XYZZY' to exit any prompt (TODO: should be control-D or something as well).
13
+
14
+ As it stands, with a full scrabble dictionary load (dicts/osw.txt) it takes about 2.6 seconds to load up on
15
+
16
+ Darwin mac.local 10.6.0 Darwin Kernel Version 10.6.0: Wed Nov 10 18:13:17 PST 2010; root:xnu-1504.9.26~3/RELEASE_I386 i386
17
+
18
+ with intel core 2 duo 2.4ghz. It takes about 200MB of ram to load the dictionary into memory. With this dictionary a given board takes around 0.14 seconds to solve (same specs). 'just_words' only has around 40k words as opposed to 260k, so it ends up being considerably faster. There are some ways to optimize the solving, but at 0.14 seconds thats not really the problem, the problem is the loading which even then isn't *horrid*.
19
+
20
+ ## Usage
21
+
22
+ Not actually a gem, just
23
+
24
+ git clone git://github.com/scheibo/boggle.git
25
+ cd boggle
26
+ ./boggle
27
+
28
+ ## Future
29
+
30
+ - timer (3 min countdown)
31
+ - interactive mode (save loading the dict)
32
+ - Ctrl+D to exit
33
+
34
+ ## Copyright
35
+
36
+ Hopefully Parker Brothers (Hasbro) isn't going to get mad I've recreated their game. All copyright, trademarks, etc belong to them.
37
+
38
+ All the dictionaries are property of whoever compiled them, and hopefully I'm not breaking any copyright by including them in this program (I just took most of them off of [wordlists.sourceforge.com](http://wordlists.sourceforge.com)).
39
+
40
+ As for the code to the game itself:
41
+
42
+ `boggle` is Copyright (c) 2011 [Kirk Scheibelhut](http://scheibo.com/about) and distributed under the MIT license.<br />
43
+ See the `LICENSE` file for further details regarding licensing and distribution.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/boggle ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+ lib = File.expand_path('../../lib', __FILE__)
3
+ $:.unshift(lib) if File.directory?(lib) && !$:.include?(lib)
4
+
5
+ require 'boggle'
6
+ require 'optparse'
7
+
8
+ opts = {}
9
+ ARGV.options do |argv|
10
+ argv.banner = "Usage: boggle [options]"
11
+ argv.on("-d", "--define", "Show a definition dump at the end") { |d| opts[:define] = d }
12
+ argv.on("--dictionary", String, "Change the default dictionary") { |dict| opts[:dictionary] = dict }
13
+ argv.on("-b", "--board", String, "Give a string as input to be the board") { |b| opts[:board] = b }
14
+ argv.on("-v", "--variant", Integer, "Give the board variant") { |v| opts[:variant] = v }
15
+ argv.on("-s", "--size", Integer, "Give the size of the board to use") { |s| opts[:size] = s }
16
+ end
17
+
18
+ opts = {:dictionary => "system.dict", :board => "", :variant => 0, :size => 4}.merge opts
19
+ opts[:size] = Math.sqrt(opts[:board].size).to_i if !opts[:board].empty?
20
+ Boggle::Game.new(Boggle::Board.new(opts), opts).start
data/boggle.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "boggle/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "boggle"
7
+ s.version = Boggle::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Kirk Scheibelhut"]
10
+ s.email = ["kjs@scheibo.com"]
11
+ s.homepage = "https://github.com/scheibo/boggle"
12
+ s.summary = "Boggle CLI app which helps improve your game"
13
+ s.description = s.summary
14
+
15
+ s.rubyforge_project = "boggle"
16
+
17
+ s.add_dependency "algorithms"
18
+
19
+ s.add_development_dependency "rake"
20
+ s.add_development_dependency "bundler", "~> 1.0.0"
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.default_executable = 'boggle'
26
+ s.require_paths = ["lib"]
27
+ end
Binary file
Binary file
data/dicts/system.dict ADDED
Binary file
data/dicts/wls.tar.gz ADDED
Binary file
data/lib/boggle.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Boggle; end
2
+
3
+ require 'boggle/version'
4
+ require 'boggle/trie'
5
+ require 'boggle/solver'
6
+ require 'boggle/game'
7
+ require 'boggle/board'
@@ -0,0 +1,121 @@
1
+ class Boggle::Board
2
+ attr_reader :size
3
+
4
+ def initialize(opts={})
5
+ @size = opts[:size]
6
+ if opts[:board].empty?
7
+ letters = Boggle::Board.distributions(@size, opts[:variant]).sort_by{ rand }.map { |d| d.sample }
8
+ else
9
+ letters = letters_from_string opts[:board]
10
+ end
11
+
12
+ @board = []
13
+ @size.times do
14
+ @board << letters.pop(@size)
15
+ end
16
+ end
17
+
18
+ def to_s
19
+ s = ""
20
+ @size.times { s<<"+"; s<<"-"*3 }; s<<"+\n"
21
+ @size .times do |row|
22
+ s << "|"
23
+ @size.times do |col|
24
+ (l=@board[row][col]) == "Qu" ? s << " #{l}|" : s << " #{l} |"
25
+ end
26
+ s<<"\n"; @size.times { s<<"+"; s<<"-"*3 }; s<<"+\n"
27
+ end
28
+ s
29
+ end
30
+
31
+ def [](row, col)
32
+ ( (row < 0) || (col < 0) || (row >= @size) || (col >= @size) ) ? nil : @board[row][col]
33
+ end
34
+
35
+ # deepcopy first
36
+ def []=(row, col, val)
37
+ @board[row][col]=val
38
+ end
39
+
40
+ def deepcopy
41
+ Marshal.load( Marshal.dump(self) )
42
+ end
43
+
44
+ def self.distributions( size, variant ) # 4,0 gives standard distrubtion
45
+
46
+ distros = [[
47
+ # http://everything2.com/title/Boggle
48
+ %w{
49
+ ASPFFK NUIHMQ OBJOAB LNHNRZ
50
+ AHSPCO RYVDEL IOTMUC LREIXD
51
+ TERWHV TSTIYD WNGEEH ERTTYL
52
+ OWTOAT AEANEG EIUNES TOESSI
53
+ },
54
+
55
+ # http://www.boardgamegeek.com/thread/300565/review-from-a-boggle-veteran-and-beware-differen
56
+ %w{
57
+ AAEEGN ELRTTY AOOTTW ABBJOO
58
+ EHRTVW CIMOTV DISTTY EIOSST
59
+ DELRVY ACHOPS HIMNQU EEINSU
60
+ EEGHNW AFFKPS HLNNRZ DEILRX
61
+ },
62
+
63
+ %w{
64
+ AACIOT AHMORS EGKLUY ABILTY
65
+ ACDEMP EGINTV GILRUW ELPSTU
66
+ DENOSW ACELRS ABJMOQ EEFHIY
67
+ EHINPS DKNOTU ADENVZ BIFORX
68
+ }
69
+ ],[
70
+
71
+ # http://boardgamegeek.com/thread/300883/letter-distribution
72
+ %w{
73
+ aaafrs aaeeee aafirs adennn aeeeem
74
+ aeegmu aegmnn afirsy bjkqxz ccenst
75
+ ceiilt ceilpt ceipst ddhnot dhhlor
76
+ dhlnor dhlnor eiiitt emottt ensssu
77
+ fiprsy gorrvw iprrry nootuw ooottu
78
+ }.map(&:upcase),
79
+
80
+ %w{
81
+ AAAFRS AAEEEE AAFIRS ADENNN AEEEEM
82
+ AEEGMU AEGMNN AFIRSY BJKQXZ CCNSTW
83
+ CEIILT CEILPT CEIPST DHHNOT DHHLOR
84
+ DHLNOR DDLNOR EIIITT EMOTTT ENSSSU
85
+ FIPRSY GORRVW HIPRRY NOOTUW OOOTTU
86
+ }
87
+ ]]
88
+
89
+ min_size = 4
90
+ distros[size-min_size].map { |dist|
91
+ dist.map { |die|
92
+ die.split(//).map { |letter|
93
+ # our distrubutions return Qu, not Q's
94
+ letter == 'Q' ? 'Qu' : letter
95
+ }
96
+ }
97
+ }[variant]
98
+ end
99
+
100
+ private
101
+
102
+ def letters_from_string(string)
103
+ letters = []
104
+ row = []
105
+ string.each_char do |c|
106
+ if c == "Q"
107
+ row << "Qu"
108
+ elsif c == "u"
109
+ row
110
+ else
111
+ row << c
112
+ end
113
+ if row.size == @size
114
+ letters << row
115
+ row = []
116
+ end
117
+ end
118
+ letters.reverse.flatten
119
+ end
120
+
121
+ end
@@ -0,0 +1,165 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../boggle')
2
+
3
+ require 'zlib'
4
+ require 'trie'
5
+
6
+ class Boggle::Game
7
+
8
+ def self.create_dictionary(file_name)
9
+ trie = Boggle::Trie.new
10
+ File.open(file_name).each_line do |line|
11
+ idx = line.index " " # get first space
12
+ if idx.nil? # no definition
13
+ word, defn = line, "no definition available"
14
+ else
15
+ word, defn = line[0..idx-1], line[idx+1..line.size]
16
+ end
17
+ #word.chomp! # case where we had just words on the line
18
+
19
+ # skip words that are too small for boggle
20
+ if word.size > 2
21
+ trie[word.upcase] = defn
22
+ end
23
+ end
24
+
25
+ dump = Marshal.dump(trie)
26
+ dict_file = File.new(File.expand_path("../../../dicts/#{file_name.chomp('.txt')}.dict", __FILE__), "w")
27
+ dict_file = Zlib::GzipWriter.new(dict_file)
28
+ dict_file.write dump
29
+ dict_file.close
30
+ end
31
+
32
+ def initialize(board=nil, opts=nil)
33
+ if ! board.nil?
34
+ @board = board
35
+ else
36
+ @board = Boggle::Board.new
37
+ end
38
+ @opts = opts
39
+ @words = []
40
+ @trie = Boggle::Trie.new
41
+ end
42
+
43
+ def start
44
+ dict = @opts[:dictionary]
45
+ fill_trie(dict) # need to only load this once
46
+ s = Boggle::Solver.new(@trie)
47
+ @found_pairs = s.start(@board)
48
+
49
+ display
50
+
51
+ good = []
52
+ bad = []
53
+ @words.uniq.each do |w|
54
+ if @found_pairs.delete w
55
+ good << w
56
+ else
57
+ bad << w
58
+ end
59
+ end
60
+
61
+ finish good, bad
62
+ end
63
+
64
+ private
65
+
66
+ def score(words)
67
+ words.reduce(0) { |a,w| a+score_word(w) }
68
+ end
69
+
70
+ def finish(good, bad)
71
+ puts "Your score was: #{score(good)}"
72
+ puts "The following words were bad:"
73
+ bad.each { |b| print "#{b} " }
74
+ puts
75
+
76
+ puts "You missed the following words"
77
+ dump_words
78
+ end
79
+
80
+ def dump_words
81
+ already_done = []
82
+ words = []
83
+ @found_pairs.keys.sort.each do |word|
84
+ if @found_pairs.include? word+"S"
85
+ words << [word, "#{word}(S)"]
86
+ already_done << word+"S"
87
+ else
88
+ words << [word, word] unless already_done.include? word
89
+ end
90
+ end
91
+
92
+ # now we need to make our groups
93
+ groups =[[],[],[],[],[],[]] # 3, 4, 5, 6, 7, 8+
94
+ words.sort_by { |e| e.first.size }.each do |pk|
95
+ case pk[0].size
96
+ when 3 then groups[0] << pk[1]
97
+ when 4 then groups[1] << pk[1]
98
+ when 5 then groups[2] << pk[1]
99
+ when 6 then groups[3] << pk[1]
100
+ when 7 then groups[4] << pk[1]
101
+ else groups[5] << pk[1]
102
+ end
103
+ end
104
+
105
+ puts
106
+ groups.each do |group|
107
+ break_after = 0
108
+ group.each do |rep|
109
+ print "#{rep} "
110
+ break_after+=rep.size
111
+ if break_after > 80
112
+ print "\n"
113
+ break_after = 0
114
+ end
115
+ end
116
+ print "\n\n" unless group.empty?
117
+ end
118
+
119
+ if @opts[:define]
120
+ @found_pairs.keys.sort.each { |word| puts "#{word}: #{@found_pairs[word]}" }
121
+ else
122
+
123
+ while true
124
+ print "Define: "
125
+ word = STDIN.gets.chomp.upcase
126
+ if word == "XYZZY"
127
+ break
128
+ else
129
+ puts "#{word}: #{@trie[word]}"
130
+ end
131
+ end
132
+
133
+ end
134
+ end
135
+
136
+ def display
137
+ puts @board
138
+ while true
139
+ word = STDIN.gets.chomp.upcase
140
+ if word == "XYZZY"
141
+ break
142
+ else
143
+ @words << word
144
+ end
145
+ end
146
+ puts "Finished, you put down #{@words.size} words"
147
+ end
148
+
149
+ def fill_trie(file_name)
150
+ file = Zlib::GzipReader.open(File.expand_path("../../../dicts/#{file_name}", __FILE__))
151
+ @trie = Marshal.load file.read
152
+ file.close
153
+ end
154
+
155
+ def score_word(word)
156
+ case word.size
157
+ when 0,1,2 then 0
158
+ when 3,4 then 1
159
+ when 5 then 2
160
+ when 6 then 3
161
+ when 7 then 5
162
+ else 11
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,83 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../boggle')
2
+ require 'trie'
3
+ require 'game'
4
+
5
+ class Boggle::Solver
6
+
7
+ def initialize(trie)
8
+ @found_words = {} # going to use it like a set
9
+ @trie = trie
10
+ end
11
+
12
+ def in_trie?(prefix)
13
+ if (d = @trie.match(prefix.upcase)) # not nil = okay
14
+ if d.class == String
15
+ @found_words[prefix] = d
16
+ end
17
+ true
18
+ end
19
+ end
20
+
21
+ def start(board)
22
+ solve board
23
+ @found_words
24
+ end
25
+
26
+ def solve(board)
27
+ board.size.times do |row|
28
+ board.size.times do |col|
29
+ solve_frame(make_frame("", board, row, col))
30
+ end
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def make_frame(prefix, board, row, col)
37
+ frame = []
38
+ frame << (prefix + board[row,col])
39
+ new_board = board.deepcopy
40
+ new_board[row,col] = nil
41
+ frame << new_board
42
+ frame << row << col
43
+ frame
44
+ end
45
+
46
+ def solve_frame(frame)
47
+ next_frames(frame).each do |f|
48
+ prefix, b, r, c = f
49
+
50
+ if in_trie? prefix
51
+ # continue
52
+ solve_frame(f)
53
+ end
54
+ # otherwise we're at a dead end!, ignore the frame
55
+ # and continue to the other ones
56
+ end
57
+ end
58
+
59
+ def next_frames(frame)
60
+ # unpack
61
+ pre, board, row, col = frame
62
+
63
+ frames = []
64
+ # row before
65
+ frames << (board[row-1, col-1] && make_frame(pre, board, row-1, col-1))
66
+ frames << (board[row-1, col ] && make_frame(pre, board, row-1, col ))
67
+ frames << (board[row-1, col+1] && make_frame(pre, board, row-1, col+1))
68
+
69
+ # same row
70
+ frames << (board[row , col-1] && make_frame(pre, board, row , col-1))
71
+ # frames << (board[row , col ] && make_frame(pre, board, row, col ))
72
+ # is guaranteed to be nil, since at row,col we are empty
73
+ frames << (board[row , col+1] && make_frame(pre, board, row , col+1))
74
+
75
+ # row after
76
+ frames << (board[row+1, col-1] && make_frame(pre, board, row+1, col-1))
77
+ frames << (board[row+1, col ] && make_frame(pre, board, row+1, col ))
78
+ frames << (board[row+1, col+1] && make_frame(pre, board, row+1, col+1))
79
+
80
+ frames.compact
81
+ end
82
+
83
+ end
@@ -0,0 +1,36 @@
1
+ require 'algorithms'
2
+
3
+ class Boggle::Trie < Containers::Trie
4
+
5
+ # returns either nil if there is nothing along that path, true
6
+ # if that path exists in the tree and the word itself if it is an endpoint
7
+
8
+ def match(string)
9
+ string = string.to_s
10
+ return nil if string.empty?
11
+ match_recursive(@root, string, 0)
12
+ end
13
+
14
+ def match_recursive(node, string, index)
15
+ return nil if node.nil?
16
+
17
+ char = string[index]
18
+
19
+ if (char < node.char)
20
+ match_recursive(node.left, string, index)
21
+ elsif (char > node.char)
22
+ match_recursive(node.right, string, index)
23
+ else
24
+ return nil if node.nil?
25
+ if index == (string.length - 1)
26
+ if node.last?
27
+ return node.value
28
+ else
29
+ return true
30
+ end
31
+ end
32
+ match_recursive(node.mid, string, index+1)
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,3 @@
1
+ module Boggle
2
+ VERSION = "0.0.2"
3
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: boggle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kirk Scheibelhut
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-22 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: algorithms
16
+ requirement: &70186838444600 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70186838444600
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70186838475640 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70186838475640
36
+ - !ruby/object:Gem::Dependency
37
+ name: bundler
38
+ requirement: &70186838475140 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70186838475140
47
+ description: Boggle CLI app which helps improve your game
48
+ email:
49
+ - kjs@scheibo.com
50
+ executables:
51
+ - boggle
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - .gitignore
56
+ - Gemfile
57
+ - LICENSE
58
+ - README.md
59
+ - Rakefile
60
+ - bin/boggle
61
+ - boggle.gemspec
62
+ - dicts/default.dict
63
+ - dicts/scrabble.dict
64
+ - dicts/system.dict
65
+ - dicts/wls.tar.gz
66
+ - lib/boggle.rb
67
+ - lib/boggle/board.rb
68
+ - lib/boggle/game.rb
69
+ - lib/boggle/solver.rb
70
+ - lib/boggle/trie.rb
71
+ - lib/boggle/version.rb
72
+ homepage: https://github.com/scheibo/boggle
73
+ licenses: []
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ required_rubygems_version: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project: boggle
92
+ rubygems_version: 1.8.6
93
+ signing_key:
94
+ specification_version: 3
95
+ summary: Boggle CLI app which helps improve your game
96
+ test_files: []