wordgrid 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/wordgrid.rb +117 -31
- metadata +1 -1
data/lib/wordgrid.rb
CHANGED
@@ -1,45 +1,38 @@
|
|
1
1
|
require 'matrix'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
if (e == letter)
|
8
|
-
cell = [row, column]
|
9
|
-
cells.push(cell)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
return cells
|
13
|
-
end
|
14
|
-
|
15
|
-
# why doesn't matrix natively support this?!?
|
16
|
-
def neighbor_cells(root_cell)
|
17
|
-
row = root_cell[0]
|
18
|
-
column = root_cell[1]
|
19
|
-
cells = []
|
20
|
-
|
21
|
-
cells.push([row-1, column-1]) if row-1 >= 0 and column-1 >= 0 #NW
|
22
|
-
cells.push([row-1, column]) if row-1 >= 0 #N
|
23
|
-
cells.push([row-1, column+1]) if row-1 >= 0 and column+1 < column_size #NE
|
24
|
-
cells.push([row, column-1]) if column-1 >=0 #W
|
25
|
-
cells.push([row, column+1]) if column+1 < column_size #E
|
26
|
-
cells.push([row+1, column-1]) if row+1 < row_size and column-1 >= 0 #SW
|
27
|
-
cells.push([row+1, column]) if row+1 < row_size #S
|
28
|
-
cells.push([row+1, column+1]) if row+1 < row_size and column+1 < column_size #SE
|
29
|
-
|
30
|
-
return cells
|
31
|
-
end
|
32
|
-
end
|
3
|
+
=begin
|
4
|
+
Wordgrid is useful for sucking the life out of games like Boggle or
|
5
|
+
Scramble with Friends. Given a matrix of letters, it provides a method
|
6
|
+
named has_word? which looks in the matrix for a requested word.
|
33
7
|
|
8
|
+
Paired with a driver script that iterates over a dictionary and checks
|
9
|
+
every single word to see if it matches, then you are provided with the
|
10
|
+
full list of words in the game. That would make the game a miserable
|
11
|
+
bore, but it was fun to write.
|
12
|
+
=end
|
34
13
|
class Wordgrid
|
35
14
|
|
15
|
+
# Wordgrid.new can optionally be called with a Matrix object.
|
36
16
|
def initialize(initial_grid=Matrix[])
|
37
17
|
self.grid = initial_grid
|
38
18
|
@cell_stack = []
|
39
19
|
end
|
40
|
-
|
20
|
+
|
21
|
+
# Wordgrid.grid is the underlying matrix of letters.
|
41
22
|
attr_reader :grid
|
42
23
|
|
24
|
+
=begin
|
25
|
+
grid takes a Matrix object and validates that it is acceptable.
|
26
|
+
|
27
|
+
* *Args* :
|
28
|
+
- +new_grid+ -> the proposed new Matrix object.
|
29
|
+
* *Returns* :
|
30
|
+
- the Matrix object itself.
|
31
|
+
* *Raises* : +ArgumentError+ in the following circumstances:
|
32
|
+
- any of the cells consist of something other than single letters
|
33
|
+
- the grid is not a square
|
34
|
+
=end
|
35
|
+
|
43
36
|
def grid=(new_grid)
|
44
37
|
# make sure each cell is a single letter character
|
45
38
|
bad_cell = new_grid.find_index{|cell| cell.match(/^[^A-Za-z]$/) }
|
@@ -54,6 +47,24 @@ class Wordgrid
|
|
54
47
|
@grid = new_grid
|
55
48
|
end
|
56
49
|
|
50
|
+
=begin
|
51
|
+
has_word? is Wordgrid's raison d'être. It take a word and returns
|
52
|
+
true/false based on whether the word can be found in the grid.
|
53
|
+
|
54
|
+
has_word? makes sure not to re-trace its steps when looking for a word.
|
55
|
+
So, for example, it will find "BEAD" in the following grid, but it will
|
56
|
+
not find "BEADED":
|
57
|
+
A B C
|
58
|
+
D E F
|
59
|
+
G H I
|
60
|
+
|
61
|
+
* *Args* :
|
62
|
+
- +word+ -> the word to search the grid for
|
63
|
+
* *Returns* :
|
64
|
+
- +true+, if the word has been found, and +false+ otherwise
|
65
|
+
* *Raises* :
|
66
|
+
- Nothing
|
67
|
+
=end
|
57
68
|
def has_word?(word)
|
58
69
|
@letters = word.split('')
|
59
70
|
first_cells = @grid.find_cells_for_letter(@letters[0])
|
@@ -70,6 +81,10 @@ class Wordgrid
|
|
70
81
|
end
|
71
82
|
|
72
83
|
=begin
|
84
|
+
find_next_letter_in_neighborhood calls itself recursively to look for
|
85
|
+
each letter in the word. One it finds the word, it stop the recursive
|
86
|
+
chain returning true. It follows the following plan:
|
87
|
+
|
73
88
|
1. start with a cell
|
74
89
|
2. look at all the neighbors of the cell for the next letter.
|
75
90
|
if we don't find it, move back up to try a different path (if there were other matches a cell up)
|
@@ -107,4 +122,75 @@ a final stack for "BEAD" should be: [0,1], [1,1], [0,0], [1,0]
|
|
107
122
|
@cell_stack.pop
|
108
123
|
return false
|
109
124
|
end
|
125
|
+
private :find_next_letter_in_neighborhood
|
110
126
|
end
|
127
|
+
|
128
|
+
# Adding two helper functions to the standard Matrix class which are
|
129
|
+
# needed by the Wordgrid class.
|
130
|
+
class Matrix
|
131
|
+
|
132
|
+
=begin
|
133
|
+
Adds a method to the Matrix class which returns an array of cells
|
134
|
+
which match the string passed in. A cell is just an array with the
|
135
|
+
row and column.
|
136
|
+
|
137
|
+
* *Args* :
|
138
|
+
- +letter+ -> the character to search the matrix for.
|
139
|
+
* *Returns* :
|
140
|
+
- the cells which match the letter passed in. An array of two-value arrays.
|
141
|
+
* *Raises* :
|
142
|
+
- Nothing
|
143
|
+
=end
|
144
|
+
|
145
|
+
def find_cells_for_letter(letter)
|
146
|
+
cells = []
|
147
|
+
each_with_index do |e, row, column|
|
148
|
+
if (e == letter)
|
149
|
+
cell = [row, column]
|
150
|
+
cells.push(cell)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
return cells
|
154
|
+
end
|
155
|
+
|
156
|
+
=begin
|
157
|
+
Adds a method to the Matrix class which will find all the cells which
|
158
|
+
border the cell requested
|
159
|
+
|
160
|
+
Neighbors are defined by the cells northwest, north, northeast, west,
|
161
|
+
east, southwest, south, and southeast of the original cell. If the
|
162
|
+
original cell is on the edge of the matrix, it does not wrap. In
|
163
|
+
other words, for the following matrix:
|
164
|
+
A B C
|
165
|
+
D E F
|
166
|
+
G H I
|
167
|
+
the cell containing the letter E has the neighbors A,B,C,D,F,G,H,I.
|
168
|
+
However, the cell containing the letter A only has the neighbors
|
169
|
+
B,D,E.
|
170
|
+
|
171
|
+
* *Args* :
|
172
|
+
- +root_cell+ -> a two-element array (row, cell) identifying the cell whose neighbors we are seeking.
|
173
|
+
* *Returns* :
|
174
|
+
- an array of cells (each cell is a two-element array, containing row and cell) neighboring the original cell.
|
175
|
+
* *Raises* :
|
176
|
+
- Nothing
|
177
|
+
=end
|
178
|
+
|
179
|
+
def neighbor_cells(root_cell)
|
180
|
+
row = root_cell[0]
|
181
|
+
column = root_cell[1]
|
182
|
+
cells = []
|
183
|
+
|
184
|
+
cells.push([row-1, column-1]) if row-1 >= 0 and column-1 >= 0 #NW
|
185
|
+
cells.push([row-1, column]) if row-1 >= 0 #N
|
186
|
+
cells.push([row-1, column+1]) if row-1 >= 0 and column+1 < column_size #NE
|
187
|
+
cells.push([row, column-1]) if column-1 >=0 #W
|
188
|
+
cells.push([row, column+1]) if column+1 < column_size #E
|
189
|
+
cells.push([row+1, column-1]) if row+1 < row_size and column-1 >= 0 #SW
|
190
|
+
cells.push([row+1, column]) if row+1 < row_size #S
|
191
|
+
cells.push([row+1, column+1]) if row+1 < row_size and column+1 < column_size #SE
|
192
|
+
|
193
|
+
return cells
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|