v0.4.1 - bug fixes. initial d12 rolls determine what player in which seat.

This commit is contained in:
Jesse
2026-08-13 15:28:53 -04:00
parent 49f8504b05
commit a7221dcf20
22 changed files with 736 additions and 237 deletions
+26 -3
View File
@@ -543,12 +543,19 @@ export type GameState = {
rngState: number;
players: Player[];
/**
* Who is sitting where: `seating[seat] = player`.
* Who is sitting where: `seating[seat] = player`, seat 0 at the WESTERN end of the chain.
*
* The identity mapping in every game today, which is what makes the seat/player split
* behaviour-neutral. Employee Rotation would rotate this array and nothing else.
* Decided at setup by §4.4's D12 (`openingRolls.division`) — a real permutation, not the identity,
* except in solitaire where one player means one seat. Employee Rotation would rotate this array
* and nothing else.
*/
seating: PlayerIndex[];
/**
* The two opening D12s, per player, kept so a client can show the rolls rather than only their
* outcome — it is the game's first moment of drama (`lobby-and-sessions.md` §4). Indexed by
* PLAYER, since that is who rolls.
*/
openingRolls: { division: number[]; superintendent: number[] };
division: Division;
officeAreas: Map<SeatIndex, OfficeArea>;
trays: Map<TrayId, CrewTray>;
@@ -623,6 +630,22 @@ export function playerAtSeat(state: GameState, seat: SeatIndex): PlayerIndex {
}
/** Where this player is sitting, and therefore which Office Area is theirs. */
/**
* The player `n` seats to the LEFT of this one, wrapping round the table.
*
* Acting order, the deal and the Fedora are all "starting here and proceeding left" (Gap 1, §4.7,
* §5), which is a statement about the physical chain of Offices — so it is seat arithmetic, not
* player arithmetic. All three used to do `(player + n) % players`, which was the same thing only
* while seating was the identity mapping. It stopped being that when §4.4's D12 started deciding
* who sits where.
*
* "Left" is increasing seat index, i.e. eastward along the chain, matching what the shift-change
* tests have always asserted.
*/
export function playerLeftOf(state: GameState, player: PlayerIndex, n = 1): PlayerIndex {
return playerAtSeat(state, (seatOf(state, player) + n) % state.seating.length);
}
export function seatOf(state: GameState, player: PlayerIndex): SeatIndex {
const seat = state.seating.indexOf(player);
if (seat < 0) throw new Error(`player ${player} is not seated`);