slop game oder so

This commit is contained in:
2026-09-24 17:57:54 +02:00
commit 4c997eee21
166 changed files with 14976 additions and 0 deletions
+110
View File
@@ -0,0 +1,110 @@
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 });
});
});