import { describe, it, expect } from 'vitest'; import { generatePointCrawlMap, hasPath } from '@/samples/slay-the-spire-like/map/generator'; import { MapNodeType, MapLayerType } from '@/samples/slay-the-spire-like/map/types'; describe('generatePointCrawlMap', () => { it('should generate a map with 10 layers', () => { const map = generatePointCrawlMap(123); expect(map.layers.length).toBe(10); }); it('should have correct layer structure', () => { const map = generatePointCrawlMap(123); const expectedStructure = [ 'start', MapLayerType.Wild, MapLayerType.Wild, MapLayerType.Settlement, MapLayerType.Wild, MapLayerType.Wild, MapLayerType.Settlement, MapLayerType.Wild, MapLayerType.Wild, 'end', ]; for (let i = 0; i < expectedStructure.length; i++) { expect(map.layers[i].layerType).toBe(expectedStructure[i]); } }); it('should have correct node counts per layer', () => { const map = generatePointCrawlMap(123); const expectedCounts = [1, 3, 3, 4, 3, 3, 4, 3, 3, 1]; for (let i = 0; i < expectedCounts.length; i++) { expect(map.layers[i].nodeIds.length).toBe(expectedCounts[i]); } }); it('should have Start and End nodes with correct types', () => { const map = generatePointCrawlMap(123); const startNode = map.nodes.get('node-0-0'); const endNode = map.nodes.get('node-9-0'); expect(startNode?.type).toBe(MapNodeType.Start); expect(endNode?.type).toBe(MapNodeType.End); }); it('should have wild layers with minion/elite/event types', () => { const map = generatePointCrawlMap(123); const wildLayerIndices = [1, 2, 4, 5, 7, 8]; const validWildTypes = new Set([MapNodeType.Minion, MapNodeType.Elite, MapNodeType.Event]); for (const layerIdx of wildLayerIndices) { const layer = map.layers[layerIdx]; for (const nodeId of layer.nodeIds) { const node = map.nodes.get(nodeId); expect(node).toBeDefined(); expect(validWildTypes.has(node!.type)).toBe(true); } } }); it('should have settlement layers with at least 1 camp, 1 shop, 1 curio', () => { const map = generatePointCrawlMap(123); const settlementLayerIndices = [3, 6]; for (const layerIdx of settlementLayerIndices) { const layer = map.layers[layerIdx]; const nodeTypes = layer.nodeIds.map(id => map.nodes.get(id)!.type); expect(nodeTypes).toContain(MapNodeType.Camp); expect(nodeTypes).toContain(MapNodeType.Shop); expect(nodeTypes).toContain(MapNodeType.Curio); expect(nodeTypes.length).toBe(4); } }); it('should have Start connected to all 3 wild nodes', () => { const map = generatePointCrawlMap(42); const startNode = map.nodes.get('node-0-0'); const wildLayer = map.layers[1]; expect(startNode?.childIds.length).toBe(3); expect(startNode?.childIds).toEqual(expect.arrayContaining(wildLayer.nodeIds)); }); it('should have each wild node connect to 1 wild node in wild→wild layers', () => { const map = generatePointCrawlMap(42); const wildToWildTransitions = [ { src: 1, tgt: 2 }, { src: 4, tgt: 5 }, { src: 7, tgt: 8 }, ]; for (const transition of wildToWildTransitions) { const srcLayer = map.layers[transition.src]; for (const srcId of srcLayer.nodeIds) { const srcNode = map.nodes.get(srcId); expect(srcNode?.childIds.length).toBe(1); const childLayer = map.layers[transition.tgt]; expect(childLayer.nodeIds).toContain(srcNode!.childIds[0]); } } }); it('should have each wild node connect to 2 settlement nodes in wild→settlement layers', () => { const map = generatePointCrawlMap(42); const wildToSettlementTransitions = [ { src: 2, tgt: 3 }, { src: 5, tgt: 6 }, ]; for (const transition of wildToSettlementTransitions) { const srcLayer = map.layers[transition.src]; for (const srcId of srcLayer.nodeIds) { const srcNode = map.nodes.get(srcId); expect(srcNode?.childIds.length).toBe(2); const childLayer = map.layers[transition.tgt]; for (const childId of srcNode!.childIds) { expect(childLayer.nodeIds).toContain(childId); } } } }); it('should have settlement nodes connect correctly (1-2-2-1 pattern)', () => { const map = generatePointCrawlMap(42); const settlementToWildTransitions = [ { src: 3, tgt: 4 }, { src: 6, tgt: 7 }, ]; for (const transition of settlementToWildTransitions) { const srcLayer = map.layers[transition.src]; const tgtLayer = map.layers[transition.tgt]; // First and last settlement connect to 1 wild const firstSettlement = map.nodes.get(srcLayer.nodeIds[0]); expect(firstSettlement?.childIds.length).toBe(1); const lastSettlement = map.nodes.get(srcLayer.nodeIds[3]); expect(lastSettlement?.childIds.length).toBe(1); // Middle two settlements connect to 2 wilds for (let i = 1; i <= 2; i++) { const midSettlement = map.nodes.get(srcLayer.nodeIds[i]); expect(midSettlement?.childIds.length).toBe(2); for (const childId of midSettlement!.childIds) { expect(tgtLayer.nodeIds).toContain(childId); } } } }); it('should have all 3 wild nodes connect to End', () => { const map = generatePointCrawlMap(42); const lastWildLayer = map.layers[8]; const endNode = map.nodes.get('node-9-0'); for (const wildId of lastWildLayer.nodeIds) { const wildNode = map.nodes.get(wildId); expect(wildNode?.childIds).toEqual([endNode!.id]); } }); it('should have all nodes reachable from Start and can reach End', () => { const map = generatePointCrawlMap(123); const startId = 'node-0-0'; const endId = 'node-9-0'; for (const nodeId of map.nodes.keys()) { if (nodeId === startId || nodeId === endId) continue; expect(hasPath(map, startId, nodeId)).toBe(true); expect(hasPath(map, nodeId, endId)).toBe(true); } }); it('should not have crossing edges in wild→wild transitions', () => { const map = generatePointCrawlMap(12345); const wildToWildTransitions = [ { src: 1, tgt: 2 }, { src: 4, tgt: 5 }, { src: 7, tgt: 8 }, ]; for (const transition of wildToWildTransitions) { const srcLayer = map.layers[transition.src]; const tgtLayer = map.layers[transition.tgt]; // Collect edges as pairs of indices const edges: Array<{ srcIndex: number; tgtIndex: number }> = []; for (let s = 0; s < srcLayer.nodeIds.length; s++) { const srcNode = map.nodes.get(srcLayer.nodeIds[s]); for (const tgtId of srcNode!.childIds) { const t = tgtLayer.nodeIds.indexOf(tgtId); edges.push({ srcIndex: s, tgtIndex: t }); } } // Check for crossings for (let e1 = 0; e1 < edges.length; e1++) { for (let e2 = e1 + 1; e2 < edges.length; e2++) { const { srcIndex: s1, tgtIndex: t1 } = edges[e1]; const { srcIndex: s2, tgtIndex: t2 } = edges[e2]; if (s1 === s2) continue; if (t1 === t2) continue; const crosses = (s1 < s2 && t1 > t2) || (s1 > s2 && t1 < t2); expect(crosses).toBe(false); } } } }); it('should not have crossing edges in wild→settlement transitions', () => { const map = generatePointCrawlMap(12345); const wildToSettlementTransitions = [ { src: 2, tgt: 3 }, { src: 5, tgt: 6 }, ]; for (const transition of wildToSettlementTransitions) { const srcLayer = map.layers[transition.src]; const tgtLayer = map.layers[transition.tgt]; // Collect edges as pairs of indices const edges: Array<{ srcIndex: number; tgtIndex: number }> = []; for (let s = 0; s < srcLayer.nodeIds.length; s++) { const srcNode = map.nodes.get(srcLayer.nodeIds[s]); for (const tgtId of srcNode!.childIds) { const t = tgtLayer.nodeIds.indexOf(tgtId); edges.push({ srcIndex: s, tgtIndex: t }); } } // Check for crossings for (let e1 = 0; e1 < edges.length; e1++) { for (let e2 = e1 + 1; e2 < edges.length; e2++) { const { srcIndex: s1, tgtIndex: t1 } = edges[e1]; const { srcIndex: s2, tgtIndex: t2 } = edges[e2]; if (s1 === s2) continue; if (t1 === t2) continue; const crosses = (s1 < s2 && t1 > t2) || (s1 > s2 && t1 < t2); expect(crosses).toBe(false); } } } }); it('should not have crossing edges in settlement→wild transitions', () => { const map = generatePointCrawlMap(12345); const settlementToWildTransitions = [ { src: 3, tgt: 4 }, { src: 6, tgt: 7 }, ]; for (const transition of settlementToWildTransitions) { const srcLayer = map.layers[transition.src]; const tgtLayer = map.layers[transition.tgt]; // Collect edges as pairs of indices const edges: Array<{ srcIndex: number; tgtIndex: number }> = []; for (let s = 0; s < srcLayer.nodeIds.length; s++) { const srcNode = map.nodes.get(srcLayer.nodeIds[s]); for (const tgtId of srcNode!.childIds) { const t = tgtLayer.nodeIds.indexOf(tgtId); edges.push({ srcIndex: s, tgtIndex: t }); } } // Check for crossings for (let e1 = 0; e1 < edges.length; e1++) { for (let e2 = e1 + 1; e2 < edges.length; e2++) { const { srcIndex: s1, tgtIndex: t1 } = edges[e1]; const { srcIndex: s2, tgtIndex: t2 } = edges[e2]; if (s1 === s2) continue; if (t1 === t2) continue; const crosses = (s1 < s2 && t1 > t2) || (s1 > s2 && t1 < t2); expect(crosses).toBe(false); } } } }); it('should assign encounters to nodes', () => { const map = generatePointCrawlMap(456); let nodesWithEncounter = 0; for (const node of map.nodes.values()) { if (node.encounter) { nodesWithEncounter++; expect(node.encounter.name).toBeTruthy(); expect(node.encounter.description).toBeTruthy(); } } expect(nodesWithEncounter).toBeGreaterThan(0); }); });