});
}
- find_connected(mini_grid_index, position) {
- /* TODO: Implement actual finding of connected squares here. */
- let connected = Array(9).fill(false);
+ find_connected_recursive(recursion_state, position) {
+
+ if (position < 0 || position >= 9)
+ return;
+
+ if (recursion_state.visited[position])
+ return;
+
+ recursion_state.visited[position] = true;
+
+ if (recursion_state.mini_grid[position].symbol !== recursion_state.target)
+ return;
+
+ recursion_state.connected[position] = true;
+
+ /* Left */
+ if (position % 3 !== 0)
+ this.find_connected_recursive(recursion_state, position - 1);
+ /* Right */
+ if (position % 3 !== 2)
+ this.find_connected_recursive(recursion_state, position + 1);
+ /* Up */
+ this.find_connected_recursive(recursion_state, position - 3);
+ /* Down */
+ this.find_connected_recursive(recursion_state, position + 3);
+ }
+
+ /* Find all cells within a mini-grid that are 4-way connected to the
+ * given cell. */
+ find_connected(mini_grid, position) {
+ const connected = Array(9).fill(false);
+
+ /* If the given cell is empty then there is nothing connected. */
+ if (mini_grid[position] === null)
+ return connected;
+
+ const cell = mini_grid[position].symbol;
+
+ let recursion_state = {
+ mini_grid: mini_grid,
+ connected: connected,
+ visited: Array(9).fill(false),
+ target: cell,
+ };
+ this.find_connected_recursive(recursion_state, position);
return connected;
}
- detect_glyph(connected) {
- /* TODO: Implement actual glyph detection here. */
+ /* Determine whether a connected group of cells is a glyph.
+ *
+ * Here, 'connected' is a length-9 array of Booleans, true
+ * for the connected cells in a mini-grid.
+ */
+ is_glyph(connected) {
+
+ /* Now that we have a set of connected cells, let's collect some
+ * stats on them, (width, height, number of cells, configuration
+ * of corner cells, etc.).
+ */
+ let min_row = 2;
+ let min_col = 2;
+ let max_row = 0;
+ let max_col = 0;
+ let count = 0;
+
+ for (let i = 0; i < 9; i++) {
+ const row = Math.floor(i/3);
+ const col = i % 3;
+
+ if (! connected[i])
+ continue;
+
+ count++;
+
+ min_row = Math.min(row, min_row);
+ min_col = Math.min(col, min_col);
+ max_row = Math.max(row, max_row);
+ max_col = Math.max(col, max_col);
+ }
+
+ const width = max_col - min_col + 1;
+ const height = max_row - min_row + 1;
+
+ /* Corners, (top-left, top-right, bottom-left, and bottom-right) */
+ const tl = connected[3 * min_row + min_col];
+ const tr = connected[3 * min_row + max_col];
+ const bl = connected[3 * max_row + min_col];
+ const br = connected[3 * max_row + max_col];
+
+ const count_true = (acc, val) => acc + (val ? 1 : 0);
+ const corners_count = [tl, tr, bl, br].reduce(count_true, 0);
+ const top_corners_count = [tl, tr].reduce(count_true, 0);
+ const bottom_corners_count = [bl, br].reduce(count_true, 0);
+ const left_corners_count = [tl, bl].reduce(count_true, 0);
+ const right_corners_count = [tr, br].reduce(count_true, 0);
+
+ let two_corners_in_a_line = false;
+ if (top_corners_count === 2 ||
+ bottom_corners_count === 2 ||
+ left_corners_count === 2 ||
+ right_corners_count === 2)
+ {
+ two_corners_in_a_line = true;
+ }
+
+ let zero_corners_in_a_line = false;
+ if (top_corners_count === 0 ||
+ bottom_corners_count === 0 ||
+ left_corners_count === 0 ||
+ right_corners_count === 0)
+ {
+ zero_corners_in_a_line = true;
+ }
+
+ /* Now we have the information we need to determine glyphs. */
+ switch (count) {
+ case 1:
+ /* Single */
+ return true;
+ case 2:
+ /* Double */
+ return true;
+ case 3:
+ /* Line */
+ return (width === 3 || height === 3);
+ case 4:
+ /* Pipe, Squat-T, and 4-block, but not Tetris S */
+ return two_corners_in_a_line;
+ case 5:
+ if (width !== 3 || height !== 3 || ! connected[4])
+ {
+ /* Pentomino P and U are not glyphs (not 3x3) */
+ /* Pentomino V is not a glyph (center not connected) */
+ return false;
+ }
+ else if (corners_count === 0 || two_corners_in_a_line)
+ {
+ /* Pentomino X is glyph Cross (no corners) */
+ /* Pentomino T is glyph T (has a row or column with 2 corners) */
+ return true;
+ } else {
+ /* The corner counting above excludes pentomino F, W, and Z
+ * which are not glyphs. */
+ return false;
+ }
+ break;
+ case 6:
+ /* 6-Block has width or height of 2. */
+ /* Bomber, Chair, and J have 3 corners occupied. */
+ if (width === 2 || height === 2 || corners_count === 3)
+ return true;
+ return false;
+ case 7:
+ /* Earring and U have no center square occupied */
+ /* H has 4 corners occupied */
+ /* House has a row or column with 0 corners occupied */
+ if ((! connected[4]) || corners_count === 4 || zero_corners_in_a_line)
+ return true;
+ return false;
+ case 8:
+ /* Ottoman or O */
+ if (corners_count === 4)
+ return true;
+ return false;
+ case 9:
+ return true;
+ }
+
+ /* Should be unreachable */
return false;
}
/* With the symbol added to the squares, we need to see if this
* newly-placed move forms a glyph or not. */
- const connected = this.find_connected(mini_grid_index, position);
- const is_glyph = this.detect_glyph(connected);
+ const connected = this.find_connected(new_squares[mini_grid_index], position);
+ const is_glyph = this.is_glyph(connected);
for (let i = 0; i < 9; i++) {
if (connected[i])