nordic-skating / mistral-medium-35-littlejs / files / test / cracks.test.ts
Branch mistral-medium-35-littlejs · default branch · all branches
cracks.test.ts
| 1 | import { describe, expect, it } from 'vitest' |
| 2 | import { type Point, crackNetwork, crackStar } from '../src/ice/cracks.js' |
| 3 | import { mulberry32 } from '../src/world/lake.js' |
| 4 | |
| 5 | const SIZE = 32 |
| 6 | const network = crackNetwork(mulberry32(2018), SIZE) |
| 7 | |
| 8 | /** Whether the stretch a-b crosses the stretch c-d somewhere inside both, not at an end of either. */ |
| 9 | function crosses(a: Point, b: Point, c: Point, d: Point): boolean { |
| 10 | const side = (p: Point, q: Point, r: Point): number => (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0]) |
| 11 | const eps = 1e-6 |
| 12 | return side(a, b, c) * side(a, b, d) < -eps && side(c, d, a) * side(c, d, b) < -eps |
| 13 | } |
| 14 | |
| 15 | describe('cracks', () => { |
| 16 | it('are the same cracks for the same numbers', () => { |
| 17 | expect(crackNetwork(mulberry32(2018), SIZE)).toEqual(network) |
| 18 | expect(crackNetwork(mulberry32(2019), SIZE)).not.toEqual(network) |
| 19 | }) |
| 20 | |
| 21 | it('are a few long ones, more between them, and many hairlines', () => { |
| 22 | const length = (points: Point[]): number => |
| 23 | points.slice(1).reduce((sum, p, i) => sum + Math.hypot(p[0] - points[i]![0], p[1] - points[i]![1]), 0) |
| 24 | const of = (rank: number): number[] => network.filter((c) => c.rank === rank).map((c) => length(c.points)) |
| 25 | expect(of(0).length).toBe(5) |
| 26 | expect(of(1).length).toBeGreaterThan(15) |
| 27 | expect(of(2).length).toBeGreaterThan(25) |
| 28 | const mean = (lengths: number[]): number => lengths.reduce((a, b) => a + b) / lengths.length |
| 29 | expect(mean(of(0))).toBeGreaterThan(15) |
| 30 | expect(mean(of(1))).toBeLessThan(mean(of(0))) |
| 31 | expect(mean(of(2))).toBeLessThan(mean(of(1))) |
| 32 | }) |
| 33 | |
| 34 | it('run nearly straight', () => { |
| 35 | for (const { points, rank } of network.filter((c) => c.rank < 2 && c.points.length > 20)) { |
| 36 | const end = points[points.length - 1]! |
| 37 | const straight = Math.hypot(end[0] - points[0]![0], end[1] - points[0]![1]) |
| 38 | const along = (points.length - 1) * 0.25 |
| 39 | // As the crow flies, most of the way it actually runs. |
| 40 | expect(straight / along).toBeGreaterThan(rank === 0 ? 0.8 : 0.6) |
| 41 | } |
| 42 | }) |
| 43 | |
| 44 | it('stop at one another and never cross', () => { |
| 45 | // Every stretch of every crack, with which crack it is of; and the sheet repeats, so |
| 46 | // each is tried against the others' copies in the sheets round it as well. |
| 47 | const stretches = network.flatMap((crack, which) => |
| 48 | crack.points.slice(1).map((p, i) => ({ a: crack.points[i]!, b: p, which }))) |
| 49 | let crossings = 0 |
| 50 | for (const one of stretches) { |
| 51 | for (const other of stretches) { |
| 52 | if (other.which <= one.which) continue |
| 53 | for (const ox of [-SIZE, 0, SIZE]) { |
| 54 | for (const oy of [-SIZE, 0, SIZE]) { |
| 55 | if (crosses(one.a, one.b, [other.a[0] + ox, other.a[1] + oy], [other.b[0] + ox, other.b[1] + oy])) crossings++ |
| 56 | } |
| 57 | } |
| 58 | } |
| 59 | } |
| 60 | // A later crack may be run through by nothing; an earlier one may, at the very start |
| 61 | // of a crack leaving it, be touched. Crossings proper there are none of, or next to. |
| 62 | expect(crossings).toBeLessThanOrEqual(3) |
| 63 | }) |
| 64 | |
| 65 | it('open as a star of arms from one point, in order outwards', () => { |
| 66 | const star = crackStar(mulberry32(7), 6) |
| 67 | expect(star.length).toBeGreaterThan(20) |
| 68 | expect(star.filter((s) => s.along === 0 && s.from[0] === 0 && s.from[1] === 0).length).toBeGreaterThanOrEqual(2) |
| 69 | let furthest = 0 |
| 70 | for (const { from, to, along } of star) { |
| 71 | expect(Math.hypot(to[0] - from[0], to[1] - from[1])).toBeCloseTo(0.25, 5) |
| 72 | expect(along).toBeGreaterThanOrEqual(0) |
| 73 | furthest = Math.max(furthest, Math.hypot(to[0], to[1])) |
| 74 | } |
| 75 | expect(furthest).toBeGreaterThan(2) |
| 76 | expect(furthest).toBeLessThan(7) |
| 77 | }) |
| 78 | }) |