Game Development Stack Exchange is a question and answer site for professional and independent game developers. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I am writing a computer version of the game Dominion. It is a turn-based card game where action cards, treasure cards, and victory point cards are accumulated into a player's personal deck. I have the class structure pretty well developed, and I am starting to design the game logic. I'm using python, and I may add a simple GUI with pygame later.

The turn sequence of the players is governed by a very simple state machine. Turns pass clockwise, and a player can't exit the game before it is over. The play of a single turn is also a state machine; in general, players pass through an "action phase", a "buy phase", and a "clean-up phase" (in that order). Based on the answer to the question How to implement turn-based game engine?, the state machine is a standard technique for this situation.

My problem is that during a player's action phase, she can use an action card that has side effects, either on herself, or on one or more of the other players. For example, one action card allows a player to take a second turn immediately following the conclusion of the current turn. Another action card causes all other players to discard two cards from their hands. Yet another action card does nothing for the current turn, but allows a player to draw extra cards on her next turn. To make things even more complicated, there are frequently new expansions to the game that add new cards. It seems to me that hard-coding the results of every action card into the game's state machine would be both ugly and unadaptable. The answer to Turn-based Strategy Loop does not go into a level of detail that addresses designs to solve this problem.

What kind of programming model should I use to encompass the fact that the general pattern for taking turns can be modified by actions that take place within the turn? Should the game object keep track of the effects of every action card? Or, if the cards should implement their own effects (e.g. by implementing an interface), what setup is required to give them enough power? I have thought up a few solutions to this problem, but I am wondering if there is a standard way to solve it. Specifically, I'd like to know what object/class/whatever is responsible for keeping track of the actions that every player must do as a consequence of an action card being played, and also how that relates to temporary changes in the normal sequence of the turn state machine.

share|improve this question
Hello Apis Utilis, and welcome to GDSE. Your question is well written, and it's great that you referenced the related questions. However, your question is covering a lot of different problems, and to fully cover it, a question would probably need to be enormous. You may still get a good answer, but yourself and the site will benefit if you break down your problem some more. Maybe start with building a simpler game and build up to Dominion? – michael.bartnett Jan 9 '13 at 7:05
I'd start from giving each card a script that modifies the game's state, and if nothing weird is going on, fall back on the default turn rules... – Jari Komppa Jan 9 '13 at 10:05
up vote 8 down vote accepted

I agree with Jari Komppa that defining card effects with a powerful scripting language is the way to go. But I believe that the key to maximum flexibility is scriptable event-handling.

In order to allow cards to interact with later game events, you could add a scripting API to add "script hooks" to certain events, like the beginnings and endings of game phases, or certain actions the players can perform. That means that the script which is executed when a card is played is able to register a function which is called the next time a specific phase is reached. The number of functions which can be registred for each event should be unlimited. When there is more than one, they are then called in their order of registration (unless of course there is a core game rule which says something different).

It should be possible to register these hooks for all players or for certain players only. I would also suggest to add the posibility for hooks to decide for themselves if they should keep being called or not. In these examples the return value of the hook function (true or false) is used to express this.

Your double-turn card would then do something like this:

add_event_hook('cleanup_phase_end', current_player, function {
     setNextPlayer(current_player); // make the player take another turn
     return false; // unregister this hook afterwards

(I have no idea if Dominion even has something like a "cleanup phase" - in this example it's the hypothetical last phase of the players turn)

A card which allows every player to draw an additional card at the beginning of their draw phase would look like this:

add_event_hook('draw_phase_begin', NULL, function {
    drawCard(current_player); // draw a card
    return true; // keep doing this until the hook is removed explicitely

A card which makes the target player lose a hit point whenever they play a card would look like this:

add_event_hook('play_card', target_player, function {
    changeHitPoints(target_player, -1); // remove a hit point
    return true; 

You won't get around hard-coding some game actions like drawing cards or losing hit points, because their complete definition - what exactly it means to "draw a card" - is part of the core game mechanics. For example, I know some TCGs where when your have to draw a card for whatever reason and your deck is empty, you lose the game. This rule isn't printed on every card which makes you draw cards, because it's in the rule book. So you shouldn't have to check for that lose condition in every card's script either. Checking things like that should be part of the hard-coded drawCard() function (which, by the way, would also be a good candidate for a hookable event).

By the way: It's unlikely that you will be able to plan ahead for every obscure mechanic future editions could come up with, so no matter what you do, you still will have to add new functionality for future editions once in a while (in this case, a confetti throwing minigame).

share|improve this answer
Wow. That chaos confetti thing. – Jari Komppa Jan 9 '13 at 16:34
Excellent answer, @Philipp , and this takes care of a great deal of things done in Dominion. However, there are actions that must occur immediately when a card is played, i.e. a card is played that forces another player to turn over the top card of his library and allowing the current player to say "Keep it" or "Discard it". Would you write event hooks to take care of such immediate actions, or would you need to come up with additional methods of scripting the cards? – fnord Jan 10 '13 at 18:25
When something needs to happen immediately, the script should call the appropriate functions directly and not register a hook function. – Philipp Jan 10 '13 at 18:44

I gave this problem - flexible computerized card game engine - some thought some time ago.

First off, a complex card game like Chez Geek or Fluxx (and, I believe, Dominion) would require cards to be scriptable. Basically each card would come with its own bunch of scripts that may change the state of the game in various ways. This would let you give the system some future-proofing, as the scripts might be able to do things you can't think of right now, but might come in a future expansion.

Second, the rigid "turn" may be causing problems.

You need some kind of "turn stack" which contains the "special turns", such as "discard 2 cards". When the stack is empty, the default normal turn continues.

In Fluxx, it's entirely possible that one turn goes something like:

  • Pick N cards (as stated by current rules, changeable via cards)
  • Play N cards (as stated by current rules, changeable via cards)
    • One of the cards may be "take 3, play 2 of them"
      • One of those cards may well be "take another turn"
    • One of the cards may be "discard and draw"
  • If you change rules to pick more cards than you did when your turn started, pick more cards
  • If you change rules for fewer cards in hand, everybody else must discard cards immediately
  • When your turn ends, discard cards until you have N cards (changeable via cards, again), then take another turn (if you played "take another turn" card sometime in the above mess).

..and so on, and so forth. So designing a turn structure that can handle the above abuse can be rather tricky. Add to that the numerous games with "whenever" cards (like in "chez geek") where the "whenever" cards can disrupt the normal flow by, for instance, cancelling whatever card was last played..

So basically I would start from designing a very flexible turn structure, design it so that it can be described as a script (as each game would need its own "master script" handling the basic game structure). Then, any card should be scriptable; most cards probably don't do anything strange, but others do. Cards can also have various attributes - whether they can be kept in hand, played "whenever", whether they can be stored as assets (like fluxx 'keepers', or various things in 'chez geek' like food)...

I never actually started implementing any of this, so in practice you may find plenty of other challenges. The easiest way to start would be to start with whatever you know of the system you want to implement, and implement them in scriptable ways, setting as little in stone as possible, so when an expansion comes along, you won't need to revise the base system - much. =)

share|improve this answer
This is a great answer, and I would have accepted both if I could have. I broke the tie by accepting the answer by the person with the lower reputation :) – Apis Utilis Jan 10 '13 at 19:30
No prob, I'm used to it by now.. =) – Jari Komppa Jan 11 '13 at 15:45

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.