I just finished designing and implementing a state based game using polymorphism.
Using a base abstract class called GamePhase that has one important method
abstract public GamePhase turn();
What this means is every GamePhase object holds the current state of the game, and a call to turn() looks at its current state and returns the next GamePhase.
Each concrete GamePhase has constructors that hold the entire game state. Each turn() method has a little bit of the game rules inside them. While this spreads the rules around, it keeps related rules close together. The end result of each turn() is just creating the next GamePhase and passing in the full state into the next phase.
This allows turn() to be very flexible. Depending on your game a given state can branch to many different types of phases. This forms a graph of all game phases.
At the highest level the code to drive it is very simple:
GamePhase state = ...initial phase
while(true) {
// read the state, do some ui work
state = state.turn();
}
This is extremely useful as I can now easily create any state/phase of the game for testing
Now to answer the second part of your question, how does this work in multiplayer? Within certain GamePhases that require user input, a call from turn() would ask the current Player their Strategy given the current state/phase. Strategy is just a interface of all the possible decisions a Player can make. This setup also allows Strategy to be implemented with AI!
Also Andrew Top said:
Your game can probably be in a (close to) infinite amount of states because of the permutations of things like how much money player A has, how much money player B has, and etc... Therefore, I'm pretty sure you want to stay away from state machines.
I think that statement is very misleading, while it is true that there are a lot of different game states, there are only a few game phases. To handle his example, all it would be is an integer parameter to the constructors of my concrete GamePhases.
Monopoly
Example of some GamePhases would be:
- GameStarts
- PlayerRolls
- PlayerLandsOnProperty (FreeParking, GoToJail, Go, etc)
- PlayerTrades
- PlayerPurchasesProperty
- PlayerPurchasesHouses
- PlayerPurchasesHotels
- PlayerPaysRent
- PlayerBankrupts
- (All Chance and Community Chest cards)
And some states in the base GamePhase are:
- List of Players
- Current Player (who's turn)
- Player's Money/Property
- Houses/Hotels on Properties
- Player Position
And then some phases would record their own state as needed, for example PlayerRolls would record the number of times a player has roll consecutive doubles. Once we leave the PlayerRolls phase, we don't care about consecutive rolls anymore.
A lot of phases can be reused and linked together. For example the GamePhase CommunityChestAdvanceToGo would create the next phase PlayerLandsOnGo with the current state and return it. In the constructor of PlayerLandsOnGo the current player would be moved to Go and their money would be incremented by $200.