]> git.cworth.org Git - lmno.games/commitdiff
Calm down the dynamic grid resizing
authorCarl Worth <cworth@cworth.org>
Fri, 6 Mar 2026 12:58:57 +0000 (07:58 -0500)
committerCarl Worth <cworth@cworth.org>
Fri, 6 Mar 2026 16:29:48 +0000 (11:29 -0500)
There was some annoying jitter as rows were being constantly
added/removed so that there were always 3 empty rows/columns around
placed tiles. Instead, in this commit, the grid starts initially at a
size of 10x10 and then only grows (by one tile) when a tile is placed
at an extreme edge.

letterrip/letterrip.jsx

index 0c5b656266fb37e125f7f7629f3f626c3757c40c..17f5cfd78dc5ab09efec0f52a5939baea53da245 100644 (file)
@@ -179,26 +179,50 @@ function get_unconnected_cells(grid, analysis) {
  * 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 };
 }
 
@@ -300,7 +324,8 @@ class Game extends React.Component {
       blank_pending: null,
       drag_source: null,
       drag_over_cell: null,
-      rack_drag_over: false
+      rack_drag_over: false,
+      grid_bounds: null
     };
   }
 
@@ -616,7 +641,11 @@ class Game extends React.Component {
 
   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++) {