@windward/games 0.0.4 → 0.0.6

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.
Files changed (29) hide show
  1. package/components/content/DatableEditor.vue +0 -3
  2. package/components/content/blocks/crosswordPuzzle/Crossword.ts +231 -153
  3. package/components/content/blocks/crosswordPuzzle/CrosswordClues.vue +91 -0
  4. package/components/content/blocks/crosswordPuzzle/CrosswordElements.ts +8 -6
  5. package/components/content/blocks/crosswordPuzzle/CrosswordPuzzle.vue +502 -371
  6. package/components/content/blocks/matchingGame/MatchingGame.vue +12 -1
  7. package/components/content/blocks/multipleChoice/MultipleChoice.vue +187 -127
  8. package/components/content/blocks/multipleChoice/QuestionDialog.vue +37 -13
  9. package/components/content/blocks/sevenStrikes/SevenStikes.vue +368 -0
  10. package/components/content/blocks/sevenStrikes/keyboard.vue +71 -0
  11. package/components/content/blocks/wordJumble/Jumble.vue +2 -10
  12. package/components/settings/CrosswordPuzzleSettingsManager.vue +12 -15
  13. package/components/settings/MatchingGameManager.vue +1 -1
  14. package/components/settings/MultipleChoiceSettingsManager.vue +21 -7
  15. package/components/settings/SevenStrikesSettingsManager.vue +288 -0
  16. package/i18n/en-US/components/content/blocks/crossword.ts +18 -3
  17. package/i18n/en-US/components/content/blocks/index.ts +2 -0
  18. package/i18n/en-US/components/content/blocks/multiple_choice.ts +2 -4
  19. package/i18n/en-US/components/content/blocks/seven_strikes.ts +6 -0
  20. package/i18n/en-US/components/settings/crossword.ts +2 -1
  21. package/i18n/en-US/components/settings/index.ts +2 -0
  22. package/i18n/en-US/components/settings/multiple_choice.ts +1 -1
  23. package/i18n/en-US/components/settings/seven_strikes.ts +8 -0
  24. package/i18n/en-US/shared/content_blocks.ts +1 -0
  25. package/i18n/en-US/shared/settings.ts +1 -0
  26. package/package.json +2 -1
  27. package/plugin.js +21 -0
  28. package/test/blocks/sevenStrikes/sevenStrikes.spec.js +24 -0
  29. package/test/settings/SevenStrikesManager.spec.js +53 -0
