1import { describe, expect, it } from 'vitest';2import { ELO_FLOOR, ELO_START, rateSim } from '../src/rating';34describe('rateSim', () => {5 it('transfers rating from loser to winner symmetrically at equal ratings', () => {6 const out = rateSim([7 { scriptId: 'a', score: 200, rating: ELO_START },8 { scriptId: 'b', score: 100, rating: ELO_START },9 ]);10 const a = out.find((o) => o.scriptId === 'a')!;11 const b = out.find((o) => o.scriptId === 'b')!;12 expect(a.delta).toBe(12); // K/2 at equal ratings13 expect(b.delta).toBe(-12);14 expect(a.place).toBe(1);15 expect(b.place).toBe(2);16 expect(a.wins).toBe(1);17 expect(b.losses).toBe(1);18 });1920 it('gives the underdog more for an upset than the favorite', () => {21 const upset = rateSim([22 { scriptId: 'dog', score: 200, rating: 800 },23 { scriptId: 'fav', score: 100, rating: 1200 },24 ]);25 const expectedWin = rateSim([26 { scriptId: 'fav', score: 200, rating: 1200 },27 { scriptId: 'dog', score: 100, rating: 800 },28 ]);29 const dogGain = upset.find((o) => o.scriptId === 'dog')!.delta;30 const favGain = expectedWin.find((o) => o.scriptId === 'fav')!.delta;31 expect(dogGain).toBeGreaterThan(favGain);32 });3334 it('ties move nothing at equal ratings and share the better place', () => {35 const out = rateSim([36 { scriptId: 'a', score: 150, rating: ELO_START },37 { scriptId: 'b', score: 150, rating: ELO_START },38 { scriptId: 'c', score: 50, rating: ELO_START },39 ]);40 const a = out.find((o) => o.scriptId === 'a')!;41 const b = out.find((o) => o.scriptId === 'b')!;42 const c = out.find((o) => o.scriptId === 'c')!;43 expect(a.place).toBe(1);44 expect(b.place).toBe(1);45 expect(c.place).toBe(3);46 expect(a.delta).toBe(b.delta);47 expect(a.wins).toBe(1); // beat c only48 expect(c.losses).toBe(2);49 });5051 it('multiplayer deltas roughly conserve rating', () => {52 const out = rateSim([53 { scriptId: 'a', score: 400, rating: 1100 },54 { scriptId: 'b', score: 300, rating: 1000 },55 { scriptId: 'c', score: 200, rating: 950 },56 { scriptId: 'd', score: 100, rating: 900 },57 ]);58 const total = out.reduce((s, o) => s + o.delta, 0);59 expect(Math.abs(total)).toBeLessThanOrEqual(out.length); // rounding only60 });6162 it('never drops a rating below the floor', () => {63 const out = rateSim([64 { scriptId: 'a', score: 200, rating: 2000 },65 { scriptId: 'b', score: 100, rating: ELO_FLOOR + 1 },66 ]);67 const b = out.find((o) => o.scriptId === 'b')!;68 expect(b.ratingAfter).toBeGreaterThanOrEqual(ELO_FLOOR);69 expect(b.ratingAfter - (ELO_FLOOR + 1)).toBe(b.delta); // delta matches the clamp70 });71});