Files
rogue/src/engine/__tests__/pathfinding.test.ts
2026-01-06 20:58:53 +11:00

84 lines
3.4 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { findPathAStar } from '../world/pathfinding';
import { type World } from '../../core/types';
import { TileType } from '../../core/terrain';
describe('Pathfinding', () => {
const createTestWorld = (width: number, height: number, tileType: number = TileType.EMPTY): World => ({
width,
height,
tiles: new Array(width * height).fill(tileType),
actors: new Map(),
exit: { x: 0, y: 0 }
});
it('should find a path between two reachable points', () => {
const world = createTestWorld(10, 10);
const seen = new Uint8Array(100).fill(1);
const path = findPathAStar(world, seen, { x: 0, y: 0 }, { x: 0, y: 3 });
expect(path.length).toBe(4); // 0,0 -> 0,1 -> 0,2 -> 0,3
expect(path[0]).toEqual({ x: 0, y: 0 });
expect(path[3]).toEqual({ x: 0, y: 3 });
});
it('should return empty array if target is a wall', () => {
const world = createTestWorld(10, 10);
world.tiles[30] = TileType.WALL; // Wall at 0,3
const seen = new Uint8Array(100).fill(1);
const path = findPathAStar(world, seen, { x: 0, y: 0 }, { x: 0, y: 3 });
expect(path).toEqual([]);
});
it('should return empty array if no path exists', () => {
const world = createTestWorld(10, 10);
// Create a wall blockage
for(let x=0; x<10; x++) world.tiles[10 + x] = TileType.WALL;
const seen = new Uint8Array(100).fill(1);
const path = findPathAStar(world, seen, { x: 0, y: 0 }, { x: 0, y: 5 });
expect(path).toEqual([]);
});
it('should respect ignoreBlockedTarget option', () => {
const world = createTestWorld(10, 10);
// Place an actor at target
world.actors.set(1, { id: 1, pos: { x: 0, y: 3 }, type: 'rat', category: 'combatant' } as any);
const seen = new Uint8Array(100).fill(1);
// Without option, it should be blocked (because actor is there)
// Wait, default pathfinding might treat actors as blocking unless specified.
// Let's check `isBlocked` usage in `pathfinding.ts`.
// It calls `isBlocked` which checks actors.
// However, findPathAStar has logic:
// if (!options.ignoreBlockedTarget && isBlocked(w, end.x, end.y, options.em)) return [];
const pathBlocked = findPathAStar(world, seen, { x: 0, y: 0 }, { x: 0, y: 3 });
expect(pathBlocked).toEqual([]);
const pathIgnored = findPathAStar(world, seen, { x: 0, y: 0 }, { x: 0, y: 3 }, { ignoreBlockedTarget: true });
expect(pathIgnored.length).toBeGreaterThan(0);
expect(pathIgnored[pathIgnored.length - 1]).toEqual({ x: 0, y: 3 });
});
it('should respect ignoreSeen option', () => {
const world = createTestWorld(10, 10);
const seen = new Uint8Array(100).fill(0); // Nothing seen
// Without ignoreSeen, should fail because target/path is unseen
const pathUnseen = findPathAStar(world, seen, { x: 0, y: 0 }, { x: 0, y: 3 });
expect(pathUnseen).toEqual([]);
// With ignoreSeen, should succeed
const pathSeenIgnored = findPathAStar(world, seen, { x: 0, y: 0 }, { x: 0, y: 3 }, { ignoreSeen: true });
expect(pathSeenIgnored.length).toBe(4);
});
});