Newer
Older
rpg / packages / engine / src / map / __tests__ / pathfinding.test.ts
import { describe, it, expect } from 'vitest';
import { findPath, type Grid } from '../pathfinding';

function makeGrid(width: number, height: number, walls: string[] = []): Grid {
    const wallSet = new Set(walls);
    return {
        width,
        height,
        isWalkable: (x, y) => !wallSet.has(`${x},${y}`)
    };
}

describe('A*', () => {
    it('прямой путь по пустой сетке', () => {
        const path = findPath(makeGrid(10, 10), { x: 0, y: 0 }, { x: 3, y: 0 });
        expect(path).toEqual([
            { x: 1, y: 0 },
            { x: 2, y: 0 },
            { x: 3, y: 0 }
        ]);
    });

    it('обходит стену', () => {
        const grid = makeGrid(5, 5, ['1,0', '1,1', '1,2']);
        const path = findPath(grid, { x: 0, y: 0 }, { x: 2, y: 0 });
        expect(path).not.toBeNull();
        expect(path!.some((p) => p.x === 1 && (p.y === 0 || p.y === 1 || p.y === 2))).toBe(false);
        expect(path![path!.length - 1]).toEqual({ x: 2, y: 0 });
    });

    it('цель заблокирована — null', () => {
        const grid = makeGrid(5, 5, ['2,2']);
        expect(findPath(grid, { x: 0, y: 0 }, { x: 2, y: 2 })).toBeNull();
    });

    it('цель вне сетки — null', () => {
        const grid = makeGrid(5, 5);
        expect(findPath(grid, { x: 0, y: 0 }, { x: 9, y: 9 })).toBeNull();
    });

    it('старт == цель — пустой путь', () => {
        const grid = makeGrid(5, 5);
        expect(findPath(grid, { x: 2, y: 2 }, { x: 2, y: 2 })).toEqual([]);
    });

    it('диагональ без среза углов', () => {
        // Стена (1,0) срезает угол диагонали (0,0)->(1,1) — она запрещена,
        // путь идёт в обход через (0,1).
        const grid = makeGrid(5, 5, ['1,0']);
        const path = findPath(grid, { x: 0, y: 0 }, { x: 1, y: 1 }, true);
        expect(path).not.toBeNull();
        expect(path![0]).toEqual({ x: 0, y: 1 });
    });

    it('угол срезан со всех сторон — пути нет', () => {
        // Из (0,0) заблокированы оба ортогональных соседа: без среза углов
        // ни шага, ни диагонали сделать нельзя.
        const grid = makeGrid(5, 5, ['1,0', '0,1']);
        expect(findPath(grid, { x: 0, y: 0 }, { x: 2, y: 2 }, true)).toBeNull();
    });

    it('диагональ разрешена, когда соседи свободны', () => {
        const grid = makeGrid(5, 5);
        const path = findPath(grid, { x: 0, y: 0 }, { x: 1, y: 1 }, true);
        expect(path).toEqual([{ x: 1, y: 1 }]);
    });
});