| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 6 |
|
| 7 |
import { expect, test } from "bun:test"; |
| 8 |
import { BODY_R, circleBlocked, moveSpeed, RUN, slideCircle } from "./glide.ts"; |
| 9 |
|
| 10 |
const TILE = 80; |
| 11 |
|
| 12 |
const room = ["#####", "#...#", "#...#", "#...#", "#####"]; |
| 13 |
const solid = (col: number, row: number): boolean => |
| 14 |
col < 0 || row < 0 || row >= room.length || col >= room[0]!.length || room[row]![col] === "#"; |
| 15 |
|
| 16 |
const slide = (x: number, y: number, dx: number, dy: number) => |
| 17 |
slideCircle(x, y, dx, dy, BODY_R, solid, TILE); |
| 18 |
|
| 19 |
test("an unobstructed step is the step it was asked for", () => { |
| 20 |
const to = slide(200, 200, 12, -7); |
| 21 |
expect(to.x).toBeCloseTo(212, 9); |
| 22 |
expect(to.y).toBeCloseTo(193, 9); |
| 23 |
expect(slide(200, 200, 0, 0)).toEqual({ x: 200, y: 200 }); |
| 24 |
}); |
| 25 |
|
| 26 |
test("a wall keeps the motion along it: running into it still slides", () => { |
| 27 |
|
| 28 |
const to = slide(300, 160, 10, 10); |
| 29 |
expect(to.y).toBeCloseTo(170, 6); |
| 30 |
expect(to.x).toBeLessThanOrEqual(304); |
| 31 |
expect(to.x).toBeGreaterThan(300); |
| 32 |
expect(circleBlocked(to.x, to.y, BODY_R, solid, TILE)).toBe(false); |
| 33 |
}); |
| 34 |
|
| 35 |
test("a corner is one place, whichever axis is resolved first", () => { |
| 36 |
|
| 37 |
const diagonal = slide(280, 280, 40, 40); |
| 38 |
|
| 39 |
const alongX = slide(280, 280, 40, 0); |
| 40 |
const xThenY = slide(alongX.x, alongX.y, 0, 40); |
| 41 |
const alongY = slide(280, 280, 0, 40); |
| 42 |
const yThenX = slide(alongY.x, alongY.y, 40, 0); |
| 43 |
expect(xThenY.x).toBeCloseTo(diagonal.x, 3); |
| 44 |
expect(xThenY.y).toBeCloseTo(diagonal.y, 3); |
| 45 |
expect(yThenX.x).toBeCloseTo(diagonal.x, 3); |
| 46 |
expect(yThenX.y).toBeCloseTo(diagonal.y, 3); |
| 47 |
|
| 48 |
expect(diagonal.x).toBeGreaterThan(320 - BODY_R - 1.5); |
| 49 |
expect(diagonal.x).toBeLessThanOrEqual(320 - BODY_R); |
| 50 |
expect(diagonal.y).toBeGreaterThan(320 - BODY_R - 1.5); |
| 51 |
expect(diagonal.y).toBeLessThanOrEqual(320 - BODY_R); |
| 52 |
}); |
| 53 |
|
| 54 |
test("a body held against a wall stops moving instead of alternating", () => { |
| 55 |
|
| 56 |
|
| 57 |
|
| 58 |
let at = { x: 300, y: 200 }; |
| 59 |
const seen: number[] = []; |
| 60 |
for (let i = 0; i < 12; i++) { |
| 61 |
at = slide(at.x, at.y, 8.3, 0); |
| 62 |
seen.push(at.x); |
| 63 |
} |
| 64 |
const settled = seen.slice(4); |
| 65 |
expect(Math.max(...settled) - Math.min(...settled)).toBeLessThan(0.05); |
| 66 |
expect(Math.max(...settled)).toBeLessThanOrEqual(320 - BODY_R); |
| 67 |
}); |
| 68 |
|
| 69 |
test("a corner is a fixed point: pressed into it, nothing moves at all", () => { |
| 70 |
let at = { x: 300, y: 300 }; |
| 71 |
for (let i = 0; i < 6; i++) at = slide(at.x, at.y, 8.3, 8.3); |
| 72 |
const settled = slide(at.x, at.y, 8.3, 8.3); |
| 73 |
expect(settled.x).toBeCloseTo(at.x, 6); |
| 74 |
expect(settled.y).toBeCloseTo(at.y, 6); |
| 75 |
}); |
| 76 |
|
| 77 |
test("a step never ends inside a wall, from anywhere in the room", () => { |
| 78 |
for (let x = 96; x < 320 - 16; x += 7) |
| 79 |
for (let y = 96; y < 320 - 16; y += 7) |
| 80 |
for (const [dx, dy] of [ |
| 81 |
[30, 0], |
| 82 |
[-30, 0], |
| 83 |
[0, 30], |
| 84 |
[0, -30], |
| 85 |
[24, 24], |
| 86 |
[-24, 24], |
| 87 |
[24, -24], |
| 88 |
[-24, -24], |
| 89 |
] as const) { |
| 90 |
const to = slide(x, y, dx, dy); |
| 91 |
expect(circleBlocked(to.x, to.y, BODY_R, solid, TILE)).toBe(false); |
| 92 |
} |
| 93 |
}); |
| 94 |
|
| 95 |
test("speed is the one table both sides read", () => { |
| 96 |
expect(moveSpeed({})).toBe(RUN); |
| 97 |
expect(moveSpeed({ haste: true })).toBeCloseTo(RUN * 1.35, 9); |
| 98 |
expect(moveSpeed({ slow: true })).toBeCloseTo(RUN * 0.55, 9); |
| 99 |
expect(moveSpeed({ heavy: true })).toBeCloseTo(RUN * 0.86, 9); |
| 100 |
expect(moveSpeed({ stopped: true, haste: true })).toBe(0); |
| 101 |
}); |