Files
ftcandClaude Opus 5 fddab60527 M1: ядро правил дурака (чистый TypeScript, без движка)
Детерминированный конечный автомат: apply(state, action) -> {state, events}.
Весь набор правил из GAMEPLAY.md проверяется headless-тестами.

- карты: Suit/Rank/Card (флайвейт, 36 штук), колода
- GameState + клонирование и сериализация в JSON
- действия (attack/defend/take/pass) и события для слоя отображения
- раздача, выбор первого атакующего, лимиты подкидывания,
  взятие карт, добор, выбывание игроков, определение дурака
- 64 теста, strict typecheck

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 10:38:49 +03:00

55 lines
1.2 KiB
TypeScript

/**
* Card ranks present in a 36-card deck. The numeric values are the comparison
* order, so `a > b` is a valid "beats within suit" test.
*/
export enum Rank {
Six = 6,
Seven = 7,
Eight = 8,
Nine = 9,
Ten = 10,
Jack = 11,
Queen = 12,
King = 13,
Ace = 14,
}
export const ALL_RANKS: readonly Rank[] = [
Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten,
Rank.Jack, Rank.Queen, Rank.King, Rank.Ace,
];
/** Short label, as printed on the card corner. */
const RANK_LABELS: Record<Rank, string> = {
[Rank.Six]: '6',
[Rank.Seven]: '7',
[Rank.Eight]: '8',
[Rank.Nine]: '9',
[Rank.Ten]: '10',
[Rank.Jack]: 'J',
[Rank.Queen]: 'Q',
[Rank.King]: 'K',
[Rank.Ace]: 'A',
};
/** Rank names as used by the card art file names. */
const RANK_NAMES: Record<Rank, string> = {
[Rank.Six]: '6',
[Rank.Seven]: '7',
[Rank.Eight]: '8',
[Rank.Nine]: '9',
[Rank.Ten]: '10',
[Rank.Jack]: 'jack',
[Rank.Queen]: 'queen',
[Rank.King]: 'king',
[Rank.Ace]: 'ace',
};
export function rankLabel(rank: Rank): string {
return RANK_LABELS[rank];
}
export function rankName(rank: Rank): string {
return RANK_NAMES[rank];
}