]> git.cworth.org Git - lmno.games/commitdiff
Don't grow the grid if not needed, (shift tiles instead)
authorCarl Worth <cworth@cworth.org>
Fri, 6 Mar 2026 14:30:06 +0000 (09:30 -0500)
committerCarl Worth <cworth@cworth.org>
Fri, 6 Mar 2026 16:29:48 +0000 (11:29 -0500)
At least, shifting of the tiles is what the user experiences. Within
the code, the concept is simpler: We don't have a grid size at all
anymore, but simply compute the grid bounds that we want whenever
things change. The bounds are made to just fit the tiles (plus one
extra row/column on each side) and never smaller than the original
size chosen to fill the screen.

letterrip/letterrip.jsx

index a9e5437e3061c108a1f2d1aa5bdc5435d6e2e483..ca07ee71cf1ca4d7096ac27255ba39171da388cd 100644 (file)
@@ -191,7 +191,10 @@ function get_cell_size() {
 
 /* The grid starts as the largest square that fits the viewport width
  * (capped at 10x10) and only grows when a tile is placed in one of
- * the outermost rows/columns. */
+ * the outermost rows/columns. When the grid expands in one direction,
+ * empty rows/columns on the opposite side are trimmed (keeping at
+ * least one empty row/column adjacent to the nearest tile). The grid
+ * never shrinks below the initial size. */
 function initial_grid_size() {
   const cell_size = get_cell_size() + 2; /* +2 for 1px border each side */
   const available = window.innerWidth - 32; /* rough page padding */
@@ -199,10 +202,12 @@ function initial_grid_size() {
   return Math.max(4, Math.min(10, fit));
 }
 
-function grid_bounds(grid, prev_bounds) {
+function grid_bounds(grid) {
+  const min_size = initial_grid_size();
   const keys = Object.keys(grid);
+
   if (keys.length === 0) {
-    const half = Math.floor(initial_grid_size() / 2);
+    const half = Math.floor(min_size / 2);
     return { minR: -half, maxR: half - 1, minC: -half, maxC: half - 1 };
   }
 
@@ -217,28 +222,25 @@ function grid_bounds(grid, prev_bounds) {
     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(initial_grid_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 };
+  /* The grid must include all tiles plus 1 empty row/column on each
+   * side, and must be at least min_size in each dimension. */
+  const tile_rows = tileMaxR - tileMinR + 1 + 2; /* +2 for padding */
+  const tile_cols = tileMaxC - tileMinC + 1 + 2;
+  const rows = Math.max(min_size, tile_rows);
+  const cols = Math.max(min_size, tile_cols);
+
+  /* Center the extra space around the tiles. */
+  const extra_rows = rows - tile_rows;
+  const extra_cols = cols - tile_cols;
+  const pad_top = Math.floor(extra_rows / 2);
+  const pad_left = Math.floor(extra_cols / 2);
+
+  return {
+    minR: tileMinR - 1 - pad_top,
+    maxR: tileMinR - 1 - pad_top + rows - 1,
+    minC: tileMinC - 1 - pad_left,
+    maxC: tileMinC - 1 - pad_left + cols - 1
+  };
 }
 
 /*********************************************************
@@ -343,7 +345,6 @@ class Game extends React.Component {
       drag_source: null,
       drag_over_cell: null,
       rack_drag_over: false,
-      grid_bounds: null,
       selected: null
     };
   }
@@ -927,11 +928,7 @@ class Game extends React.Component {
 
   render_board(analysis, invalid_cells, unconnected_cells) {
     const { grid, drag_over_cell, drag_source, selected } = this.state;
-    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 bounds = grid_bounds(grid);
     const rows = [];
 
     for (let r = bounds.minR; r <= bounds.maxR; r++) {