Files
Dune/docs/TWO_PLAYER_TWO_FACTIONS.md
T

7.9 KiB

Two players controlling two factions each

Status and authority

Prototyped; wider rules and acceptance remain open. The GF9 classic rulebook, printed p. 6 offers a two-player setup in which each person plays two factions, with the two-player four-stronghold victory requirement. The user-authorized revised Advanced source, physical p. 4 (printed p. 2), offers the same choice and threshold. The supplied PDF stays local; its identity and precedence are in the decision index. The similarly titled movie game's double-faction rules are a different game and are not authority for this mode.

Neither cited classic passage says whether the two factions controlled by one person are automatically allied, how their holdings combine for victory, or how controller identity affects every faction-counted ability. Do not silently adopt the movie game's automatic alliances or blended inventories. A practical first version can retain independent faction records, actual alliance choices, and a four-stronghold target per eligible victory side as an explicitly provisional policy. Resolve combined holdings and any conflicting interaction from authoritative sources or document the chosen interpretation; this design does not certify those answers.

Model and setup

Keep four distinct Player faction records and their normal physical forces, leaders, decks, private hands, turns and faction identities. Add an explicit, lobby-validated association of those four distinct faction seats with exactly two controllers, two seats per controller. A controller is not itself a faction or a new recipient of faction income. Validate the complete mapping before starting and reject ambiguous, duplicate, missing or stale assignments; changing an assignment or faction clears readiness. Existing two-seat games, already-started games, and saved positions without the new mode retain their original interpretation and do not receive invented controller assignments.

Expose this as an explicit two-player setup option. Let each person create or join their first seat and securely add/select their second faction before readiness, with clear names and a persistent active-faction indicator. Avoid claiming complete support for the mode until complete games and wider interactions are verified. The existing model still permits ordinary two-seat play and separate one-faction controllers.

Separate controllers from faction seats

The players array currently represents faction seats, not physical humans. For this option game/victory-progress.ts must use the validated controller count for the printed two-player threshold; players.length === 2 alone is incorrect for four faction seats. Apply any agreed threshold increase through the same shared victory projection and quote, and retain explicit ordinary, alliance, Ecaz and special victory ordering. Never infer controller count by names, cookies, browser tabs, AI labels or faction alliances.

Audit each other count before changing it. Faction turn order, physical pieces, dealt cards, faction-specific payments and action ownership can still depend on the four faction seats. Optional-module eligibility and phase rules referring to players must be checked against their specific source; for example, existing Tech Token gates use players.length >= 3 in game/engine.ts and game/faction-module-profile.ts. A global substitution of controller count would silently change unrelated rules. Record uncertain interpretations and keep unsupported combinations visibly provisional rather than fabricating a new setup or automatic alliance.

Authentication and switching

Today db/schema.ts makes seats.token_hash the primary key; db/rooms.ts authenticates a token to one player_id and act rechecks that exact unrevoked room/player/token tuple before the versioned action. The normal joinRoom path returns alreadySeated for an authenticated caller. Room API routes retain the original room-scoped HttpOnly cookie and use separately scoped HttpOnly cookies for added faction seats. Reusing one token for two database rows, trusting a caller-supplied seat ID as authorization, or projecting both seats' private information into one general view would break those existing boundaries.

The authenticated add-second-seat operation atomically validates the lobby/controller assignment and creates the second distinct seat credential. Provision separately random, room-scoped HttpOnly credentials for both faction seats. A public active-seat selector may choose which credential to present, but every API read, action, message, control or recovery request must authenticate that selected credential and verify its actual player_id; the selector itself grants no access. Apply the same room-control, revocation, origin, retry, version and removal checks as today. Preserve existing seat cookies and single-seat clients; use additive schema or durable mapping changes if required, never reset the room database. No secret token belongs in source, client-visible game state or logs.

On a faction switch, app/page.tsx should advance its existing request epoch, discard stale cross-seat results and fetch the selected authorized view. Fence in-flight actions and responses by room, selected seat and version so a delayed response from the other faction cannot overwrite or act through the current view. Explicitly handle refresh and recovery of both distinct credentials, individual revocation, host assignment and relinquishment; a recovered or revoked seat must not accidentally grant the other seat or an opponent. Keep the UI explicit about which faction owns each pending decision. Existing autopilot can operate a faction seat only under its existing authorization; until full AI refinement, each offered decision needs a minimal legal bot path.

The prototype supports human controllers, an AI controller with two bot seats, and ordinary existing human-seat autopilot. AI factions use independent legal policies, not coordinated strategy or calibrated two-faction strength. The host can add either AI faction, configure each and remove a bot seat; if the primary bot leaves, its remaining bot is promoted to the controller identity. Each controller must be entirely human or entirely AI. Accepting a handover into a browser already holding a different active seat for the same room remains unsupported. The mode cannot be disabled while a secondary faction is present. Recovery replaces an existing scoped credential for the recovered seat without overwriting another valid legacy seat. Comprehensive recovery, revocation and custody acceptance remain open.

Integration and evidence

  • Implement one shared controller/seat contract through lobby creation, assignment, setup, the projected active seat, action dispatch and the shared stronghold progress and victory quote. Retain source-specific faction counts and provisional boundaries rather than just adding a selector or changing the victory number.
  • Include focused engine tests for valid/invalid mappings, original and changed thresholds, faction-specific private state, controlled faction decisions, ties and the intended victory-side interpretation. Exercise a real setup and a meaningful sequence using both factions of each controller; include legal minimal bot participation and usable action controls.
  • Test authenticated add/switch/read/action with distinct credentials, denied target seats, stale or concurrent switches and independent private views. Do not expose or reset unrelated rooms; use isolated dedicated test records.
  • Broader recovery, revocation, privacy and custody assurance, valid module combinations, complete human/AI games, browser/mobile acceptance and deployed verification remain required after the rules-first prototype. A passing focused test or this document does not certify a complete mode or change the publication gates.