import { Card } from '../cards/Card'; import { Suit } from '../cards/Suit'; import { Action } from '../rules/Action'; import { GameState, Phase, PlayerId, TableSlot } from '../rules/GameState'; import { currentActor, legalActions, seatOf } from '../rules/DurakRules'; /** What one player can see about another. */ export interface OpponentView { readonly id: PlayerId; readonly handSize: number; readonly out: boolean; } /** * Everything a participant may legitimately know, and nothing else. * * Bots are handed one of these rather than the `GameState`, which makes * cheating structurally impossible instead of a matter of discipline. It is * also, not coincidentally, the shape a network protocol would send to a client. */ export interface PlayerView { readonly me: PlayerId; readonly hand: readonly Card[]; /** Opponents in clockwise order, starting with the seat after `me`. */ readonly opponents: readonly OpponentView[]; readonly table: readonly TableSlot[]; readonly trumpSuit: Suit; /** The face-up card that set the trump. Visible all game, even once drawn. */ readonly trumpCard: Card; readonly deckRemaining: number; /** True while the trump card is still the bottom card of the draw pile. */ readonly trumpCardStillInDeck: boolean; readonly discardCount: number; readonly phase: Phase; readonly turnNumber: number; readonly attacker: PlayerId; readonly defender: PlayerId; readonly defenderTook: boolean; /** True when the game is waiting on this player. */ readonly onMove: boolean; /** Everything this player may do right now. Empty unless `onMove`. */ readonly legalActions: readonly Action[]; } export function projectView(state: GameState, player: PlayerId): PlayerView { const seat = seatOf(state, player); const count = state.players.length; const opponents: OpponentView[] = []; for (let i = 1; i < count; i++) { const other = state.players[(seat + i) % count]; opponents.push({ id: other.id, handSize: other.hand.length, out: other.out }); } return { me: player, hand: state.players[seat].hand.slice(), opponents, table: state.table.map((s) => ({ attack: s.attack, defence: s.defence })), trumpSuit: state.trumpSuit, trumpCard: state.trumpCard, deckRemaining: state.deck.length, trumpCardStillInDeck: state.deck.length > 0, discardCount: state.discard.length, phase: state.phase, turnNumber: state.turnNumber, attacker: state.players[state.attacker].id, defender: state.players[state.defender].id, defenderTook: state.defenderTook, onMove: currentActor(state) === player, legalActions: legalActions(state, player), }; } /** Convenience: is this card a trump, from a view's point of view. */ export function isTrump(view: PlayerView, card: Card): boolean { return card.suit === view.trumpSuit; }