Skip to content

Simultaneous Move support#356

Open
hopshackle wants to merge 18 commits into
masterfrom
simultaneous-moves
Open

Simultaneous Move support#356
hopshackle wants to merge 18 commits into
masterfrom
simultaneous-moves

Conversation

@hopshackle

Copy link
Copy Markdown
Collaborator

This provides initial support in the core framework for Simultaneous Move games. This enables new games to have simultaneous moves, or for old ones with internal implementations (SushiGo, War of theToads, Diamant, Resistance...) to be converted to the new framework. However, it is backwards compatible with all existing games, so no conversion is required.

The advantage of converting a game is that SFP algorithms may then be able to take advantage of this.

AbstractGameState

  • added new method to getSimultaneousPlayers(), that returns a List of the players who make a move at the same time
  • a new method for redeterminise(int playerId) has been added to enable a clean separation between copy() and redeterminise(); which really are two different things. [see note at the end of this PR for plans here]

AbstractForwardModel

  • added new methods to specify (optionally) the player for whom moves should be calculated in computeAvailableActions

IExtendedSequence

  • added support for delegation from FM/State for simultaneous moves

Game

  • amended the core loop to assume simultaneous moves, and use the new methods in AbstractGameState and AbstractForwardModel above
  • default implementations of these new methods in AbstractGameState and AbstractForwardModel ensure backwards compatibility and the default assumption of sequential player actions

At the moment, only SushiGo has been converted to the new set up.

The split of _copy() and redeterminise() was required for SushiGo because the redeterminisation has to set the turnOwner, and _copy() is called at the start of copy(), before copy() then sets up the fields defined in AbstractGameState (overriding turnOwner).

This split into _copy() and redeterminise(in playerId) enables the latter to be called at the end of the copy() method, giving it the option of shuffling/obscuring data at the AGS level, which is theoretically desirable. It also provides a clean semantic split between, "the code that copies the state data", and "the code that shuffles this to account for imperfect information", which is in any case a better design that may reduce errors.
The intention is therefore in a separate PR to fully implement this _copy/redeterminise split across all existing games...but not in this one.

ibrahim-mLq and others added 18 commits April 14, 2026 15:09
- Add getSimultaneousPlayers() to AbstractGameState with default
  implementation wrapping getCurrentPlayer() for backwards compatibility

- Add SimultaneousAction class to core/actions/ extending AbstractAction
  stores player -> action map with LinkedHashMap for deterministic order
  implements execute(), copy(), getString() and toString()

- Extract makeDecisionForPlayer() from oneAction() in Game
  handles observation copy, action computation and player decision

- Add oneSimultaneousAction() to Game
  loops over active players, collects actions, wraps into SimultaneousAction
  applies once via forwardModel.next()

- Wire oneAction() to delegate to oneSimultaneousAction()
  when getCurrentSimultaneousPlayers().size() > 1
# Conflicts:
#	src/main/java/core/Game.java
…to fail without me noticing in a differetn PR)
@hopshackle hopshackle requested a review from rdgain June 8, 2026 10:47
throw new AssertionError("Action played that was not in the list of available actions: " + action);
}

if (actionValidation && !observedActions.contains(action))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a big thing, but I'm wondering if in some games this may be allowed (so part of core games parameters instead, for example). Say, everyone throw a card down, if someone doesn't play a legal action, they're out of the game, or lose a point, but the rest of the actions are still applied. No game comes to mind that would use this mechanism, but I think there would be cases where the legal actions should still be applied while ignoring or penalizing the others, so I'd be in favour of keeping this definable at game level. Could be actionValidation = true by default with the behaviour being a function called on the FM for handling invalid actions (I think there may already be something like that, or at least it was at some point before on the FM), with the possibility of overriding this by individual games for different behaviours.




*/

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Odd comments, class should be nicely documented especially as a first example of using proper simultaneous actions

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any old code should also be removed to allow proper diff and reduce confusion

throw new AssertionError("Player " + i + " has " + gs.getPlayerHands().get(i).getSize() + " cards, expected " + expectedPlayerCards);
}
}
// int expectedPlayerCards = gs.getPlayerHands().get(0).getSize();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be cleaned up if no longer necessary

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.

3 participants