maze-test 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -339,7 +339,8 @@ function analyzeSolidCellMaze(maze) {
339
339
  const distances = bfsDistances(maze.entry, graph);
340
340
  const degreeValues = [...graph.values()].map((items) => items.length);
341
341
  const edgeCount = degreeValues.reduce((sum, value) => sum + value, 0) / 2;
342
- const mainPath = shortestPath(maze, maze.entry, maze.goal);
342
+ const mainPath = shortestPathInGraph(graph, maze.entry, maze.goal);
343
+ const entryGoalSeparators = separatingCellKeys(graph, maze.entry, maze.goal);
343
344
  const objectCounts = {};
344
345
  for (const object of maze.objects ?? []) {
345
346
  objectCounts[object.type] = (objectCounts[object.type] ?? 0) + 1;
@@ -357,7 +358,7 @@ function analyzeSolidCellMaze(maze) {
357
358
  entryGoalDistance: mainPath.length > 0 ? mainPath.length - 1 : null,
358
359
  doorCount: maze.doors.length,
359
360
  gatingDoorCount: maze.doors.filter(
360
- (door) => shortestPath(maze, maze.entry, maze.goal, door.position).length === 0
361
+ (door) => entryGoalSeparators.has(cellKey(door.position))
361
362
  ).length,
362
363
  objectCounts
363
364
  };
@@ -427,6 +428,9 @@ function validateSolidCellMaze(maze) {
427
428
  function shortestPath(maze, start, goal, excludedCell = null) {
428
429
  if (excludedCell && (sameCell(start, excludedCell) || sameCell(goal, excludedCell))) return [];
429
430
  const graph = adjacency(maze, excludedCell);
431
+ return shortestPathInGraph(graph, start, goal);
432
+ }
433
+ function shortestPathInGraph(graph, start, goal) {
430
434
  const queue = [start];
431
435
  const parent = /* @__PURE__ */ new Map([[cellKey(start), null]]);
432
436
  const values = /* @__PURE__ */ new Map([[cellKey(start), start]]);
@@ -558,12 +562,14 @@ function degreeMap(maze) {
558
562
  }
559
563
  function selectGatingDoorIndices(maze, mainPath, count) {
560
564
  if (count === 0) return [];
561
- const degrees = degreeMap(maze);
565
+ const graph = adjacency(maze);
566
+ const separators = separatingCellKeys(graph, maze.entry, maze.goal);
562
567
  const candidates = [];
563
568
  for (let index = 3; index < mainPath.length - 3; index += 1) {
564
569
  const pathCell = mainPath[index];
565
- if (!pathCell || (degrees.get(cellKey(pathCell)) ?? 0) !== 2) continue;
566
- if (shortestPath(maze, maze.entry, maze.goal, pathCell).length === 0) candidates.push(index);
570
+ if (!pathCell) continue;
571
+ const key = cellKey(pathCell);
572
+ if ((graph.get(key)?.length ?? 0) === 2 && separators.has(key)) candidates.push(index);
567
573
  }
568
574
  if (candidates.length < count) {
569
575
  throw new Error(`Only ${candidates.length} non-bypassable path cells are available for ${count} doors.`);
@@ -582,6 +588,59 @@ function selectGatingDoorIndices(maze, mainPath, count) {
582
588
  if (selected.length !== count) throw new Error(`Could not space ${count} doors along the main path.`);
583
589
  return selected.sort((left, right) => left - right);
584
590
  }
591
+ function separatingCellKeys(graph, start, goal) {
592
+ const startKey = cellKey(start);
593
+ const goalKey = cellKey(goal);
594
+ if (!graph.has(startKey) || !graph.has(goalKey)) return /* @__PURE__ */ new Set();
595
+ const discovered = /* @__PURE__ */ new Map();
596
+ const low = /* @__PURE__ */ new Map();
597
+ const parent = /* @__PURE__ */ new Map([[startKey, null]]);
598
+ const stack = [
599
+ { key: startKey, nextNeighbor: 0 }
600
+ ];
601
+ let order = 1;
602
+ discovered.set(startKey, order);
603
+ low.set(startKey, order);
604
+ while (stack.length > 0) {
605
+ const frame = stack.at(-1);
606
+ if (!frame) break;
607
+ const adjacent = graph.get(frame.key) ?? [];
608
+ const next = adjacent[frame.nextNeighbor];
609
+ if (next) {
610
+ frame.nextNeighbor += 1;
611
+ const nextKey = cellKey(next);
612
+ if (!discovered.has(nextKey)) {
613
+ order += 1;
614
+ discovered.set(nextKey, order);
615
+ low.set(nextKey, order);
616
+ parent.set(nextKey, frame.key);
617
+ stack.push({ key: nextKey, nextNeighbor: 0 });
618
+ continue;
619
+ }
620
+ if (nextKey !== parent.get(frame.key)) {
621
+ low.set(frame.key, Math.min(low.get(frame.key) ?? order, discovered.get(nextKey) ?? order));
622
+ }
623
+ continue;
624
+ }
625
+ stack.pop();
626
+ const parentKey = parent.get(frame.key);
627
+ if (parentKey) {
628
+ low.set(parentKey, Math.min(low.get(parentKey) ?? order, low.get(frame.key) ?? order));
629
+ }
630
+ }
631
+ if (!discovered.has(goalKey)) return /* @__PURE__ */ new Set();
632
+ const separators = /* @__PURE__ */ new Set();
633
+ let childKey = goalKey;
634
+ while (childKey !== startKey) {
635
+ const parentKey = parent.get(childKey);
636
+ if (!parentKey) break;
637
+ if (parentKey !== startKey && (low.get(childKey) ?? Number.POSITIVE_INFINITY) >= (discovered.get(parentKey) ?? Number.NEGATIVE_INFINITY)) {
638
+ separators.add(parentKey);
639
+ }
640
+ childKey = parentKey;
641
+ }
642
+ return separators;
643
+ }
585
644
  function freePathCellBefore(mainPath, preferredIndex, occupied) {
586
645
  for (let distance = 0; distance < mainPath.length; distance += 1) {
587
646
  for (const index of [preferredIndex - distance, preferredIndex + distance]) {