@windward/games 0.0.3 → 0.0.4
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.
- package/components/content/blocks/crosswordPuzzle/Crossword.ts +475 -0
- package/components/content/blocks/crosswordPuzzle/CrosswordElements.ts +36 -0
- package/components/content/blocks/crosswordPuzzle/CrosswordPuzzle.vue +542 -0
- package/components/content/blocks/wordJumble/WordJumble.vue +22 -10
- package/components/settings/CrosswordPuzzleSettingsManager.vue +274 -0
- package/components/settings/WordJumbleSettingsManager.vue +4 -1
- package/i18n/en-US/components/content/blocks/crossword.ts +7 -0
- package/i18n/en-US/components/content/blocks/index.ts +2 -0
- package/i18n/en-US/components/settings/crossword.ts +6 -0
- package/i18n/en-US/components/settings/index.ts +2 -0
- package/i18n/en-US/components/settings/word_jumble.ts +1 -1
- package/i18n/en-US/shared/content_blocks.ts +1 -0
- package/i18n/en-US/shared/settings.ts +1 -0
- package/package.json +1 -1
- package/plugin.js +22 -1
- package/test/blocks/crossword/CrosswordPuzzle.spec.js +49 -0
- package/test/settings/BucketGameManager.spec.js +1 -1
- package/test/settings/CrosswordPuzzleManager.spec.js +103 -0
- package/test/settings/WordJumbleManager.spec.js +2 -2
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CrosswordCell,
|
|
3
|
+
CrosswordCellNode,
|
|
4
|
+
WordElement,
|
|
5
|
+
} from './CrosswordElements'
|
|
6
|
+
class Crossword {
|
|
7
|
+
public GRID_COLS: any = 50
|
|
8
|
+
public GRID_ROWS: any = 50
|
|
9
|
+
// This is an index of the positions of the char in the crossword (so we know where we can potentially place words)
|
|
10
|
+
// example {"a" : [{'row' : 10, 'col' : 5}, {'row' : 62, 'col' :17}], {'row' : 54, 'col' : 12}], "b" : [{'row' : 3, 'col' : 13}]}
|
|
11
|
+
// where the two item arrays are the row and column of where the letter occurs
|
|
12
|
+
public char_index!: {}
|
|
13
|
+
// these words are the words that can't be placed on the crossword
|
|
14
|
+
public bad_words!: any
|
|
15
|
+
public words_in!: any
|
|
16
|
+
public clues_in!: any
|
|
17
|
+
public grid: any = []
|
|
18
|
+
public word_elements: any = []
|
|
19
|
+
constructor(words_in, clues_in) {
|
|
20
|
+
this.words_in = words_in
|
|
21
|
+
this.clues_in = clues_in
|
|
22
|
+
// constructor
|
|
23
|
+
if (this.words_in.length < 2)
|
|
24
|
+
throw 'A crossword must have at least 2 words'
|
|
25
|
+
if (this.words_in.length != clues_in.length)
|
|
26
|
+
throw 'The number of words must equal the number of clues'
|
|
27
|
+
// build the grid;
|
|
28
|
+
this.grid = new Array(this.GRID_ROWS)
|
|
29
|
+
for (var i = 0; i < this.GRID_ROWS; i++) {
|
|
30
|
+
this.grid[i] = new Array(this.GRID_COLS)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// build the element list (need to keep track of indexes in the originial input arrays)
|
|
34
|
+
for (let i = 0; i < words_in.length; i++) {
|
|
35
|
+
this.word_elements.push(new WordElement(words_in[i], i))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// I got this sorting idea from http://stackoverflow.com/questions/943113/algorithm-to-generate-a-crossword/1021800#1021800
|
|
39
|
+
// seems to work well
|
|
40
|
+
this.word_elements.sort(function (a, b) {
|
|
41
|
+
return b.word.length - a.word.length
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// returns the crossword grid that has the ratio closest to 1 or null if it can't build one
|
|
46
|
+
public getSquareGrid(max_tries) {
|
|
47
|
+
let best_grid: any = null
|
|
48
|
+
var best_ratio = 0
|
|
49
|
+
for (var i = 0; i < max_tries; i++) {
|
|
50
|
+
var a_grid = this.getGrid(1)
|
|
51
|
+
if (a_grid == null) {
|
|
52
|
+
continue
|
|
53
|
+
}
|
|
54
|
+
var ratio =
|
|
55
|
+
(Math.min(a_grid.length, a_grid[0].length) * 1.0) /
|
|
56
|
+
Math.max(a_grid.length, a_grid[0].length)
|
|
57
|
+
if (ratio > best_ratio) {
|
|
58
|
+
best_grid = a_grid
|
|
59
|
+
best_ratio = ratio
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (best_ratio == 1) break
|
|
63
|
+
}
|
|
64
|
+
return best_grid
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// returns an abitrary grid, or null if it can't build one
|
|
68
|
+
public getGrid(max_tries) {
|
|
69
|
+
for (var tries = 0; tries < max_tries; tries++) {
|
|
70
|
+
this.clear() // always start with a fresh grid and char_index
|
|
71
|
+
// place the first word in the middle of the grid
|
|
72
|
+
var start_dir = this.randomDirection()
|
|
73
|
+
var r = Math.floor(this.grid.length / 2)
|
|
74
|
+
var c = Math.floor(this.grid[0].length / 2)
|
|
75
|
+
|
|
76
|
+
var word_element = this.word_elements[0]
|
|
77
|
+
if (start_dir == 'across') {
|
|
78
|
+
c -= Math.floor(word_element.word.length / 2)
|
|
79
|
+
} else {
|
|
80
|
+
r -= Math.floor(word_element.word.length / 2)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
this.canPlaceWordAt(word_element.word, r, c, start_dir) !==
|
|
85
|
+
false
|
|
86
|
+
) {
|
|
87
|
+
this.placeWordAt(
|
|
88
|
+
word_element.word,
|
|
89
|
+
word_element.index,
|
|
90
|
+
r,
|
|
91
|
+
c,
|
|
92
|
+
start_dir
|
|
93
|
+
)
|
|
94
|
+
} else {
|
|
95
|
+
this.bad_words = [word_element]
|
|
96
|
+
return null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// start with a group containing all the words (except the first)
|
|
100
|
+
// as we go, we try to place each word in the group onto the grid
|
|
101
|
+
// if the word can't go on the grid, we add that word to the next group
|
|
102
|
+
var groups: any = []
|
|
103
|
+
let word_has_been_added_to_grid
|
|
104
|
+
groups.push(this.word_elements.slice(1))
|
|
105
|
+
for (var g = 0; g < groups.length; g++) {
|
|
106
|
+
word_has_been_added_to_grid = false
|
|
107
|
+
// try to add all the words in this group to the grid
|
|
108
|
+
for (var i = 0; i < groups[g].length; i++) {
|
|
109
|
+
word_element = groups[g][i]
|
|
110
|
+
var best_position = this.findPositionForWord(
|
|
111
|
+
word_element.word
|
|
112
|
+
)
|
|
113
|
+
if (!best_position) {
|
|
114
|
+
// make the new group (if needed)
|
|
115
|
+
if (groups.length - 1 == g) groups.push([])
|
|
116
|
+
// place the word in the next group
|
|
117
|
+
groups[g + 1].push(word_element)
|
|
118
|
+
} else {
|
|
119
|
+
;(r = best_position['row']), (c = best_position['col'])
|
|
120
|
+
var dir = best_position['direction']
|
|
121
|
+
this.placeWordAt(
|
|
122
|
+
word_element.word,
|
|
123
|
+
word_element.index,
|
|
124
|
+
r,
|
|
125
|
+
c,
|
|
126
|
+
dir
|
|
127
|
+
)
|
|
128
|
+
word_has_been_added_to_grid = true
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// if we haven't made any progress, there is no point in going on to the next group
|
|
132
|
+
if (!word_has_been_added_to_grid) break
|
|
133
|
+
}
|
|
134
|
+
// no need to try again
|
|
135
|
+
if (word_has_been_added_to_grid) return this.minimizeGrid()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.bad_words = groups[groups.length - 1]
|
|
139
|
+
return null
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// returns the list of WordElements that can't fit on the crossword
|
|
143
|
+
public getBadWords() {
|
|
144
|
+
return this.bad_words
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// get two arrays ("across" and "down") that contain objects describing the
|
|
148
|
+
// topological position of the word (e.g. 1 is the first word starting from
|
|
149
|
+
// the top left, going to the bottom right), the index of the word (in the
|
|
150
|
+
// original input list), the clue, and the word itself
|
|
151
|
+
public getLegend(grid) {
|
|
152
|
+
var groups = { across: [], down: [] }
|
|
153
|
+
var position = 1
|
|
154
|
+
for (var r = 0; r < grid.length; r++) {
|
|
155
|
+
for (var c = 0; c < grid[r].length; c++) {
|
|
156
|
+
var cell = grid[r][c]
|
|
157
|
+
var increment_position = false
|
|
158
|
+
// check across and down
|
|
159
|
+
for (var k in groups) {
|
|
160
|
+
// does a word start here? (make sure the cell isn't null, first)
|
|
161
|
+
if (cell && cell[k] && cell[k]['is_start_of_word']) {
|
|
162
|
+
var index = cell[k]['index']
|
|
163
|
+
groups[k].push({
|
|
164
|
+
position: position,
|
|
165
|
+
index: index,
|
|
166
|
+
clue: this.clues_in[index],
|
|
167
|
+
word: this.words_in[index],
|
|
168
|
+
})
|
|
169
|
+
increment_position = true
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (increment_position) position++
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return groups
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// move the grid onto the smallest grid that will fit it
|
|
180
|
+
public minimizeGrid() {
|
|
181
|
+
// find bounds
|
|
182
|
+
var r_min = this.GRID_ROWS - 1,
|
|
183
|
+
r_max = 0,
|
|
184
|
+
c_min = this.GRID_COLS - 1,
|
|
185
|
+
c_max = 0
|
|
186
|
+
for (var r = 0; r < this.GRID_ROWS; r++) {
|
|
187
|
+
for (var c = 0; c < this.GRID_COLS; c++) {
|
|
188
|
+
var cell = this.grid[r][c]
|
|
189
|
+
if (cell != null) {
|
|
190
|
+
if (r < r_min) r_min = r
|
|
191
|
+
if (r > r_max) r_max = r
|
|
192
|
+
if (c < c_min) c_min = c
|
|
193
|
+
if (c > c_max) c_max = c
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// initialize new grid
|
|
198
|
+
var rows = r_max - r_min + 1
|
|
199
|
+
var cols = c_max - c_min + 1
|
|
200
|
+
var new_grid = new Array(rows)
|
|
201
|
+
for (let r = 0; r < rows; r++) {
|
|
202
|
+
for (let c = 0; c < cols; c++) {
|
|
203
|
+
new_grid[r] = new Array(cols)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// copy the grid onto the smaller grid
|
|
208
|
+
for (let r = r_min, r2 = 0; r2 < rows; r++, r2++) {
|
|
209
|
+
for (let c = c_min, c2 = 0; c2 < cols; c++, c2++) {
|
|
210
|
+
new_grid[r2][c2] = this.grid[r][c]
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return new_grid
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// helper for placeWordAt();
|
|
218
|
+
public addCellToGrid(
|
|
219
|
+
word,
|
|
220
|
+
index_of_word_in_input_list,
|
|
221
|
+
index_of_char,
|
|
222
|
+
r,
|
|
223
|
+
c,
|
|
224
|
+
direction
|
|
225
|
+
) {
|
|
226
|
+
var char = word.charAt(index_of_char)
|
|
227
|
+
if (this.grid[r][c] == null) {
|
|
228
|
+
this.grid[r][c] = new CrosswordCell(char)
|
|
229
|
+
|
|
230
|
+
// init the char_index for that character if needed
|
|
231
|
+
if (!this.char_index[char]) this.char_index[char] = []
|
|
232
|
+
|
|
233
|
+
// add to index
|
|
234
|
+
this.char_index[char].push({ row: r, col: c })
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
var is_start_of_word = index_of_char == 0
|
|
238
|
+
this.grid[r][c][direction] = new CrosswordCellNode(
|
|
239
|
+
is_start_of_word,
|
|
240
|
+
index_of_word_in_input_list
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// place the word at the row and col indicated (the first char goes there)
|
|
245
|
+
// the next chars go to the right (across) or below (down), depending on the direction
|
|
246
|
+
public placeWordAt(word, index_of_word_in_input_list, row, col, direction) {
|
|
247
|
+
if (direction == 'across') {
|
|
248
|
+
for (let c = col, i = 0; c < col + word.length; c++, i++) {
|
|
249
|
+
this.addCellToGrid(
|
|
250
|
+
word,
|
|
251
|
+
index_of_word_in_input_list,
|
|
252
|
+
i,
|
|
253
|
+
row,
|
|
254
|
+
c,
|
|
255
|
+
direction
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
} else if (direction == 'down') {
|
|
259
|
+
for (let r = row, i = 0; r < row + word.length; r++, i++) {
|
|
260
|
+
this.addCellToGrid(
|
|
261
|
+
word,
|
|
262
|
+
index_of_word_in_input_list,
|
|
263
|
+
i,
|
|
264
|
+
r,
|
|
265
|
+
col,
|
|
266
|
+
direction
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
throw 'Invalid Direction'
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// you can only place a char where the space is blank, or when the same
|
|
275
|
+
// character exists there already
|
|
276
|
+
// returns false, if you can't place the char
|
|
277
|
+
// 0 if you can place the char, but there is no intersection
|
|
278
|
+
// 1 if you can place the char, and there is an intersection
|
|
279
|
+
public canPlaceCharAt(char, row, col) {
|
|
280
|
+
// no intersection
|
|
281
|
+
if (this.grid[row][col] == null) return 0
|
|
282
|
+
// intersection!
|
|
283
|
+
if (this.grid[row][col]['char'] == char) return 1
|
|
284
|
+
|
|
285
|
+
return false
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// determines if you can place a word at the row, column in the direction
|
|
289
|
+
public canPlaceWordAt(word, row, col, direction): any {
|
|
290
|
+
// out of bounds
|
|
291
|
+
if (
|
|
292
|
+
row < 0 ||
|
|
293
|
+
row >= this.grid.length ||
|
|
294
|
+
col < 0 ||
|
|
295
|
+
col >= this.grid[row].length
|
|
296
|
+
)
|
|
297
|
+
return false
|
|
298
|
+
|
|
299
|
+
var intersections
|
|
300
|
+
|
|
301
|
+
if (direction == 'across') {
|
|
302
|
+
// out of bounds (word too long)
|
|
303
|
+
if (col + word.length > this.grid[row].length) return false
|
|
304
|
+
// can't have a word directly to the left
|
|
305
|
+
if (col - 1 >= 0 && this.grid[row][col - 1] != null) return false
|
|
306
|
+
// can't have word directly to the right
|
|
307
|
+
if (
|
|
308
|
+
col + word.length < this.grid[row].length &&
|
|
309
|
+
this.grid[row][col + word.length] != null
|
|
310
|
+
)
|
|
311
|
+
return false
|
|
312
|
+
|
|
313
|
+
// check the row above to make sure there isn't another word
|
|
314
|
+
// running parallel. It is ok if there is a character above, only if
|
|
315
|
+
// the character below it intersects with the current word
|
|
316
|
+
for (
|
|
317
|
+
let r = row - 1, c = col, i = 0;
|
|
318
|
+
r >= 0 && c < col + word.length;
|
|
319
|
+
c++, i++
|
|
320
|
+
) {
|
|
321
|
+
let is_empty = this.grid[r][c] == null
|
|
322
|
+
let is_intersection =
|
|
323
|
+
this.grid[row][c] != null &&
|
|
324
|
+
this.grid[row][c]['char'] == word.charAt(i)
|
|
325
|
+
let can_place_here = is_empty || is_intersection
|
|
326
|
+
if (!can_place_here) return false
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// same deal as above, we just search in the row below the word
|
|
330
|
+
for (
|
|
331
|
+
let r = row + 1, c = col, i = 0;
|
|
332
|
+
r < this.grid.length && c < col + word.length;
|
|
333
|
+
c++, i++
|
|
334
|
+
) {
|
|
335
|
+
let is_empty = this.grid[r][c] == null
|
|
336
|
+
let is_intersection =
|
|
337
|
+
this.grid[row][c] != null &&
|
|
338
|
+
this.grid[row][c]['char'] == word.charAt(i)
|
|
339
|
+
let can_place_here = is_empty || is_intersection
|
|
340
|
+
if (!can_place_here) return false
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// check to make sure we aren't overlapping a char (that doesn't match)
|
|
344
|
+
// and get the count of intersections
|
|
345
|
+
intersections = 0
|
|
346
|
+
for (let c = col, i = 0; c < col + word.length; c++, i++) {
|
|
347
|
+
let result = this.canPlaceCharAt(word.charAt(i), row, c)
|
|
348
|
+
if (result === false) return false
|
|
349
|
+
intersections += result
|
|
350
|
+
}
|
|
351
|
+
} else if (direction == 'down') {
|
|
352
|
+
// out of bounds
|
|
353
|
+
if (row + word.length > this.grid.length) return false
|
|
354
|
+
// can't have a word directly above
|
|
355
|
+
if (row - 1 >= 0 && this.grid[row - 1][col] != null) return false
|
|
356
|
+
// can't have a word directly below
|
|
357
|
+
if (
|
|
358
|
+
row + word.length < this.grid.length &&
|
|
359
|
+
this.grid[row + word.length][col] != null
|
|
360
|
+
)
|
|
361
|
+
return false
|
|
362
|
+
|
|
363
|
+
// check the column to the left to make sure there isn't another
|
|
364
|
+
// word running parallel. It is ok if there is a character to the
|
|
365
|
+
// left, only if the character to the right intersects with the
|
|
366
|
+
// current word
|
|
367
|
+
for (
|
|
368
|
+
let c = col - 1, r = row, i = 0;
|
|
369
|
+
c >= 0 && r < row + word.length;
|
|
370
|
+
r++, i++
|
|
371
|
+
) {
|
|
372
|
+
let is_empty = this.grid[r][c] == null
|
|
373
|
+
let is_intersection =
|
|
374
|
+
this.grid[r][col] != null &&
|
|
375
|
+
this.grid[r][col]['char'] == word.charAt(i)
|
|
376
|
+
let can_place_here = is_empty || is_intersection
|
|
377
|
+
if (!can_place_here) return false
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// same deal, but look at the column to the right
|
|
381
|
+
for (
|
|
382
|
+
let c = col + 1, r = row, i = 0;
|
|
383
|
+
r < row + word.length && c < this.grid[r].length;
|
|
384
|
+
r++, i++
|
|
385
|
+
) {
|
|
386
|
+
let is_empty = this.grid[r][c] == null
|
|
387
|
+
let is_intersection =
|
|
388
|
+
this.grid[r][col] != null &&
|
|
389
|
+
this.grid[r][col]['char'] == word.charAt(i)
|
|
390
|
+
let can_place_here = is_empty || is_intersection
|
|
391
|
+
if (!can_place_here) return false
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// check to make sure we aren't overlapping a char (that doesn't match)
|
|
395
|
+
// and get the count of intersections
|
|
396
|
+
intersections = 0
|
|
397
|
+
for (let r = row, i = 0; r < row + word.length; r++, i++) {
|
|
398
|
+
let result = this.canPlaceCharAt(word.charAt(i, 1), r, col)
|
|
399
|
+
if (result === false) return false
|
|
400
|
+
intersections += result
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
throw 'Invalid Direction'
|
|
404
|
+
}
|
|
405
|
+
return intersections
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
public randomDirection() {
|
|
409
|
+
return Math.floor(Math.random() * 2) ? 'across' : 'down'
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
public findPositionForWord(word) {
|
|
413
|
+
// check the char_index for every letter, and see if we can put it there in a direction
|
|
414
|
+
let bests: object[] = []
|
|
415
|
+
for (var i = 0; i < word.length; i++) {
|
|
416
|
+
var possible_locations_on_grid = this.char_index[word.charAt(i)]
|
|
417
|
+
if (!possible_locations_on_grid) continue
|
|
418
|
+
for (var j = 0; j < possible_locations_on_grid.length; j++) {
|
|
419
|
+
var point = possible_locations_on_grid[j]
|
|
420
|
+
var r = point['row']
|
|
421
|
+
var c = point['col']
|
|
422
|
+
// the c - i, and r - i here compensate for the offset of character in the word
|
|
423
|
+
var intersections_across = this.canPlaceWordAt(
|
|
424
|
+
word,
|
|
425
|
+
r,
|
|
426
|
+
c - i,
|
|
427
|
+
'across'
|
|
428
|
+
)
|
|
429
|
+
var intersections_down = this.canPlaceWordAt(
|
|
430
|
+
word,
|
|
431
|
+
r - i,
|
|
432
|
+
c,
|
|
433
|
+
'down'
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
if (intersections_across !== false) {
|
|
437
|
+
var objectAcross = {
|
|
438
|
+
intersections: intersections_across,
|
|
439
|
+
row: r,
|
|
440
|
+
col: c - i,
|
|
441
|
+
direction: 'across',
|
|
442
|
+
}
|
|
443
|
+
bests.push(objectAcross)
|
|
444
|
+
}
|
|
445
|
+
if (intersections_down !== false) {
|
|
446
|
+
var objectDown = {
|
|
447
|
+
intersections: intersections_down,
|
|
448
|
+
row: r - i,
|
|
449
|
+
col: c,
|
|
450
|
+
direction: 'down',
|
|
451
|
+
}
|
|
452
|
+
bests.push(objectDown)
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (bests.length == 0) return false
|
|
458
|
+
|
|
459
|
+
// find a good random position
|
|
460
|
+
var best = bests[Math.floor(Math.random() * bests.length)]
|
|
461
|
+
|
|
462
|
+
return best
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
public clear() {
|
|
466
|
+
for (var r = 0; r < this.grid.length; r++) {
|
|
467
|
+
for (var c = 0; c < this.grid[r].length; c++) {
|
|
468
|
+
this.grid[r][c] = null
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
this.char_index = {}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
export { Crossword }
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class CrosswordCell {
|
|
2
|
+
public char!: any
|
|
3
|
+
public across!: any
|
|
4
|
+
public down!: any
|
|
5
|
+
constructor(letter) {
|
|
6
|
+
this.char = letter // the actual letter for the cell on the crossword
|
|
7
|
+
// If a word hits this cell going in the "across" direction, this will be a CrosswordCellNode
|
|
8
|
+
this.across = null
|
|
9
|
+
// If a word hits this cell going in the "down" direction, this will be a CrosswordCellNode
|
|
10
|
+
this.down = null
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// You can tell if the Node is the start of a word (which is needed if you want to number the cells)
|
|
15
|
+
// and what word and clue it corresponds to (using the index)
|
|
16
|
+
class CrosswordCellNode {
|
|
17
|
+
public is_start_of_word!: any
|
|
18
|
+
public index!: any
|
|
19
|
+
constructor(is_start_of_word, index) {
|
|
20
|
+
this.is_start_of_word = is_start_of_word
|
|
21
|
+
this.index = index // use to map this node to its word or clue
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// public functionName
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class WordElement {
|
|
28
|
+
public word!: any
|
|
29
|
+
public index!: any
|
|
30
|
+
constructor(word, index) {
|
|
31
|
+
this.word = word // the actual word
|
|
32
|
+
this.index = index // use to map this node to its word or clue
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { CrosswordCell, CrosswordCellNode, WordElement }
|