boggler 0.0.1
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +4 -0
- data.tar.gz.sig +3 -0
- data/bin/boggler +3 -0
- data/bin/boggler_solver +3 -0
- data/lib/boggler.rb +24 -0
- data/lib/boggler/benchmarking.rb +15 -0
- data/lib/boggler/cell.rb +95 -0
- data/lib/boggler/core.rb +41 -0
- data/lib/boggler/dice.rb +83 -0
- data/lib/boggler/dictionary.rb +76 -0
- data/lib/boggler/die.rb +18 -0
- data/lib/boggler/grid.rb +94 -0
- data/lib/boggler/solver.rb +27 -0
- data/lib/boggler/version.rb +3 -0
- data/readme.md +160 -0
- data/vendor/dictionary.txt +62916 -0
- metadata +115 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 86259e476a7f531b684261ce5062509d9500c37e
|
4
|
+
data.tar.gz: 7da27f1ea047c7fad9a7fa8690980435b2368b13
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b1b71f7431211d276e4a96283ab96c674e363ccb1a37eb14a6670aaa9e95ef4cc0186a0f664b1c2d3d213da2a1b04cb55a3eafc9b64f19e38d06acf3cbd5cd9b
|
7
|
+
data.tar.gz: 5653dfac6b0ba0daec843046192aa99b8accaf37b8039766cd2495c03e5367e0d7a01883de09cd380e8e6aafc49ecf9a5341876db7fd3c09baa7785c66e557b5
|
checksums.yaml.gz.sig
ADDED
data.tar.gz.sig
ADDED
data/bin/boggler
ADDED
data/bin/boggler_solver
ADDED
data/lib/boggler.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Boggler
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def load
|
7
|
+
require File.join(load_path, 'core')
|
8
|
+
require File.join(load_path, 'die')
|
9
|
+
require File.join(load_path, 'dice')
|
10
|
+
require File.join(load_path, 'grid')
|
11
|
+
require File.join(load_path, 'cell')
|
12
|
+
require File.join(load_path, 'dictionary')
|
13
|
+
require File.join(load_path, 'benchmarking')
|
14
|
+
require File.join(load_path, 'solver')
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def load_path
|
20
|
+
@load_path ||= File.expand_path('../boggler', __FILE__)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
Boggler.load
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
3
|
+
module Boggler
|
4
|
+
module Benchmarking
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def measure(description, &block)
|
8
|
+
benchmark_format = "%n\t#{Benchmark::FORMAT}"
|
9
|
+
|
10
|
+
puts Benchmark.measure(description) {
|
11
|
+
yield if block_given?
|
12
|
+
}.format(benchmark_format)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/boggler/cell.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
module Boggler
|
2
|
+
class Cell
|
3
|
+
attr_reader :row, :column, :letter, :word, :previous
|
4
|
+
|
5
|
+
MOVES = [
|
6
|
+
[-1, -1],
|
7
|
+
[-1, 0],
|
8
|
+
[-1, 1],
|
9
|
+
[ 0, -1],
|
10
|
+
[ 0, 1],
|
11
|
+
[ 1, -1],
|
12
|
+
[ 1, 0],
|
13
|
+
[ 1, 1],
|
14
|
+
]
|
15
|
+
|
16
|
+
def initialize(
|
17
|
+
grid, row, column, dictionary = nil, used = nil, previous = nil)
|
18
|
+
@row = row
|
19
|
+
@column = column
|
20
|
+
@letter = grid[row - 1][column - 1]
|
21
|
+
@grid = grid
|
22
|
+
@previous = previous
|
23
|
+
@dictionary = dictionary || Dictionary.words
|
24
|
+
|
25
|
+
set_used used
|
26
|
+
@used[row][column] = true
|
27
|
+
|
28
|
+
@word = ((previous && previous.word) || '') + @letter
|
29
|
+
set_dictionary
|
30
|
+
end
|
31
|
+
|
32
|
+
def next_cell
|
33
|
+
begin
|
34
|
+
iterator.next
|
35
|
+
rescue StopIteration
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def valid_word?
|
41
|
+
!!@dictionary['']
|
42
|
+
end
|
43
|
+
|
44
|
+
def possible_word?
|
45
|
+
!!@dictionary
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def set_dictionary
|
51
|
+
case word.length
|
52
|
+
when 1, 2
|
53
|
+
@dictionary = {}
|
54
|
+
when 3
|
55
|
+
@dictionary = Dictionary.words[@word]
|
56
|
+
else
|
57
|
+
@dictionary = @dictionary[@word[-1]]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_used(used)
|
62
|
+
@used = used && used.dup
|
63
|
+
@used ||= {}
|
64
|
+
|
65
|
+
@grid.size.times do |i|
|
66
|
+
row = used && used[i + 1] && used[i + 1].dup
|
67
|
+
@used[i + 1] = row || {}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def iterator
|
72
|
+
@iterator ||= neighbors.each
|
73
|
+
end
|
74
|
+
|
75
|
+
def neighbors
|
76
|
+
return @neighbors if @neighbors
|
77
|
+
|
78
|
+
@neighbors = []
|
79
|
+
|
80
|
+
MOVES.each do |move|
|
81
|
+
next_row = row + move.first
|
82
|
+
next_column = column + move.last
|
83
|
+
|
84
|
+
if next_row > 0 && next_row <= @grid.size &&
|
85
|
+
next_column > 0 && next_column <= @grid.size &&
|
86
|
+
!@used[next_row][next_column]
|
87
|
+
@neighbors << Cell.new(
|
88
|
+
@grid, next_row, next_column, @dictionary, @used, self)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
@neighbors
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/boggler/core.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Boggler
|
2
|
+
module Core
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def run
|
6
|
+
start
|
7
|
+
Solver.solve @grid
|
8
|
+
end
|
9
|
+
|
10
|
+
def game
|
11
|
+
start
|
12
|
+
|
13
|
+
time = Time.now
|
14
|
+
Dictionary.words
|
15
|
+
while Time.now - time < 3 * 60 do
|
16
|
+
end
|
17
|
+
|
18
|
+
puts "\n" * 100
|
19
|
+
|
20
|
+
Solver.solve @grid
|
21
|
+
end
|
22
|
+
|
23
|
+
def seed
|
24
|
+
return @seed if defined?(@seed)
|
25
|
+
#@seed = 7567 # postdated appears
|
26
|
+
#@seed = 532183 # used for construction of grid word construction
|
27
|
+
@seed = rand(999_999)
|
28
|
+
srand @seed
|
29
|
+
@seed
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def start
|
35
|
+
seed
|
36
|
+
puts "Randomized with seed #{seed}"
|
37
|
+
@grid = Grid.new
|
38
|
+
puts @grid.to_s
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/boggler/dice.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module Boggler
|
2
|
+
module Dice
|
3
|
+
extend self
|
4
|
+
|
5
|
+
BOGGLE_DICE = [
|
6
|
+
Die.new(%W{a a c i o t}),
|
7
|
+
Die.new(%W{a h m o r s}),
|
8
|
+
Die.new(%W{e g k l u y}),
|
9
|
+
Die.new(%W{a b i l t y}),
|
10
|
+
Die.new(%W{a c d e m p}),
|
11
|
+
Die.new(%W{e g i n t v}),
|
12
|
+
Die.new(%W{g i l r u w}),
|
13
|
+
Die.new(%W{e l p s t u}),
|
14
|
+
Die.new(%W{d e n o s w}),
|
15
|
+
Die.new(%W{a c e l r s}),
|
16
|
+
Die.new(%W{a b j m o q}),
|
17
|
+
Die.new(%W{e e f h i y}),
|
18
|
+
Die.new(%W{e h i n p s}),
|
19
|
+
Die.new(%W{d k n o t u}),
|
20
|
+
Die.new(%W{a d e n v z}),
|
21
|
+
Die.new(%W{b i f o r x}),
|
22
|
+
]
|
23
|
+
|
24
|
+
STATIC_BOGGLE = [
|
25
|
+
Die.new(%W{e g i n t v}),
|
26
|
+
Die.new(%W{e l p s t u}),
|
27
|
+
Die.new(%W{a b j m o q}),
|
28
|
+
Die.new(%W{e h i n p s}),
|
29
|
+
Die.new(%W{d k n o t u}),
|
30
|
+
Die.new(%W{a d e n v z}),
|
31
|
+
Die.new(%W{b i f o r x}),
|
32
|
+
].shuffle
|
33
|
+
|
34
|
+
STATIC_DICE = [
|
35
|
+
Die.new(['h']),
|
36
|
+
Die.new(['e']),
|
37
|
+
Die.new(['a']),
|
38
|
+
Die.new(['r']),
|
39
|
+
STATIC_BOGGLE[0],
|
40
|
+
Die.new(['o']),
|
41
|
+
Die.new(['r']),
|
42
|
+
STATIC_BOGGLE[1],
|
43
|
+
STATIC_BOGGLE[2],
|
44
|
+
STATIC_BOGGLE[3],
|
45
|
+
Die.new(['m']),
|
46
|
+
STATIC_BOGGLE[4],
|
47
|
+
STATIC_BOGGLE[5],
|
48
|
+
STATIC_BOGGLE[6],
|
49
|
+
Die.new(['y']),
|
50
|
+
Die.new(['e']),
|
51
|
+
]
|
52
|
+
|
53
|
+
def get(opts = {})
|
54
|
+
opts ||= {}
|
55
|
+
|
56
|
+
case opts[:method]
|
57
|
+
when :random
|
58
|
+
random_dice
|
59
|
+
when :boggle
|
60
|
+
BOGGLE_DICE.shuffle
|
61
|
+
when :big_boggle
|
62
|
+
[
|
63
|
+
BOGGLE_DICE.shuffle,
|
64
|
+
BOGGLE_DICE.shuffle,
|
65
|
+
BOGGLE_DICE.shuffle,
|
66
|
+
BOGGLE_DICE.shuffle,
|
67
|
+
].flatten
|
68
|
+
when :static
|
69
|
+
STATIC_DICE
|
70
|
+
else
|
71
|
+
BOGGLE_DICE.shuffle
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def random_dice
|
78
|
+
16.times.map do
|
79
|
+
Die.new
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Boggler
|
2
|
+
module Dictionary
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def words
|
6
|
+
return @words if defined?(@words)
|
7
|
+
|
8
|
+
content = nil
|
9
|
+
|
10
|
+
Benchmarking.measure('dictionary') do
|
11
|
+
path = File.expand_path('../../../vendor/dictionary.txt', __FILE__)
|
12
|
+
content = File.read(path)
|
13
|
+
content = content.split("\n")
|
14
|
+
@words = words_to_hash(content)
|
15
|
+
end
|
16
|
+
|
17
|
+
@words
|
18
|
+
end
|
19
|
+
|
20
|
+
def words_to_hash(words)
|
21
|
+
hash = {}
|
22
|
+
|
23
|
+
words.each do |word|
|
24
|
+
word = word.split('')
|
25
|
+
|
26
|
+
key = word[0..2].join
|
27
|
+
letters = word[3..-1]
|
28
|
+
|
29
|
+
merge_word hash, key, word_to_hash(letters)
|
30
|
+
end
|
31
|
+
|
32
|
+
hash
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def merge_word(hash, key, word_hash)
|
38
|
+
if hash[key]
|
39
|
+
merge_word_hash hash[key], word_hash
|
40
|
+
else
|
41
|
+
hash[key] = word_hash
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def merge_word_hash(hash, word_hash)
|
46
|
+
hash.merge!(word_hash) do |key, old_value, new_value|
|
47
|
+
if old_value.is_a?(Hash) && new_value.is_a?(Hash)
|
48
|
+
merge_word_hash(old_value, new_value)
|
49
|
+
else
|
50
|
+
old_value || new_value
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def word_to_hash(letters)
|
56
|
+
if letters.empty?
|
57
|
+
{'' => true}
|
58
|
+
else
|
59
|
+
letters_to_hash({}, letters)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def letters_to_hash(hash = {}, letters = [])
|
64
|
+
hash ||= {}
|
65
|
+
letters.each_with_index do |letter, i|
|
66
|
+
if letters.length > 1
|
67
|
+
hash[letter] = letters_to_hash(hash[letter], letters[(i + 1)..-1])
|
68
|
+
return hash
|
69
|
+
else
|
70
|
+
hash[letter] = {'' => true}
|
71
|
+
return hash
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/boggler/die.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Boggler
|
2
|
+
class Die
|
3
|
+
def initialize(sides = [])
|
4
|
+
@sides = sides || []
|
5
|
+
@sides << random_letter if sides.empty?
|
6
|
+
end
|
7
|
+
|
8
|
+
def roll
|
9
|
+
@sides.sample
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def random_letter
|
15
|
+
rand(10...36).to_s 36
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/boggler/grid.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module Boggler
|
2
|
+
class Grid
|
3
|
+
def initialize
|
4
|
+
@grid = []
|
5
|
+
|
6
|
+
@dice = Dice.get
|
7
|
+
@size = Math.sqrt(@dice.length).to_i
|
8
|
+
|
9
|
+
build_grid
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
row_strings = []
|
14
|
+
|
15
|
+
@grid.each do |row|
|
16
|
+
row_strings << row_string_for(row)
|
17
|
+
end
|
18
|
+
|
19
|
+
grid_string = separator
|
20
|
+
row_strings.each do |row|
|
21
|
+
grid_string += row
|
22
|
+
grid_string += separator
|
23
|
+
end
|
24
|
+
|
25
|
+
grid_string
|
26
|
+
end
|
27
|
+
|
28
|
+
def words
|
29
|
+
return @words if defined?(@words)
|
30
|
+
|
31
|
+
@words = []
|
32
|
+
|
33
|
+
@size.times do |i|
|
34
|
+
@size.times do |j|
|
35
|
+
words_starting_at(i + 1, j + 1)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
@words
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def words_starting_at(row, column)
|
45
|
+
start_cell = Cell.new(@grid, row, column)
|
46
|
+
words_from_cell start_cell
|
47
|
+
end
|
48
|
+
|
49
|
+
def words_from_cell(cell)
|
50
|
+
@words << cell.word if cell.valid_word?
|
51
|
+
next_cell = next_possible_cell(cell)
|
52
|
+
words_from_cell(next_cell) if next_cell
|
53
|
+
end
|
54
|
+
|
55
|
+
def next_possible_cell(cell)
|
56
|
+
return unless cell
|
57
|
+
candidate = cell && cell.next_cell
|
58
|
+
while candidate && !candidate.possible_word?
|
59
|
+
candidate = cell.next_cell
|
60
|
+
end
|
61
|
+
|
62
|
+
return candidate if candidate
|
63
|
+
|
64
|
+
next_possible_cell cell.previous
|
65
|
+
end
|
66
|
+
|
67
|
+
def build_grid
|
68
|
+
letters = []
|
69
|
+
|
70
|
+
@dice.each_with_index do |die, i|
|
71
|
+
if i % @size == 0
|
72
|
+
@grid << letters unless letters.empty?
|
73
|
+
letters = []
|
74
|
+
end
|
75
|
+
|
76
|
+
letters << die.roll
|
77
|
+
end
|
78
|
+
|
79
|
+
@grid << letters
|
80
|
+
end
|
81
|
+
|
82
|
+
def row_string_for(row)
|
83
|
+
row_string = '|'
|
84
|
+
row.each do |cell|
|
85
|
+
row_string += " #{cell.upcase} |"
|
86
|
+
end
|
87
|
+
row_string + "\n"
|
88
|
+
end
|
89
|
+
|
90
|
+
def separator
|
91
|
+
@sep ||= '-' + ('-' * (4 * @size)) + "\n"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|