AT Mot — a bilingual (EN/FR) daily word game native to the AT Protocol.
1import { describe, it, expect } from 'vitest';
2import { scoreGuess, isWin } from '../src/engine/scoring.js';
3import type { TileState } from '../src/engine/types.js';
4
5const C: TileState = 'correct';
6const P: TileState = 'present';
7const A: TileState = 'absent';
8
9describe('scoreGuess', () => {
10 it('all correct on exact match', () => {
11 expect(scoreGuess('CRANE', 'CRANE')).toEqual([C, C, C, C, C]);
12 expect(isWin(scoreGuess('CRANE', 'CRANE'))).toBe(true);
13 });
14
15 it('marks present for right letter wrong spot', () => {
16 // answer PLANT, guess PETAL
17 // idx0 P==P correct; E absent; T present; A present; L present
18 expect(scoreGuess('PETAL', 'PLANT')).toEqual([C, A, P, P, P]);
19 });
20
21 it('handles duplicate letters around exact matches', () => {
22 // answer THREE = T H R E E; guess EERIE = E E R I E
23 // idx2 R correct, idx4 E correct; remaining E:1
24 // idx0 E -> present (consumes last E); idx1 E -> absent; idx3 I absent
25 expect(scoreGuess('EERIE', 'THREE')).toEqual([P, A, C, A, C]);
26 });
27
28 it('classic duplicate case: guess SPEED vs answer ERASE', () => {
29 // ERASE: E R A S E (E at 0 and 4, one S at 3)
30 // SPEED: S P E E D
31 // idx0 S: answer has S(idx3), not exact -> present
32 // idx1 P: absent
33 // idx2 E: present (answer has E x2)
34 // idx3 E: present (2nd E) -> now 0 E remaining
35 // idx4 D: absent
36 expect(scoreGuess('SPEED', 'ERASE')).toEqual([P, A, P, P, A]);
37 });
38
39 it('throws on wrong-length input', () => {
40 expect(() => scoreGuess('ABC', 'ABCDE')).toThrow();
41 });
42});