Skip to content

docs(examples): add ultimatum-game demo composing the three multiplayer packages#18

Open
htsukamoto5 wants to merge 3 commits into
mainfrom
example-ultimatum-game
Open

docs(examples): add ultimatum-game demo composing the three multiplayer packages#18
htsukamoto5 wants to merge 3 commits into
mainfrom
example-ultimatum-game

Conversation

@htsukamoto5

Copy link
Copy Markdown
Member

Summary

Adds examples/ultimatum-game.html — the flagship end-to-end demo for the multiplayer packages. A turn-based ultimatum game (Güth, Schmittberger & Schwarze, 1982): two players split a $10 pot; the proposer offers, the responder accepts (both keep the split) or rejects (both get nothing).

It composes all three packages so the experiment carries almost no synchronization or coordination code of its own:

  • adapter-multiplayer-jatos — the network backend (JATOS group session + channel), connected once before jsPsych.run.
  • plugin-multiplayer-role — assigns proposer/responder by deterministic consensus, with a spectator overflow role for extra arrivals.
  • plugin-multiplayer-sync — every "wait for the other player" point (lobby, offer, decision) is one declarative barrier trial.

The rewrite

This is a rewrite of the author's ultimatum demo from jsPsych#3694. The key change: the hand-rolled role block that used to live in the lobby's on_finish (sort the participant ids, take the first two as proposer/responder, route the rest to a "game full" screen) becomes a single plugin-multiplayer-role trial. Every client now computes the same role map from the shared group session with no coordinator — and because ordering is by first-seen join time, the proposer/responder pair stays stable even if a spectator joins later.

Population model

Open-population: the lobby admits when ≥2 players are present, the first two play, and any later arrival becomes a spectator routed to a friendly "game is full" screen — graceful for the extra/abandoned participants open recruitment produces. (A stricter "exactly two" variant is possible via the role plugin's group_size: 2; see that package's own example.)

Status / dependencies

🤖 Generated with Claude Code

htsukamoto5 and others added 3 commits June 30, 2026 13:43
…er packages

The flagship end-to-end demo for the multiplayer packages: a turn-based ultimatum
game (Güth et al. 1982) where two players split a $10 pot. It composes all three:

- adapter-multiplayer-jatos as the network backend,
- plugin-multiplayer-role to assign proposer/responder by deterministic consensus
  (with a "spectator" overflow role for extra arrivals), and
- plugin-multiplayer-sync for every wait-for-the-other-player barrier.

Rewritten from the author's #3694 demo: the hand-rolled "sort participant ids, take
the first two" role block in the lobby's on_finish becomes a single role trial, so
every client computes the same role map with no coordinator. Open-population model —
the first two to arrive play, later arrivals get a friendly "game full" screen.

Illustrative (not single-tab runnable): needs jsPsych#3694's multiplayer API, the
JATOS environment, and >=2 participants. Adds examples/README.md documenting all of
this plus the scientific citation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review of #18 noted the demo's sync barriers had no timeout, so an active player
closing their tab would hang their partner forever — contradicting the README's
"graceful for abandoned participants" wording (which only ever meant the overflow
spectator case). Three fixes:

- Prose: scope the README claim to "extra arrivals"/over-enrollment, and document
  the new mid-game abandonment handling explicitly.
- Conditional: gate the game on `myRole === "proposer" || "responder"` so a client
  whose role assignment times out (role null) no longer falls through to a roleless
  outcome screen rendering undefined values.
- Timeouts: add `timeout` + a thin `on_timeout` (set a `partnerLeft` flag) to the two
  barriers that wait on the OTHER player (proposerWaitTrial, responderWaitTrial); the
  timeline then shows a brief "the other player left" screen instead of the outcome.
  Barriers that wait on a player's own data (the lobby, the responder confirming its
  own decision) keep waiting indefinitely — correct for open recruitment.

Handling stays declarative (one flag, the plugin's own timeout/on_timeout params), so
the demo still carries essentially no synchronization code of its own. PARTNER_TIMEOUT_MS
is a single tunable constant at the top of the file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Document that the abandonment timeouts are per-client with no mutual handshake, so a
false timeout (a slow-but-present responder exceeding PARTNER_TIMEOUT_MS) yields
divergent end-states — proposer sees "the other player left" while the responder
completes and sees a normal outcome. Point to the opt-in hardening (generous timeout
and/or a trial_duration cap on the decision screen below PARTNER_TIMEOUT_MS), and note
why it's left off by default (it imposes an auto-advance + forced decision).

Docs-only; surfaced in review of #18.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant