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