@@ -170,8 +170,6 @@ export default {
170
170
  const item = evt.draggedContext.element
171
171
  const itemIdx = evt.draggedContext.futureIndex
172
172
 
173
- console.log('onMoveCallback', evt)
174
-
175
173
  if (item.locked) {
176
174
  return false
177
175
  }
@@ -179,7 +177,6 @@ export default {
179
177
  return true
180
178
  },
181
179
  onDropCallback(evt, originalEvent) {
182
- console.log('onDropCallback')
183
180
  this.$emit('input', this.items)
184
181
  },
185
182
  editItem(item) {
@@ -9,163 +9,172 @@ class Crossword {
9
9
  // This is an index of the positions of the char in the crossword (so we know where we can potentially place words)
10
10
  // example {"a" : [{'row' : 10, 'col' : 5}, {'row' : 62, 'col' :17}], {'row' : 54, 'col' : 12}], "b" : [{'row' : 3, 'col' : 13}]}
11
11
  // where the two item arrays are the row and column of where the letter occurs
12
- public char_index!: {}
12
+ public charIndex: any = {}
13
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
14
+ public badWords!: any
15
+ public wordsIn!: any
16
+ public cluesIn!: any
17
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
18
+ public wordElements: any = []
19
+
20
+ constructor(wordsIn: any, cluesIn: any) {
21
+ this.wordsIn = wordsIn
22
+ this.cluesIn = cluesIn
22
23
  // constructor
23
- if (this.words_in.length < 2)
24
+ if (this.wordsIn.length < 2)
24
25
  throw 'A crossword must have at least 2 words'
25
- if (this.words_in.length != clues_in.length)
26
+ if (this.wordsIn.length !== cluesIn.length)
26
27
  throw 'The number of words must equal the number of clues'
27
28
  // build the grid;
28
29
  this.grid = new Array(this.GRID_ROWS)
29
- for (var i = 0; i < this.GRID_ROWS; i++) {
30
+ for (let i = 0; i < this.GRID_ROWS; i++) {
30
31
  this.grid[i] = new Array(this.GRID_COLS)
31
32
  }
32
33
 
33
34
  // 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))
35
+ for (let i = 0; i < wordsIn.length; i++) {
36
+ this.wordElements.push(new WordElement(wordsIn[i], i))
36
37
  }
37
38
 
38
39
  // I got this sorting idea from http://stackoverflow.com/questions/943113/algorithm-to-generate-a-crossword/1021800#1021800
39
40
  // seems to work well
40
- this.word_elements.sort(function (a, b) {
41
+ this.wordElements.sort(function (a: any, b: any) {
41
42
  return b.word.length - a.word.length
42
43
  })
43
44
  }
44
45
 
45
46
  // 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) {
47
+ public getSquareGrid(maxTries: number) {
48
+ let bestGrid: any = null
49
+ let bestRatio = 0
50
+ for (let i = 0; i < maxTries; i++) {
51
+ let aGrid: any = this.getGrid(1)
52
+ if (aGrid === null) {
52
53
  continue
53
54
  }
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
55
+ let ratio =
56
+ (Math.min(aGrid.length, aGrid[0].length) * 1.0) /
57
+ Math.max(aGrid.length, aGrid[0].length)
58
+ if (ratio > bestRatio) {
59
+ bestGrid = aGrid
60
+ bestRatio = ratio
60
61
  }
61
62
 
62
- if (best_ratio == 1) break
63
+ if (bestRatio === 1) break
63
64
  }
64
- return best_grid
65
+ return bestGrid
65
66
  }
66
67
 
67
68
  // 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
69
+ public getGrid(maxTries: number) {
70
+ const groups: any = []
71
+ let wordHasBeenAddedToGrid: boolean = false
72
+
73
+ for (let tries = 0; tries < maxTries; tries++) {
74
+ this.clear() // always start with a fresh grid and charIndex
71
75
  // 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)
76
+ let start_dir = this.randomDirection()
77
+ let r = Math.floor(this.grid.length / 2)
78
+ let c = Math.floor(this.grid[0].length / 2)
75
79
 
76
- var word_element = this.word_elements[0]
77
- if (start_dir == 'across') {
78
- c -= Math.floor(word_element.word.length / 2)
80
+ let wordElement = this.wordElements[0]
81
+ if (start_dir === 'across') {
82
+ c -= Math.floor(wordElement.word.length / 2)
79
83
  } else {
80
- r -= Math.floor(word_element.word.length / 2)
84
+ r -= Math.floor(wordElement.word.length / 2)
81
85
  }
82
86
 
83
87
  if (
84
- this.canPlaceWordAt(word_element.word, r, c, start_dir) !==
85
- false
88
+ this.canPlaceWordAt(wordElement.word, r, c, start_dir) !== false
86
89
  ) {
87
90
  this.placeWordAt(
88
- word_element.word,
89
- word_element.index,
91
+ wordElement.word,
92
+ wordElement.index,
90
93
  r,
91
94
  c,
92
95
  start_dir
93
96
  )
94
97
  } else {
95
- this.bad_words = [word_element]
98
+ this.badWords = [wordElement]
96
99
  return null
97
100
  }
98
101
 
99
102
  // start with a group containing all the words (except the first)
100
103
  // as we go, we try to place each word in the group onto the grid
101
104
  // 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
105
+
106
+ groups.push(this.wordElements.slice(1))
107
+ for (let g = 0; g < groups.length; g++) {
108
+ wordHasBeenAddedToGrid = false
107
109
  // 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
110
+ for (let i = 0; i < groups[g].length; i++) {
111
+ wordElement = groups[g][i]
112
+ const bestPosition: any = this.findPositionForWord(
113
+ wordElement.word
112
114
  )
113
- if (!best_position) {
115
+ if (!bestPosition) {
114
116
  // make the new group (if needed)
115
- if (groups.length - 1 == g) groups.push([])
117
+ if (groups.length - 1 === g) groups.push([])
116
118
  // place the word in the next group
117
- groups[g + 1].push(word_element)
119
+ groups[g + 1].push(wordElement)
118
120
  } else {
119
- ;(r = best_position['row']), (c = best_position['col'])
120
- var dir = best_position['direction']
121
+ r = bestPosition['row']
122
+ c = bestPosition['col']
123
+
121
124
  this.placeWordAt(
122
- word_element.word,
123
- word_element.index,
125
+ wordElement.word,
126
+ wordElement.index,
124
127
  r,
125
128
  c,
126
- dir
129
+ bestPosition['direction']
127
130
  )
128
- word_has_been_added_to_grid = true
131
+ wordHasBeenAddedToGrid = true
129
132
  }
130
133
  }
131
134
  // 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
135
+ if (!wordHasBeenAddedToGrid) break
133
136
  }
134
137
  // no need to try again
135
- if (word_has_been_added_to_grid) return this.minimizeGrid()
138
+ if (wordHasBeenAddedToGrid) return this.minimizeGrid()
136
139
  }
137
140
 
138
- this.bad_words = groups[groups.length - 1]
141
+ this.badWords = groups[groups.length - 1]
139
142
  return null
140
143
  }
141
144
 
142
145
  // returns the list of WordElements that can't fit on the crossword
143
146
  public getBadWords() {
144
- return this.bad_words
147
+ return this.badWords
145
148
  }
146
149
 
147
150
  // get two arrays ("across" and "down") that contain objects describing the
148
151
  // topological position of the word (e.g. 1 is the first word starting from
149
152
  // the top left, going to the bottom right), the index of the word (in the
150
153
  // 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
154
+ public getLegend(grid: any) {
155
+ let groups: any = { across: [], down: [] }
156
+ let position = 1
157
+ for (let row = 0; row < grid.length; row++) {
158
+ for (let col = 0; col < grid[row].length; col++) {
159
+ let cell = grid[row][col]
160
+ let increment_position = false
158
161
  // check across and down
159
- for (var k in groups) {
162
+ for (let direction in groups) {
160
163
  // 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
+ if (
165
+ cell &&
166
+ cell[direction] &&
167
+ cell[direction].isStartOfWord
168
+ ) {
169
+ const index = cell[direction].index
170
+ groups[direction].push({
164
171
  position: position,
165
172
  index: index,
166
- clue: this.clues_in[index],
167
- word: this.words_in[index],
173
+ clue: this.cluesIn[index],
174
+ word: this.wordsIn[index],
168
175
  })
176
+ // Set the cell position now that we've generated a legend
177
+ cell[direction].position = position
169
178
  increment_position = true
170
179
  }
171
180
  }
@@ -179,14 +188,15 @@ class Crossword {
179
188
  // move the grid onto the smallest grid that will fit it
180
189
  public minimizeGrid() {
181
190
  // 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) {
191
+ let r_min = this.GRID_ROWS - 1
192
+ let r_max = 0
193
+ let c_min = this.GRID_COLS - 1
194
+ let c_max = 0
195
+
196
+ for (let r = 0; r < this.GRID_ROWS; r++) {
197
+ for (let c = 0; c < this.GRID_COLS; c++) {
198
+ let cell = this.grid[r][c]
199
+ if (cell !== null) {
190
200
  if (r < r_min) r_min = r
191
201
  if (r > r_max) r_max = r
192
202
  if (c < c_min) c_min = c
@@ -195,9 +205,9 @@ class Crossword {
195
205
  }
196
206
  }
197
207
  // 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)
208
+ let rows = r_max - r_min + 1
209
+ let cols = c_max - c_min + 1
210
+ let new_grid = new Array(rows)
201
211
  for (let r = 0; r < rows; r++) {
202
212
  for (let c = 0; c < cols; c++) {
203
213
  new_grid[r] = new Array(cols)
@@ -216,50 +226,55 @@ class Crossword {
216
226
 
217
227
  // helper for placeWordAt();
218
228
  public addCellToGrid(
219
- word,
220
- index_of_word_in_input_list,
221
- index_of_char,
222
- r,
223
- c,
224
- direction
229
+ word: any,
230
+ indexOfWordInInputList: any,
231
+ indexOfChar: any,
232
+ row: any,
233
+ col: any,
234
+ direction: any
225
235
  ) {
226
- var char = word.charAt(index_of_char)
227
- if (this.grid[r][c] == null) {
228
- this.grid[r][c] = new CrosswordCell(char)
236
+ const char = word.charAt(indexOfChar)
237
+ if (this.grid[row][col] === null) {
238
+ this.grid[row][col] = new CrosswordCell(char)
229
239
 
230
- // init the char_index for that character if needed
231
- if (!this.char_index[char]) this.char_index[char] = []
240
+ // init the charIndex for that character if needed
241
+ if (!this.charIndex[char]) this.charIndex[char] = []
232
242
 
233
243
  // add to index
234
- this.char_index[char].push({ row: r, col: c })
244
+ this.charIndex[char].push({ row, col })
235
245
  }
236
246
 
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
247
+ this.grid[row][col][direction] = new CrosswordCellNode(
248
+ indexOfChar === 0,
249
+ indexOfWordInInputList
241
250
  )
242
251
  }
243
252
 
244
253
  // place the word at the row and col indicated (the first char goes there)
245
254
  // 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') {
255
+ public placeWordAt(
256
+ word: any,
257
+ indexOfWordInInputList: any,
258
+ row: any,
259
+ col: any,
260
+ direction: any
261
+ ) {
262
+ if (direction === 'across') {
248
263
  for (let c = col, i = 0; c < col + word.length; c++, i++) {
249
264
  this.addCellToGrid(
250
265
  word,
251
- index_of_word_in_input_list,
266
+ indexOfWordInInputList,
252
267
  i,
253
268
  row,
254
269
  c,
255
270
  direction
256
271
  )
257
272
  }
258
- } else if (direction == 'down') {
273
+ } else if (direction === 'down') {
259
274
  for (let r = row, i = 0; r < row + word.length; r++, i++) {
260
275
  this.addCellToGrid(
261
276
  word,
262
- index_of_word_in_input_list,
277
+ indexOfWordInInputList,
263
278
  i,
264
279
  r,
265
280
  col,
@@ -276,17 +291,17 @@ class Crossword {
276
291
  // returns false, if you can't place the char
277
292
  // 0 if you can place the char, but there is no intersection
278
293
  // 1 if you can place the char, and there is an intersection
279
- public canPlaceCharAt(char, row, col) {
294
+ public canPlaceCharAt(char: any, row: any, col: any) {
280
295
  // no intersection
281
- if (this.grid[row][col] == null) return 0
296
+ if (this.grid[row][col] === null) return 0
282
297
  // intersection!
283
- if (this.grid[row][col]['char'] == char) return 1
298
+ if (this.grid[row][col]['char'] === char) return 1
284
299
 
285
300
  return false
286
301
  }
287
302
 
288
303
  // determines if you can place a word at the row, column in the direction
289
- public canPlaceWordAt(word, row, col, direction): any {
304
+ public canPlaceWordAt(word: any, row: any, col: any, direction: any): any {
290
305
  // out of bounds
291
306
  if (
292
307
  row < 0 ||
@@ -296,17 +311,17 @@ class Crossword {
296
311
  )
297
312
  return false
298
313
 
299
- var intersections
314
+ let intersections
300
315
 
301
- if (direction == 'across') {
316
+ if (direction === 'across') {
302
317
  // out of bounds (word too long)
303
318
  if (col + word.length > this.grid[row].length) return false
304
319
  // can't have a word directly to the left
305
- if (col - 1 >= 0 && this.grid[row][col - 1] != null) return false
320
+ if (col - 1 >= 0 && this.grid[row][col - 1] !== null) return false
306
321
  // can't have word directly to the right
307
322
  if (
308
323
  col + word.length < this.grid[row].length &&
309
- this.grid[row][col + word.length] != null
324
+ this.grid[row][col + word.length] !== null
310
325
  )
311
326
  return false
312
327
 
@@ -318,10 +333,10 @@ class Crossword {
318
333
  r >= 0 && c < col + word.length;
319
334
  c++, i++
320
335
  ) {
321
- let is_empty = this.grid[r][c] == null
336
+ let is_empty = this.grid[r][c] === null
322
337
  let is_intersection =
323
- this.grid[row][c] != null &&
324
- this.grid[row][c]['char'] == word.charAt(i)
338
+ this.grid[row][c] !== null &&
339
+ this.grid[row][c]['char'] === word.charAt(i)
325
340
  let can_place_here = is_empty || is_intersection
326
341
  if (!can_place_here) return false
327
342
  }
@@ -332,10 +347,10 @@ class Crossword {
332
347
  r < this.grid.length && c < col + word.length;
333
348
  c++, i++
334
349
  ) {
335
- let is_empty = this.grid[r][c] == null
350
+ let is_empty = this.grid[r][c] === null
336
351
  let is_intersection =
337
- this.grid[row][c] != null &&
338
- this.grid[row][c]['char'] == word.charAt(i)
352
+ this.grid[row][c] !== null &&
353
+ this.grid[row][c]['char'] === word.charAt(i)
339
354
  let can_place_here = is_empty || is_intersection
340
355
  if (!can_place_here) return false
341
356
  }
@@ -348,15 +363,15 @@ class Crossword {
348
363
  if (result === false) return false
349
364
  intersections += result
350
365
  }
351
- } else if (direction == 'down') {
366
+ } else if (direction === 'down') {
352
367
  // out of bounds
353
368
  if (row + word.length > this.grid.length) return false
354
369
  // can't have a word directly above
355
- if (row - 1 >= 0 && this.grid[row - 1][col] != null) return false
370
+ if (row - 1 >= 0 && this.grid[row - 1][col] !== null) return false
356
371
  // can't have a word directly below
357
372
  if (
358
373
  row + word.length < this.grid.length &&
359
- this.grid[row + word.length][col] != null
374
+ this.grid[row + word.length][col] !== null
360
375
  )
361
376
  return false
362
377
 
@@ -369,10 +384,10 @@ class Crossword {
369
384
  c >= 0 && r < row + word.length;
370
385
  r++, i++
371
386
  ) {
372
- let is_empty = this.grid[r][c] == null
387
+ let is_empty = this.grid[r][c] === null
373
388
  let is_intersection =
374
- this.grid[r][col] != null &&
375
- this.grid[r][col]['char'] == word.charAt(i)
389
+ this.grid[r][col] !== null &&
390
+ this.grid[r][col]['char'] === word.charAt(i)
376
391
  let can_place_here = is_empty || is_intersection
377
392
  if (!can_place_here) return false
378
393
  }
@@ -383,10 +398,10 @@ class Crossword {
383
398
  r < row + word.length && c < this.grid[r].length;
384
399
  r++, i++
385
400
  ) {
386
- let is_empty = this.grid[r][c] == null
401
+ let is_empty = this.grid[r][c] === null
387
402
  let is_intersection =
388
- this.grid[r][col] != null &&
389
- this.grid[r][col]['char'] == word.charAt(i)
403
+ this.grid[r][col] !== null &&
404
+ this.grid[r][col]['char'] === word.charAt(i)
390
405
  let can_place_here = is_empty || is_intersection
391
406
  if (!can_place_here) return false
392
407
  }
@@ -409,24 +424,24 @@ class Crossword {
409
424
  return Math.floor(Math.random() * 2) ? 'across' : 'down'
410
425
  }
411
426
 
412
- public findPositionForWord(word) {
413
- // check the char_index for every letter, and see if we can put it there in a direction
427
+ public findPositionForWord(word: any) {
428
+ // check the charIndex for every letter, and see if we can put it there in a direction
414
429
  let bests: object[] = []
415
- for (var i = 0; i < word.length; i++) {
416
- var possible_locations_on_grid = this.char_index[word.charAt(i)]
430
+ for (let i = 0; i < word.length; i++) {
431
+ const possible_locations_on_grid = this.charIndex[word.charAt(i)]
417
432
  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']
433
+ for (let j = 0; j < possible_locations_on_grid.length; j++) {
434
+ const point = possible_locations_on_grid[j]
435
+ const r = point['row']
436
+ const c = point['col']
422
437
  // the c - i, and r - i here compensate for the offset of character in the word
423
- var intersections_across = this.canPlaceWordAt(
438
+ const intersections_across = this.canPlaceWordAt(
424
439
  word,
425
440
  r,
426
441
  c - i,
427
442
  'across'
428
443
  )
429
- var intersections_down = this.canPlaceWordAt(
444
+ const intersections_down = this.canPlaceWordAt(
430
445
  word,
431
446
  r - i,
432
447
  c,
@@ -434,7 +449,7 @@ class Crossword {
434
449
  )
435
450
 
436
451
  if (intersections_across !== false) {
437
- var objectAcross = {
452
+ const objectAcross = {
438
453
  intersections: intersections_across,
439
454
  row: r,
440
455
  col: c - i,
@@ -443,7 +458,7 @@ class Crossword {
443
458
  bests.push(objectAcross)
444
459
  }
445
460
  if (intersections_down !== false) {
446
- var objectDown = {
461
+ const objectDown = {
447
462
  intersections: intersections_down,
448
463
  row: r - i,
449
464
  col: c,
@@ -454,22 +469,85 @@ class Crossword {
454
469
  }
455
470
  }
456
471
 
457
- if (bests.length == 0) return false
472
+ if (bests.length === 0) return false
458
473
 
459
474
  // find a good random position
460
- var best = bests[Math.floor(Math.random() * bests.length)]
461
-
462
- return best
475
+ return bests[Math.floor(Math.random() * bests.length)]
463
476
  }
464
477
 
465
478
  public clear() {
466
- for (var r = 0; r < this.grid.length; r++) {
467
- for (var c = 0; c < this.grid[r].length; c++) {
479
+ for (let r = 0; r < this.grid.length; r++) {
480
+ for (let c = 0; c < this.grid[r].length; c++) {
468
481
  this.grid[r][c] = null
469
482
  }
470
483
  }
471
- this.char_index = {}
484
+ this.charIndex = {}
485
+ }
486
+ }
487
+
488
+ class CrosswordUtils {
489
+ public static PATH_TO_PNGS_OF_NUMBERS: string = 'numbers/'
490
+
491
+ public static toHtml(grid: any, show_answers: any) {
492
+ if (grid === null) return
493
+ const html: Array<string> = []
494
+ html.push("<table class='crossword'>")
495
+ let label = 1
496
+ for (let r = 0; r < grid.length; r++) {
497
+ html.push('<tr>')
498
+ for (let c = 0; c < grid[r].length; c++) {
499
+ const cell = grid[r][c]
500
+ let isStartOfWord = false
501
+ let css_class, char
502
+ if (cell === null) {
503
+ char = '&nbsp;'
504
+ css_class = 'no-border'
505
+ } else {
506
+ char = cell['char']
507
+ css_class = ''
508
+ isStartOfWord =
509
+ (cell.across && cell.across.isStartOfWord) ||
510
+ (cell.down && cell.down.isStartOfWord)
511
+ }
512
+
513
+ if (isStartOfWord) {
514
+ const img_url =
515
+ this.PATH_TO_PNGS_OF_NUMBERS + label.toString() + '.png'
516
+ html.push(
517
+ "<td class='" +
518
+ css_class +
519
+ "' title='" +
520
+ r +
521
+ ', ' +
522
+ c +
523
+ "' style=\"background-image:url('" +
524
+ img_url +
525
+ '\')">'
526
+ )
527
+ label++
528
+ } else {
529
+ html.push(
530
+ "<td class='" +
531
+ css_class +
532
+ "' title='" +
533
+ r +
534
+ ', ' +
535
+ c +
536
+ "'>"
537
+ )
538
+ }
539
+
540
+ if (show_answers) {
541
+ html.push(char)
542
+ } else {
543
+ html.push('&nbsp;')
544
+ }
545
+ }
546
+ html.push('</tr>')
547
+ }
548
+ html.push('</table>')
549
+ return html.join('\n')
472
550
  }
473
551
  }
474
552
 
475
- export { Crossword }
553
+ export { Crossword, CrosswordUtils }