* Compute grid render bounds *
*********************************************************/
-function grid_bounds(grid) {
+/* The grid starts at 10x10 and only grows when a tile is placed in
+ * one of the outermost rows/columns, ensuring there is always at
+ * least 1 empty row/column at each edge for placing new tiles. */
+const GRID_MIN_SIZE = 10;
+
+function grid_bounds(grid, prev_bounds) {
const keys = Object.keys(grid);
if (keys.length === 0) {
- return { minR: -5, maxR: 4, minC: -5, maxC: 4 };
+ const half = Math.floor(GRID_MIN_SIZE / 2);
+ return { minR: -half, maxR: half - 1, minC: -half, maxC: half - 1 };
}
- let minR = Infinity, maxR = -Infinity, minC = Infinity, maxC = -Infinity;
+
+ /* Find the bounding box of placed tiles. */
+ let tileMinR = Infinity, tileMaxR = -Infinity;
+ let tileMinC = Infinity, tileMaxC = -Infinity;
for (const key of keys) {
const [r, c] = key.split(",").map(Number);
- if (r < minR) minR = r;
- if (r > maxR) maxR = r;
- if (c < minC) minC = c;
- if (c > maxC) maxC = c;
- }
- /* Add padding. */
- const PAD = 3;
- minR -= PAD; maxR += PAD;
- minC -= PAD; maxC += PAD;
- /* Minimum 10x10. */
- while (maxR - minR + 1 < 10) { minR--; maxR++; }
- while (maxC - minC + 1 < 10) { minC--; maxC++; }
+ if (r < tileMinR) tileMinR = r;
+ if (r > tileMaxR) tileMaxR = r;
+ if (c < tileMinC) tileMinC = c;
+ if (c > tileMaxC) tileMaxC = c;
+ }
+
+ /* Start from previous bounds (or initial size if none). */
+ let minR, maxR, minC, maxC;
+ if (prev_bounds) {
+ minR = prev_bounds.minR;
+ maxR = prev_bounds.maxR;
+ minC = prev_bounds.minC;
+ maxC = prev_bounds.maxC;
+ } else {
+ const half = Math.floor(GRID_MIN_SIZE / 2);
+ minR = Math.min(-half, tileMinR - 1);
+ maxR = Math.max(half - 1, tileMaxR + 1);
+ minC = Math.min(-half, tileMinC - 1);
+ maxC = Math.max(half - 1, tileMaxC + 1);
+ }
+
+ /* Expand only if a tile touches the edge (ensure 1 empty row/col). */
+ if (tileMinR <= minR) minR = tileMinR - 1;
+ if (tileMaxR >= maxR) maxR = tileMaxR + 1;
+ if (tileMinC <= minC) minC = tileMinC - 1;
+ if (tileMaxC >= maxC) maxC = tileMaxC + 1;
+
return { minR, maxR, minC, maxC };
}
blank_pending: null,
drag_source: null,
drag_over_cell: null,
- rack_drag_over: false
+ rack_drag_over: false,
+ grid_bounds: null
};
}
render_board(analysis, invalid_cells, unconnected_cells) {
const { grid, drag_over_cell, drag_source } = this.state;
- const bounds = grid_bounds(grid);
+ const bounds = grid_bounds(grid, this.state.grid_bounds);
+ /* Store bounds so the grid only grows, never shrinks. */
+ if (JSON.stringify(bounds) !== JSON.stringify(this.state.grid_bounds)) {
+ setTimeout(() => this.setState({ grid_bounds: bounds }), 0);
+ }
const rows = [];
for (let r = bounds.minR; r <= bounds.maxR; r++) {