boggle_solver 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ ...