From: Carl Worth Date: Fri, 6 Mar 2026 12:58:57 +0000 (-0500) Subject: Calm down the dynamic grid resizing X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=c0566ef305b34380df6ec2dba6642e2564772c49;p=lmno.games Calm down the dynamic grid resizing 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. --- diff --git a/letterrip/letterrip.jsx b/letterrip/letterrip.jsx index 0c5b656..17f5cfd 100644 --- a/letterrip/letterrip.jsx +++ b/letterrip/letterrip.jsx @@ -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++) {