Context: I'm going to use the board game Sorry as an example, but this question could apply to other board games. In Sorry, each player has four pawns. Based on drawn cards, a player moves their pawns around the board. These moves can impact other players' pawns, like bumping another player's pawn back to Start.
For an (oversimplified) possible implementation of the game, consider this architecture: 1. Game class - Responsibility is handling turns. Has a collection of Player objects 2. Player class - Responsibility is enabling a player (AI or human) to take actions. Has a collection of that player's Pawns. 3. Pawn class - Responsibility is knowing its state; each instance represents a single entity.
Example: For some Player actions, like performing a Sorry, the player needs to know where all pawns are to make an informed decision. Other actions are similar, such as choosing which pawn to move; I might move a pawn so it ends on an opponent's pawn.
A simple approach would be for the Player class to have functions like: Pawn* ChooseSorryPawn(vector oppPlayers). The function would then query each Player for its Pawns, build the whole list of opponent Pawns, and then make a decision.
Concern: For one function, the above might be ok. But this same pattern would be needed for all Player functions involved with making a decision, since a player needs to know the whole state of the board.
Question: Is this really the best way to go, or is there a better design pattern for letting a Player make decisions without passing in direct references to opposing Players and their Pawns? Might a Mediator be the right approach here?
As a related question, would it better to store Pawn position state in a separate class, like a GameBoard class, instead of with the Pawns themselves? That way, each Player would take a GameBoard reference in its constructor.