A settled move may be taken back as a draft while the round still waits on others
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jm2auWk6RP71CjaAb4FMoG
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
af25b672f1
commit
59494aa29f
+2
-1
@@ -142,7 +142,8 @@ if it were never there.
|
|||||||
- `validate(state, seat, input)`: a reason to refuse a move, shown to the player.
|
- `validate(state, seat, input)`: a reason to refuse a move, shown to the player.
|
||||||
- `seatsFor(options)`: fewer seats for some table options.
|
- `seatsFor(options)`: fewer seats for some table options.
|
||||||
- `settled(input)`: whether an input is final. A game with drafts answers
|
- `settled(input)`: whether an input is final. A game with drafts answers
|
||||||
false until the player says it is done; a draft may be replaced, and the
|
false until the player says it is done; a draft may be replaced (and a
|
||||||
|
settled move taken back as a draft) until the round resolves, and the
|
||||||
round waits for every seat's settled input. A seat's view carries its
|
round waits for every seat's settled input. A seat's view carries its
|
||||||
own input in waiting (`yourInput`), so a page reopened shows the draft,
|
own input in waiting (`yourInput`), so a page reopened shows the draft,
|
||||||
and its input in the last round it played (`yourLastInput`), so the page
|
and its input in the last round it played (`yourLastInput`), so the page
|
||||||
|
|||||||
@@ -62,7 +62,9 @@ describe('rooms', () => {
|
|||||||
expect(room.resolved).toBe(1);
|
expect(room.resolved).toBe(1);
|
||||||
expect(room.state!.rounds[0].picks).toEqual({ A: 3, B: 2 });
|
expect(room.state!.rounds[0].picks).toEqual({ A: 3, B: 2 });
|
||||||
rooms.submit(room, bo, { pick: 1, final: true }, 1);
|
rooms.submit(room, bo, { pick: 1, final: true }, 1);
|
||||||
expect(() => rooms.submit(room, bo, { pick: 2 }, 1)).toThrow(/not your move/);
|
expect(() => rooms.submit(room, bo, { pick: 2, final: true }, 1)).toThrow(/already in/);
|
||||||
|
rooms.submit(room, bo, { pick: 2 }, 1);
|
||||||
|
expect(rooms.waitingOn(room)).toEqual(['A', 'B']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows a seat its own move in waiting and its last move played, and nobody else's", () => {
|
it("shows a seat its own move in waiting and its last move played, and nobody else's", () => {
|
||||||
|
|||||||
@@ -430,7 +430,8 @@ export class Rooms<State, Input> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Record a seat's move on the ledger, so a restart keeps it; resolve the round once nobody else is awaited.
|
* Record a seat's move on the ledger, so a restart keeps it; resolve the round once nobody else is awaited.
|
||||||
* A draft (an input the game does not call settled) may be replaced until it is settled.
|
* A draft (an input the game does not call settled) may be replaced, and a settled move taken back
|
||||||
|
* as a draft, until the round resolves.
|
||||||
* `resolved` is how many rounds the page had seen played when the move was made: a move that
|
* `resolved` is how many rounds the page had seen played when the move was made: a move that
|
||||||
* arrives after its round was played (a slow connection, a second click, a stale tab) is refused
|
* arrives after its round was played (a slow connection, a second click, a stale tab) is refused
|
||||||
* rather than taken as the move for the next round.
|
* rather than taken as the move for the next round.
|
||||||
@@ -442,8 +443,10 @@ export class Rooms<State, Input> {
|
|||||||
if (typeof resolved !== 'number') throw new RoomError('This page is out of date. Reload it to see the table as it stands.', 409);
|
if (typeof resolved !== 'number') throw new RoomError('This page is out of date. Reload it to see the table as it stands.', 409);
|
||||||
if (resolved !== room.resolved) throw new RoomError('That move was made for a turn that has already been played. Your page will catch up.', 409);
|
if (resolved !== room.resolved) throw new RoomError('That move was made for a turn that has already been played. Your page will catch up.', 409);
|
||||||
|
|
||||||
if (!this.waitingOn(room).includes(seat.id)) throw new RoomError('It is not your move.', 409);
|
if (!this.game.needsInput(room.state, seat.id)) throw new RoomError('It is not your move.', 409);
|
||||||
const input = this.game.cleanInput(raw);
|
const input = this.game.cleanInput(raw);
|
||||||
|
// A settled move is final; a draft may replace it while the round still waits on others.
|
||||||
|
if (seat.id in room.pending && this.settled(room.pending[seat.id]) && this.settled(input)) throw new RoomError('Your move is already in.', 409);
|
||||||
const why = this.game.validate?.(room.state, seat.id, input);
|
const why = this.game.validate?.(room.state, seat.id, input);
|
||||||
if (why) throw new RoomError(why);
|
if (why) throw new RoomError(why);
|
||||||
this.commit(room, { t: 'input', at: Date.now(), id: seat.id, input });
|
this.commit(room, { t: 'input', at: Date.now(), id: seat.id, input });
|
||||||
|
|||||||
Reference in New Issue
Block a user