Any patterns for modelling board games?

For fun, I'm trying to write one of my son's favorite board games as a piece of software. Eventually I expect to build a WPF UI on top of it, but right now I'm building the machine that models the games and its rules.

As I do this, I keep seeing problems that I think are common to many board games, and perhaps others have already solved them better than I will.

(Note that AI to play the game, and patterns around high performance are not interesting to me.)

So far my patterns are:

  • Several immutable types representing entities in the game box, eg dice, checkers, cards, a board, spaces on the board, money, etc.

  • An object for each player, which contains the players resources (eg money, score), their name, etc.

  • An object that represents the state of the game: the players, who's turn it is, the layout of the peices on the board, etc.

  • A state machine that manages the turn sequence. For example, many games have a small pre-game where each player rolls to see who goes first; that's the start state. When a player's turn starts, first they roll, then they move, then they have to dance in place, then other players guess what breed of chicken they are, then they receive points.

  • Is there some prior art I can take advantage of?

    EDIT: One thing I realized recently is that game state can be split in to two categories:

  • Game artifact state . "I have $10" or "my left hand is on blue".

  • Game sequence state . "I have rolled doubles twice; the next one puts me in jail". A state machine may make sense here.

  • EDIT: What I'm really looking for here is the best way to implement multiplayer turn-based games like Chess or Scrabble or Monopoly. I'm sure I could create such a game by just working through it start to finish, but, like other Design Patterns, there are probably some ways to make things go much more smoothly that aren't obvious without careful study. That's what I'm hoping for.


    it seems this is a 2 month old thread that I've just noticed now, but what the heck. I've designed and developed the gameplay framework for a commercial, networked board game before. We had a very pleasant experience working with it.

    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.

    The idea behind our framework was to represent the game state as structure with all the data fields that together, provide the complete game state (ie: if you wanted to save the game to disk, you write that structure out).

    We used the Command Pattern to represent all of the valid game actions a player could make. Here would be an example action:

    class RollDice : public Action
    {
      public:
      RollDice(int player);
    
      virtual void Apply(GameState& gameState) const; // Apply the action to the gamestate, modifying the gamestate
      virtual bool IsLegal(const GameState& gameState) const; // Returns true if this is a legal action
    };
    

    So you see that to decide whether a move is valid, you can construct that action and then call its IsLegal function, passing in the current game state. If it is valid, and the player confirms the action, you can call the Apply function to actually modify the game state. By ensuring that your gameplay code can only modify the game state by creating and submitting legal Actions (so in other words, the Action::Apply family of methods are the only thing that directly modifies the game state), then you ensure that your game state will never be invalid. Furthermore, by using the command pattern, you make it possible to serialize your player's desired moves and send them over a network to be executed on other player's game states.

    There ended up being one gotcha with this system that turned out to have a fairly elegant solution. Sometimes actions would have two or more phases. For example, the player may land on a property in Monopoly and must now make a new decision. What is the game state between when the player rolled the dice, and before they decide to purchase a property or not? We managed situations like this by featuring an "Action Context" member of our game state. The action context would normally be null, indicating that the game is not currently in any special state. When the player rolls the dice and the dice rolling action is applied to the game state, it will realize that the player has landed on an un-owned property, and can create a new "PlayerDecideToPurchaseProperty" action context that contains the index of the player we are waiting for a decision from. By the time the RollDice action has completed, our game state represents that it is currently waiting for the specified player to decide whether to buy a property is not. It is now easy for all other actions' IsLegal method to return false, except for the "BuyProperty" and "PassPropertyPurchaseOpportunity" actions, which are only legal when the game state has the "PlayerDecideToPurchaseProperty" action context.

    Through the use of action contexts, there is never a single point in the life-time of the board game where the game state structure does not completely represent EXACTLY what is happening in the game at that point in time. This is a very desirable property of your board game system. It will it much easier for you to write code when you can find everything you ever want to know about what's happening in the game by examining only one structure.

    Furthermore, it extends very nicely to networked environments, where clients can submit their actions over a network to a host machine, which can apply the action to the host's "official" game state, and then echo that action back to all the other clients to have them apply it to their replicated game states.

    I hope this was concise and helpful.


    The basic structure of your game engine uses the State Pattern. The items of your game box are singletons of various classes. The structure of each state may use Strategy Pattern or the Template Method.

    A Factory is used to create the players which are inserted into a list of players, another singleton. The GUI will keep watch on the Game Engine by using the Observer pattern and interact with this by using one of several Command object created using the Command Pattern. The use of Observer and Command can be used with in the context of a Passive View But just about any MVP/MVC pattern could be used depending on your preferences. When you save the game you need to grab a memento of it's current state

    I recommending looking over some of the patterns on this site and see if any of them grab you as a starting point. Again the heart of your game board is going to be a state machine. Most games will represented by two states pre-game/setup and the actual game. But you can had more states if the game you are modelling has several distinct modes of play. States don't have to be sequential for example the wargame Axis & Battles has a battle board the players can use to resolve battles. So there are three states pre-game, main board, battle board with the game continually switching between the main board and battle board. Of course the turn sequence can also be represented by a state machine.


    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 GamePhase s 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 GamePhase s.

    Monopoly

    Example of some GamePhase s 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.

    链接地址: http://www.djcxy.com/p/50058.html

    上一篇: AngularJS:了解设计模式

    下一篇: 任何模拟棋盘游戏的模式?