UNPKG

@nodots-llc/backgammon-types

Version:
216 lines (196 loc) 7.04 kB
import { BackgammonBoard } from './board' import { BackgammonMoveOrigin } from './checkercontainer' import { BackgammonCube } from './cube' import { IntegerRange } from './generics' import { BackgammonPlay, BackgammonPlayDoubled, BackgammonPlayMoving, BackgammonPlayRolled, } from './play' import { BackgammonPlayer, BackgammonPlayerActive, BackgammonPlayerDoubled, BackgammonPlayerInactive, BackgammonPlayerMoving, BackgammonPlayerRolled, BackgammonPlayerRolledForStart, BackgammonPlayerRolling, BackgammonPlayerWinner, BackgammonPlayers, } from './player' // -------------------------------------------------------------------------------------- // DESIGN NOTE: Game State Modeling - "playing"/"played" vs. "moving"/"moved" // // The current model uses "moving" and "moved" states, which are primarily driven by UI events // (e.g., when a user clicks or drags a checker). However, in the rules of backgammon, a player's // turn (a "play") consists of a sequence of moves (2 or 4, depending on the dice roll), and the // turn is only complete when the player has finished all possible/legal moves and explicitly // indicates they are done (e.g., by clicking a "Done" button). // // A more accurate and robust model would use "playing" and "played" states: // - "playing": The player is in the process of making their play (their turn), which may consist // of multiple moves. The player remains in this state until they indicate completion. // - "played": The player has finished their play (clicked "Done"), and the game can validate // the play and transition to the next state (e.g., next player's turn, or game end). // // This approach: // - Aligns the state machine with the actual rules and flow of backgammon, not just UI actions. // - Makes it easier to reason about game logic, validation, and undo/redo functionality. // - Separates UI-driven pseudo-states from rule-driven game states, leading to a cleaner design. // // For now, the code retains "moving" and "moved" for compatibility, but a future refactor should // implement "playing" and "played" as described above. // -------------------------------------------------------------------------------------- export type Latitude = 'north' | 'south' export type Longitude = 'east' | 'west' export type BackgammonColor = 'black' | 'white' export type BackgammonMoveDirection = 'clockwise' | 'counterclockwise' export type BackgammonPips = IntegerRange<1, 167> export const MAX_PIP_COUNT = 167 export const CHECKERS_PER_PLAYER = 15 export type BackgammonGameStateKind = | 'rolling-for-start' | 'rolled-for-start' | 'rolling' | 'rolled' | 'doubling' | 'doubled' | 'moving' | 'moved' | 'completed' interface BaseGame { id: string players: BackgammonPlayers board: BackgammonBoard cube: BackgammonCube winner?: BackgammonPlayer activeColor?: BackgammonColor activePlay?: BackgammonPlay activePlayer?: BackgammonPlayer inactivePlayer?: BackgammonPlayer } interface Game extends BaseGame { stateKind: BackgammonGameStateKind } export type BackgammonGameRollingForStart = Game & { stateKind: 'rolling-for-start' } export type BackgammonGameRolledForStart = Game & { stateKind: 'rolled-for-start' activeColor: BackgammonColor activePlayer: BackgammonPlayerRolledForStart inactivePlayer: BackgammonPlayerInactive } export type BackgammonGameRolling = Game & { stateKind: 'rolling' activeColor: BackgammonColor activePlayer: BackgammonPlayerRolling inactivePlayer: BackgammonPlayerInactive } // Changed activePlayer to BackgammonPlayerRolled export type BackgammonGameRolled = Game & { stateKind: 'rolled' activeColor: BackgammonColor activePlayer: BackgammonPlayerRolled inactivePlayer: BackgammonPlayerInactive activePlay: BackgammonPlayRolled } export type BackgammonGameDoubling = Game & { stateKind: 'doubling' activeColor: BackgammonColor activePlay: BackgammonPlayDoubled activePlayer: BackgammonPlayerDoubled inactivePlayer: BackgammonPlayerInactive } export type BackgammonGameDoubled = Game & { stateKind: 'doubled' activeColor: BackgammonColor activePlay: BackgammonPlayDoubled activePlayer: BackgammonPlayerDoubled inactivePlayer: BackgammonPlayerInactive } export type BackgammonGameMoving = Game & { stateKind: 'moving' activeColor: BackgammonColor activePlay: BackgammonPlayMoving activePlayer: BackgammonPlayerMoving inactivePlayer: BackgammonPlayerInactive } export type BackgammonGameMoved = Game & { stateKind: 'moved' activeColor: BackgammonColor activePlay: BackgammonPlayMoving activePlayer: BackgammonPlayerMoving inactivePlayer: BackgammonPlayerInactive } export type BackgammonGameCompleted = Game & { stateKind: 'completed' winner: BackgammonPlayerWinner } export type BackgammonGame = | BackgammonGameRollingForStart | BackgammonGameRolledForStart | BackgammonGameRolling | BackgammonGameRolled | BackgammonGameDoubled | BackgammonGameMoving | BackgammonGameMoved | BackgammonGameCompleted export interface GameProps { players: BackgammonPlayers board?: BackgammonBoard cube?: BackgammonCube } export interface GameClass { id: string stateKind: BackgammonGameStateKind players: BackgammonPlayers board: BackgammonBoard cube: BackgammonCube activeColor: BackgammonColor activePlay: BackgammonPlay activePlayer: BackgammonPlayerActive inactivePlayer: BackgammonPlayerInactive initialize: ( players: BackgammonPlayers, id?: string, stateKind?: BackgammonGameStateKind, board?: BackgammonBoard, cube?: BackgammonCube, activePlay?: BackgammonPlay, activeColor?: BackgammonColor, activePlayer?: BackgammonPlayerActive, inactivePlayer?: BackgammonPlayerInactive, origin?: BackgammonMoveOrigin ) => BackgammonGame rollForStart: ( game: BackgammonGameRollingForStart ) => BackgammonGameRolledForStart roll: (game: BackgammonGameRolledForStart) => BackgammonGameRolled /** * This is a pseudo state transition. The user transitions into a "moving" state when they * click on a checker (rather than the cube). But the instant they click the * checker they are in a moved state. */ toMoving: ( game: BackgammonGameRolled | BackgammonGameDoubled ) => BackgammonGameMoving /** * This is another pseudo state transition. Argument for this is weaker. */ toDoubling: (game: BackgammonGameRolled) => BackgammonGameDoubling double: (game: BackgammonGameDoubling) => BackgammonGameDoubled move: ( game: BackgammonGameMoving | BackgammonGameRolled, origin: BackgammonMoveOrigin ) => BackgammonGameMoved getActivePlayer: (game: BackgammonGame) => BackgammonPlayerActive getInactivePlayer: (game: BackgammonGame) => BackgammonPlayerInactive getPlayersForColor: ( players: BackgammonPlayers, color: BackgammonColor ) => [BackgammonPlayerActive, BackgammonPlayerInactive] sanityCheckMovingGame: (game: BackgammonGame) => BackgammonGameMoving | false }