Make the expand and collapse interactions inverses of one another

For the most part, at least. If the edge cases where they differ still feel weird, I can iterate on this further.

The diff is unfortunately a bit impenetrable, because I had to change both the fillGaps and cycleTileSize core algorithms used by the big grid layout. But: the main change of significance is the addition of a function vacateArea, which clears out an area within the grid in a specific way that mirrors the motion performed by fillGaps.
This commit is contained in:
Robin Townsend 2023-07-06 00:43:17 -04:00
parent aab08d2bf1
commit 3ac98c8865
3 changed files with 543 additions and 321 deletions

View file

@ -22,10 +22,10 @@ limitations under the License.
// Array.prototype.findLastIndex
export function findLastIndex<T>(
array: T[],
predicate: (item: T) => boolean
predicate: (item: T, index: number) => boolean
): number | null {
for (let i = array.length - 1; i >= 0; i--) {
if (predicate(array[i])) return i;
if (predicate(array[i], i)) return i;
}
return null;
@ -34,5 +34,11 @@ export function findLastIndex<T>(
/**
* Counts the number of elements in an array that satsify the given predicate.
*/
export const count = <T>(array: T[], predicate: (item: T) => boolean): number =>
array.reduce((acc, item) => (predicate(item) ? acc + 1 : acc), 0);
export const count = <T>(
array: T[],
predicate: (item: T, index: number) => boolean
): number =>
array.reduce(
(acc, item, index) => (predicate(item, index) ? acc + 1 : acc),
0
);

File diff suppressed because it is too large Load diff

View file

@ -20,7 +20,7 @@ import {
cycleTileSize,
fillGaps,
forEachCellInArea,
BigGridState,
Grid,
resize,
row,
moveTile,
@ -30,13 +30,13 @@ import { TileDescriptor } from "../../src/video-grid/VideoGrid";
/**
* Builds a grid from a string specifying the contents of each cell as a letter.
*/
function mkGrid(spec: string): BigGridState {
function mkGrid(spec: string): Grid {
const secondNewline = spec.indexOf("\n", 1);
const columns = secondNewline === -1 ? spec.length : secondNewline - 1;
const cells = spec.match(/[a-z ]/g) ?? ([] as string[]);
const areas = new Set(cells);
areas.delete(" "); // Space represents an empty cell, not an area
const grid: BigGridState = { columns, cells: new Array(cells.length) };
const grid: Grid = { columns, cells: new Array(cells.length) };
for (const area of areas) {
const start = cells.indexOf(area);
@ -60,7 +60,7 @@ function mkGrid(spec: string): BigGridState {
/**
* Turns a grid into a string showing the contents of each cell as a letter.
*/
function showGrid(g: BigGridState): string {
function showGrid(g: Grid): string {
let result = "\n";
for (let i = 0; i < g.cells.length; i++) {
if (i > 0 && i % g.columns == 0) result += "\n";
@ -116,11 +116,11 @@ mno`,
`
aebch
difgl
monjk`
mjnok`
);
testFillGaps(
"fills a big gap",
"fills a big gap with 1×1 tiles",
`
abcd
e f
@ -128,19 +128,19 @@ g h
ijkl`,
`
abcd
elhf
gkji`
ehkf
glji`
);
testFillGaps(
"only moves 1×1 tiles",
"fills a big gap with a large tile",
`
aa
bc`,
`
bc
aa`
aa
cb`
);
testFillGaps(
@ -186,7 +186,7 @@ iief`
);
testFillGaps(
"pushes a chain of large tiles upwards",
"collapses large tiles trapped at the bottom",
`
abcd
e fg
@ -195,24 +195,24 @@ hh
ii
ii`,
`
hhcd
abcd
hhfg
aiib
eii`
hhie`
);
testFillGaps(
"gives up on pushing large tiles upwards when not possible",
`
aabb
aabb
cc
cc`,
aa
aa
bccd
eccf
ghij`,
`
aabb
aabb
cc
cc`
aadf
aaji
bcch
eccg`
);
function testCycleTileSize(
@ -237,9 +237,9 @@ def
ghi`,
`
acc
bcc
def
ghi`
dcc
gbe
ifh`
);
testCycleTileSize(
@ -249,10 +249,10 @@ testCycleTileSize(
abcd
efgh`,
`
abcd
eggg
acdh
bggg
fggg
h`
e`
);
testCycleTileSize(
@ -264,9 +264,9 @@ dbbe
fghi
jk`,
`
akbc
djhe
fig`
abhc
djge
fik`
);
testCycleTileSize(
@ -284,9 +284,9 @@ abb
gbb
dde
ddf
cci
ccm
cch
klm`
lik`
);
testCycleTileSize(
@ -304,6 +304,34 @@ dde
ddf`
);
test("cycleTileSize is its own inverse", () => {
const input = `
abc
def
ghi
jk`;
const grid = mkGrid(input);
let gridAfter = grid;
const toggle = (tileId: string) => {
const tile = grid.cells.find((c) => c?.item.id === tileId)!.item;
gridAfter = cycleTileSize(gridAfter, tile);
};
// Toggle a series of tiles
toggle("j");
toggle("h");
toggle("a");
// Now do the same thing in reverse
toggle("a");
toggle("h");
toggle("j");
// The grid should be back to its original state
expect(showGrid(gridAfter)).toBe(input);
});
function testAddItems(
title: string,
items: TileDescriptor<unknown>[],
@ -437,9 +465,9 @@ gh`,
af
bb
bb
dd
dd
ch
dd
dd
eg`
);
@ -455,9 +483,8 @@ dd
dd
eg`,
`
bbbc
bbbf
addd
hddd
ge`
afcd
bbbg
bbbe
h`
);