111 lines
3.6 KiB
JavaScript
111 lines
3.6 KiB
JavaScript
import { describe, expect, it } from 'vitest';
|
|
|
|
import { distance, length } from '../../app/static/js/logic/math.js';
|
|
import {
|
|
combine,
|
|
encirclePoint,
|
|
flankPoint,
|
|
flee,
|
|
keepDistance,
|
|
seek,
|
|
separation,
|
|
steerTowards,
|
|
} from '../../app/static/js/logic/steering.js';
|
|
|
|
const close = (actual, expected) => {
|
|
expect(actual.x).toBeCloseTo(expected.x);
|
|
expect(actual.y).toBeCloseTo(expected.y);
|
|
};
|
|
|
|
describe('seek / flee', () => {
|
|
it('bewegt sich mit voller Geschwindigkeit zum Ziel bzw. davon weg', () => {
|
|
close(seek({ x: 0, y: 0 }, { x: 10, y: 0 }, 5), { x: 5, y: 0 });
|
|
close(flee({ x: 0, y: 0 }, { x: 0, y: 10 }, 3), { x: 0, y: -3 });
|
|
});
|
|
|
|
it('steht still, wenn es bereits am Ziel ist', () => {
|
|
expect(seek({ x: 4, y: 4 }, { x: 4, y: 4 }, 100)).toEqual({ x: 0, y: 0 });
|
|
});
|
|
});
|
|
|
|
describe('separation', () => {
|
|
it('stößt nahe Nachbarn ab und ignoriert entfernte', () => {
|
|
const self = { x: 0, y: 0 };
|
|
const push = separation(self, [self, { x: 10, y: 0 }, { x: 500, y: 0 }], 20);
|
|
expect(push.x).toBeLessThan(0);
|
|
expect(push.y).toBeCloseTo(0);
|
|
});
|
|
|
|
it('trennt exakt übereinanderliegende Gegner ohne NaN', () => {
|
|
const push = separation({ x: 5, y: 5 }, [{ x: 5, y: 5 }], 20);
|
|
expect(Number.isFinite(push.x) && Number.isFinite(push.y)).toBe(true);
|
|
expect(length(push)).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('drückt stärker, je näher der Nachbar ist', () => {
|
|
const near = separation({ x: 0, y: 0 }, [{ x: 2, y: 0 }], 20);
|
|
const far = separation({ x: 0, y: 0 }, [{ x: 18, y: 0 }], 20);
|
|
expect(length(near)).toBeGreaterThan(length(far));
|
|
});
|
|
});
|
|
|
|
describe('keepDistance', () => {
|
|
const band = { min: 200, max: 300 };
|
|
const target = { x: 0, y: 0 };
|
|
|
|
it('nähert sich an, wenn es zu weit weg ist', () => {
|
|
const v = keepDistance({ x: 500, y: 0 }, target, band, 10, 1);
|
|
close(v, { x: -10, y: 0 });
|
|
});
|
|
|
|
it('weicht zurück, wenn es zu nah ist', () => {
|
|
const v = keepDistance({ x: 100, y: 0 }, target, band, 10, 1);
|
|
close(v, { x: 10, y: 0 });
|
|
});
|
|
|
|
it('weicht im Wohlfühlbereich seitlich aus (senkrecht zur Linie)', () => {
|
|
const cw = keepDistance({ x: 250, y: 0 }, target, band, 10, 1, 0.5);
|
|
const ccw = keepDistance({ x: 250, y: 0 }, target, band, 10, -1, 0.5);
|
|
expect(cw.x).toBeCloseTo(0);
|
|
expect(Math.abs(cw.y)).toBeCloseTo(5);
|
|
expect(ccw.y).toBeCloseTo(-cw.y);
|
|
});
|
|
});
|
|
|
|
describe('flankPoint / encirclePoint', () => {
|
|
it('liegt im gewünschten Abstand seitlich versetzt', () => {
|
|
const target = { x: 0, y: 0 };
|
|
const enemy = { x: 100, y: 0 };
|
|
const p = flankPoint(target, enemy, Math.PI / 2, 50);
|
|
expect(distance(p, target)).toBeCloseTo(50);
|
|
close(p, { x: 0, y: 50 });
|
|
const other = flankPoint(target, enemy, -Math.PI / 2, 50);
|
|
close(other, { x: 0, y: -50 });
|
|
});
|
|
|
|
it('verteilt Umzingelungsplätze auf einem Kreis', () => {
|
|
const target = { x: 10, y: 10 };
|
|
close(encirclePoint(target, 0, 100), { x: 110, y: 10 });
|
|
close(encirclePoint(target, Math.PI, 100), { x: -90, y: 10 });
|
|
});
|
|
});
|
|
|
|
describe('combine / steerTowards', () => {
|
|
it('gewichtet Anteile und begrenzt auf die Höchstgeschwindigkeit', () => {
|
|
const v = combine(
|
|
[
|
|
{ vector: { x: 10, y: 0 }, weight: 1 },
|
|
{ vector: { x: 0, y: 10 }, weight: 2 },
|
|
],
|
|
15,
|
|
);
|
|
expect(length(v)).toBeCloseTo(15);
|
|
expect(v.y).toBeGreaterThan(v.x);
|
|
});
|
|
|
|
it('nähert sich der Wunschgeschwindigkeit schrittweise an', () => {
|
|
close(steerTowards({ x: 0, y: 0 }, { x: 10, y: -10 }, 0.25), { x: 2.5, y: -2.5 });
|
|
close(steerTowards({ x: 3, y: 3 }, { x: 10, y: -10 }, 1), { x: 10, y: -10 });
|
|
});
|
|
});
|