boggle_solver 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.
data/README.markdown ADDED
@@ -0,0 +1,59 @@
1
+ Ruby program that employs graph theory to solve a word matrix puzzle (Boggle).
2
+
3
+ # Introduction
4
+ Boggle Master consists of a 5x5 grid of letters. The goal of the game is to make as many words as possible from this grid by connecting neighboring letters. To generate a random puzzle the game uses 25 dice with letters on each side. The dice are shaken and each fall into a slot in the grid.
5
+
6
+ # Example Grid
7
+
8
+ Below is an example grid. One possible word in this graph is `wear`. Arrows are added for convenience.
9
+
10
+ J O X T Y
11
+
12
+ A C D F P
13
+
14
+ Y K B E O
15
+
16
+ A M G P W
17
+ |
18
+ D S R<--A<--E
19
+
20
+ The goal of this project is to find all possible words. Mostly to serve as a benchmark with which to compare my performance.
21
+
22
+
23
+ # Coordinate System
24
+ Below are the indicies used to represent the dice in the board:
25
+
26
+ 0 1 2 3 4
27
+ 5 6 7 8 9
28
+ 10 11 12 13 14
29
+ 15 16 17 18 19
30
+ 20 21 22 23 24
31
+
32
+ # Connection Matrix
33
+ Adjacency matrix representing the connections in Boggle:
34
+
35
+ 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
36
+ 1 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
37
+ 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
38
+ 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
39
+ 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
40
+ 1 1 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0
41
+ 1 1 1 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0
42
+ 0 1 1 1 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0
43
+ 0 0 1 1 1 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0
44
+ 0 0 0 1 1 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0
45
+ 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 0
46
+ 0 0 0 0 0 1 1 1 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0 0
47
+ 0 0 0 0 0 0 1 1 1 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0 0
48
+ 0 0 0 0 0 0 0 1 1 1 0 0 1 0 1 0 0 1 1 1 0 0 0 0 0
49
+ 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 1 1 0 0 0 0 0
50
+ 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 0 0 0
51
+ 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 1 0 0 1 1 1 0 0
52
+ 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 1 0 0 1 1 1 0
53
+ 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 1 0 0 1 1 1
54
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0 0 0 0 1 1
55
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0
56
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 1 0 0
57
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 1 0
58
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 1 0 1
59
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0
data/bin/boggle_solver ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'boggle_solver/runner'
4
+
5
+ runner = BoggleSolver::Runner.new(ARGV)
6
+ runner.run
@@ -0,0 +1,16 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "boggle_solver"
3
+ s.summary = "Find all words in a letter matrix"
4
+ s.description = "File.read(File.join(File.dirname(__FILE__), 'README'))"
5
+ s.requirements = "[ 'An installed dictionary (most Unix systems have one)']"
6
+ s.version = "0.0.1"
7
+ s.author = "Casey Robinson"
8
+ s.email = "kc@rampantmonkey.com"
9
+ s.homepage = "http://rampantmonkey.com"
10
+ s.platform = Gem::Platform::RUBY
11
+ s.required_ruby_version = '>=1.9'
12
+ s.files = Dir['**/**']
13
+ s.executables = [ 'boggle_solver' ]
14
+ s.test_files = Dir["test/test*.rb"]
15
+ s.has_rdoc = false
16
+ end
@@ -0,0 +1,52 @@
1
+ module BoggleSolver
2
+
3
+ class AdjacencyMatrix
4
+
5
+ def initialize( n = 2 )
6
+ @m = Array.new(n, 0)
7
+ @m.map!{Array.new(n, 0)}
8
+ @n = n
9
+ end
10
+
11
+ def to_s
12
+ s = ""
13
+ @m.each do |row|
14
+ row.each {|e| s += e.to_s + " "}
15
+ s += "\n"
16
+ end
17
+ s.chop
18
+ end
19
+
20
+ def add_directed_edge(i, j, w=1)
21
+ @m[i][j] = w unless i == j
22
+ end
23
+
24
+ def add_edge(i, j, w=1)
25
+ add_directed_edge(i, j, w)
26
+ add_directed_edge(j, i, w)
27
+ end
28
+
29
+ def adjacent(u)
30
+ a = Array.new
31
+ @m[u].each_with_index {|v,i| a.push i unless v == 0 }
32
+ a
33
+ end
34
+
35
+ def depth_first_search
36
+ @visited = Array.new(@n, nil)
37
+ @finished = Array.new(@n, nil)
38
+ @visited.each_with_index do |visit_status, u|
39
+ depth_first_search_visit(u) if visit_status.nil?
40
+ end
41
+ end
42
+
43
+ def depth_first_search_visit(u, *)
44
+ @visited[u] = 1
45
+ puts u
46
+ adjacent(u).each { |v| depth_first_search_visit(v) if @visited[v].nil?}
47
+ @finished[u] = 1
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,124 @@
1
+ require_relative 'adjacency_matrix'
2
+ require 'thread'
3
+
4
+ module BoggleSolver
5
+
6
+ class Board < AdjacencyMatrix
7
+ attr_reader :words
8
+
9
+ def initialize
10
+ super(25)
11
+ connect
12
+ @board = []
13
+ # @prng = Random.new
14
+ @words = []
15
+ generate_board
16
+ load_dictionary
17
+ end
18
+
19
+ def connect
20
+ j=0;
21
+ while j<25 do
22
+ add_edge(j, j-6) unless j < 6 or j % 5 == 0
23
+ add_edge(j, j-5) unless j < 5
24
+ add_edge(j, j-4) unless j < 4 or j % 5 == 4
25
+ add_edge(j, j-1) unless j % 5 == 0
26
+ add_edge(j, j+1) unless j % 5 == 4
27
+ add_edge(j, j+4) unless j > 19 or j % 5 == 0
28
+ add_edge(j, j+5) unless j > 19
29
+ add_edge(j, j+6) unless j > 18 or j % 5 == 4
30
+ j += 1
31
+ end
32
+ end
33
+
34
+ def generate_board
35
+ # @board.map!{|item| (65.+rand(25)).chr}
36
+ @board = ["a", "r", "t", "a", "c", "c", "e", "a", "r", "d", "f", "e", "w", "o", "o", "n", "p", "l", "m", "i", "c", "r", "u", "b", "l"]
37
+ @board_hash = Hash.new(0)
38
+ @board.each {|letter| @board_hash[letter] += 1}
39
+ end
40
+
41
+ def load_dictionary
42
+ dictionary_file = "/usr/share/dict/words"
43
+ @dictionary = Array.new
44
+
45
+ File.foreach(dictionary_file) do |line|
46
+ next_word = line.chomp
47
+ @dictionary.push next_word if possible? next_word
48
+ end
49
+ end
50
+
51
+ def possible? word
52
+ word_hash = Hash.new(0)
53
+ result = true
54
+ word.each_char {|letter| word_hash[letter] += 1}
55
+ word_hash.each do |letter, count|
56
+ result &&= count <= @board_hash[letter]
57
+ end
58
+ result
59
+ end
60
+
61
+ def to_s
62
+ s = ""
63
+ @board.each_with_index do |letter, index|
64
+ s += letter.to_s + " "
65
+ s += "\n" if index % 5 == 4
66
+ end
67
+ s.chomp
68
+ end
69
+
70
+ def in_dictionary? s
71
+ @dictionary.index s
72
+ end
73
+
74
+ def part_in_dictionary? s
75
+ s = "\\A" + s + "..*"
76
+ regex = Regexp.new(s)
77
+ !(@dictionary.grep regex).empty?
78
+ end
79
+
80
+ def find_words
81
+ threads = []
82
+ @board.each_with_index do |letter, index|
83
+ threads << Thread.new { depth_first_search(index) }
84
+ end
85
+
86
+ threads.each do |t|
87
+ t.join
88
+ @words.concat t[:words]
89
+ end
90
+
91
+ @words.uniq!
92
+ @words.sort_by!{|item| item.length}
93
+ end
94
+
95
+ def depth_first_search(offset=0)
96
+ Thread.current[:visited] = Array.new(@n, nil)
97
+ Thread.current[:finished] = Array.new(@n, nil)
98
+ Thread.current[:words] = []
99
+ Thread.current[:visited][offset..-1].each_with_index do |visit_status, u|
100
+ u += offset
101
+ search_string = @board[u]
102
+ depth_first_search_visit(u, search_string) if Thread.current[:finished][u].nil?
103
+ end
104
+ end
105
+
106
+ def depth_first_search_visit(u, search_string)
107
+ Thread.current[:visited][u] = 1
108
+ Thread.current[:words] << search_string if in_dictionary? search_string
109
+ adjacent(u).each do |v|
110
+ if Thread.current[:visited][v].nil?
111
+ s = search_string + @board[v]
112
+ Thread.current[:words].push s if in_dictionary? s
113
+ if part_in_dictionary? s
114
+ depth_first_search_visit(v, s)
115
+ end
116
+ end
117
+ end
118
+ Thread.current[:finished][u] = 1
119
+ Thread.current[:visited][u] = nil
120
+ end
121
+
122
+ end
123
+
124
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'board'
2
+
3
+ module BoggleSolver
4
+
5
+ class Runner
6
+ def initialize(argv)
7
+ #Parse options here
8
+ end
9
+
10
+ def run
11
+ board = Board.new
12
+ board.find_words
13
+ p board.words
14
+ end
15
+
16
+ end
17
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: boggle_solver
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Casey Robinson
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-15 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: File.read(File.join(File.dirname(__FILE__), 'README'))
15
+ email: kc@rampantmonkey.com
16
+ executables:
17
+ - boggle_solver
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - boggle_solver-0.0.1.gem
22
+ - boggle_solver.gemspec
23
+ - README.markdown
24
+ - bin/boggle_solver
25
+ - lib/boggle_solver/adjacency_matrix.rb
26
+ - lib/boggle_solver/board.rb
27
+ - lib/boggle_solver/runner.rb
28
+ homepage: http://rampantmonkey.com
29
+ licenses: []
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '1.9'
39
+ none: false
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ none: false
46
+ requirements:
47
+ - ! '[ ''An installed dictionary (most Unix systems have one)'']'
48
+ rubyforge_project:
49
+ rubygems_version: 1.8.15
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: Find all words in a letter matrix
53
+ test_files: []
54
+ ...