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>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/** The four suits of a standard deck. */
|
||||
export enum Suit {
|
||||
Clubs = 0,
|
||||
Diamonds = 1,
|
||||
Hearts = 2,
|
||||
Spades = 3,
|
||||
}
|
||||
|
||||
export const ALL_SUITS: readonly Suit[] = [Suit.Clubs, Suit.Diamonds, Suit.Hearts, Suit.Spades];
|
||||
|
||||
const SUIT_SYMBOLS: Record<Suit, string> = {
|
||||
[Suit.Clubs]: '\u2663',
|
||||
[Suit.Diamonds]: '\u2666',
|
||||
[Suit.Hearts]: '\u2665',
|
||||
[Suit.Spades]: '\u2660',
|
||||
};
|
||||
|
||||
/** Suit names as used by the card art file names. */
|
||||
const SUIT_NAMES: Record<Suit, string> = {
|
||||
[Suit.Clubs]: 'clubs',
|
||||
[Suit.Diamonds]: 'diamonds',
|
||||
[Suit.Hearts]: 'hearts',
|
||||
[Suit.Spades]: 'spades',
|
||||
};
|
||||
|
||||
export function suitSymbol(suit: Suit): string {
|
||||
return SUIT_SYMBOLS[suit];
|
||||
}
|
||||
|
||||
export function suitName(suit: Suit): string {
|
||||
return SUIT_NAMES[suit];
|
||||
}
|
||||
|
||||
export function isRedSuit(suit: Suit): boolean {
|
||||
return suit === Suit.Diamonds || suit === Suit.Hearts;
|
||||
}
|
||||
Reference in New Issue
Block a user