1
0
Fork 0
polaris-task-force/src/lib/minigames/poker/views.ts
Z8MB1E 6968ee0abc feat(poker): add poker engine, evaluation and stats libs with tests
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-09-11 03:25:43 -04:00

85 lines
2.6 KiB
TypeScript

/**
* Projection helpers that turn the full table state into safe views.
*
* The full state (including every hole card and the deck order) must never
* leave the server; clients only ever receive these views. Hole cards are
* hidden except for the viewer's own seats and for live hands at showdown.
*/
import { getLegalActions } from "./betting";
import type {
Card,
PokerSeatId,
PokerTableState,
PrivatePlayerView,
PublicPlayerView,
PublicTableView,
} from "./types";
function sumCommitted(state: PokerTableState): number {
return state.players.reduce((total, player) => total + player.committed, 0);
}
function projectPlayer(
state: PokerTableState,
player: PokerTableState["players"][number],
viewerSeat: PokerSeatId | null,
): PublicPlayerView {
const isViewer = viewerSeat !== null && player.seatId === viewerSeat;
const reveal = (state.phase === "showdown" && !player.folded) || isViewer;
const holeCards: readonly Card[] | null = reveal && player.holeCards.length > 0
? player.holeCards
: null;
return {
seatId: player.seatId,
name: player.name,
stack: player.stack,
committed: player.committed,
totalCommitted: player.totalCommitted,
folded: player.folded,
allIn: player.allIn,
hasActed: player.hasActed,
eliminated: player.eliminated,
holeCards,
cardCount: player.holeCards.length,
};
}
/**
* Public view of the table. Pass `viewerSeat` to also reveal that seat's own
* hole cards; pass null for a fully spectator-safe view.
*/
export function buildPublicView(
state: PokerTableState,
viewerSeat: PokerSeatId | null,
): PublicTableView {
return {
players: state.players.map((player) => projectPlayer(state, player, viewerSeat)),
buttonSeat: state.buttonSeat,
smallBlindSeat: state.smallBlindSeat,
bigBlindSeat: state.bigBlindSeat,
street: state.street,
phase: state.phase,
potTotal: state.collectedPot + sumCommitted(state),
communityCards: state.communityCards,
currentBet: state.currentBet,
toActSeat: state.toActSeat,
handNumber: state.handNumber,
config: state.config,
showdown: state.showdown,
finalPlacements: state.finalPlacements,
};
}
/** Everything one seated player is allowed to know, including their options. */
export function buildPrivateView(state: PokerTableState, seatId: PokerSeatId): PrivatePlayerView {
const viewer = state.players[seatId];
const publicView = buildPublicView(state, seatId);
return {
...publicView,
viewerSeat: seatId,
yourHoleCards: viewer?.holeCards ?? [],
legalActions: getLegalActions(state, seatId),
yourTurn: state.phase === "betting" && state.toActSeat === seatId,
};
}