views:

2696

answers:

7

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, e.g. dice, checkers, cards, a board, spaces on the board, money, etc.

  • An object for each player, which contains the players resources (e.g. 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.

+2  A: 

Three Rings offers LGPL'd Java libraries. Nenya and Vilya are the libraries for game related stuff.

Of course, it would help if your question mentioned platform and/or language restrictions you might have.

jmucchiello
"Eventually I expect to build a WPF UI" - that means .NET. At least, as far as I can tell.
Mark Allen
Alphabet soup I'm not aware of.
jmucchiello
Yeah, I am doing .NET, but my question isn't language- or platform-specific.
Jay Bazuzi
+8  A: 

Of course there are many, many, many, many, many, many, many, resources about this topic. But I think you are on the right path dividing up the objects and let them handle its own events/data and so on.

When doing tiled based board games you'll find it nice to have routines to map between the board array and row/col and back, along other features. I remember my first board game (long long ago) when I struggeled with how to get row/col from boardarray 5.

1  2  3  
4 (5) 6  BoardArray 5 = row 2, col 2
7  8  9

Nostalgy. ;)

Anyway, http://www.gamedev.net/ is a good place for information. http://www.gamedev.net/reference/

Stefan
Why don't you just use a 2-dimensional array? Then the compiler can handle this for you.
Jay Bazuzi
My excuse is that this was a long long time ago. ;)
Stefan
gamedev has a ton if stuff, but I didn't see quite what I was looking for.
Jay Bazuzi
what language were you using?
tranced_UT3
Basic, Basica, QB, QuickBasic and so on. ;)
Stefan
+10  A: 

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.

RS Conley
+54  A: 

Hi there, 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.

Andrew Top
I don't think it's concise, but it is helpful! Upvoted.
Jay Bazuzi
Glad it was helpful... Which parts were not concise? I would be happy to do a clarification edit.
Andrew Top
I'm building a turn-based game right now and this post has been really helpful!
Kiv
I read that Memento is the pattern to use for undo... Memento vs Command Pattern for undo, your thoughts plz..
tranced_UT3
This is the best answer I have read in Stackoverflow so far. THANKS!
codecaster
+4  A: 

Much of the materials I can find online are lists of published references. The publications section of Game Design Patterns has links to PDF versions of the articles and theses. Many of these look like academic papers like Design Patterns for Games. There is also at least one book available from Amazon, Patterns in Game Design.

Eric Weilnau
+3  A: 

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.

Pyrolistical
+1  A: 

Is there some prior art I can take advantage of?

If your question ain't language- or platform-specific. then I would recommend that you consider AOP Patterns for State, Memento, Command, etc.

Whats the .NET answer to AOP???

Also try to find some cool websites such as http://www.chessbin.com

tranced_UT3