1

I'm trying to code Star Trek The Dice Game. The basic concept is you roll dice, which become different types of crew (Science, Medical, Command, Engineer, etc...) and you can "spend" crew to take actions (heal people, transport people, raise/lower shields, cause the ship to travel, repair the hull, etc...).

The game has event and development cards. Event cards are bad things (like the ship takes damage, an action can't be used, you need more crew to raise shields, etc...). Development cards are good things (hull repairs fix extra hull, raise shields for free, heal fixes two people, etc...).

My problem - I'm struggling to determine how to enforce the card's rules as they enter and leave play. My concerns are two-fold:

  1. Not all rules are boolean. The rules engines I've run across seem to always assume boolean rules; an object meets the rule or doesn't. While some of the rules are boolean (ex: did the player provide enough crew to repair the hull), others aren't (ex: healing fixes an additional person).

  2. One approach might be to have booleans in different objects which track the rules in play. For instance, an HealPersonAction class that tracks if costs went up, if an extra person is healed, etc... But that means the rules knowledge is distributed across the game and I'd need messages or an observer to inform every affected object as cards enter or exit play. My instinct is encapsulating rules logic within the cards is a wiser way to go, especially since it becomes easier to change the rules later by just changing the card.

Is there a recommended design to address the problem of game cards temporarily modifying game rules in various ways?

1
  • 1
    "Not all rules are boolean." - ok, and where is the problem? Just do not use boolean variables for tracking non-boolean rules, but variables of a type which matches your requirements. – Doc Brown Apr 28 at 18:29
2

The rules in play seem to be a property of the game itself, and the cards just act as "switches" (maybe switches with parameters) to activate them or deactivate them. Hence my first approach would be to model this exactly that way - an "ActiveRules" object which tracks the state of the currently valid rules. So it does actually not matter if the rule changes are caused by "cards" entering or leaving the game, or if there are other kinds of events or configuration parameters which affect the rules.

If that "ActiveRules" object holds the state information directly in itself (like a - non-boolean - member variable "Number_of_people_to_heal_at_once"), or is just the root of an object hierarchy which contains the rules in a more structured way is pretty impossible to tell from a 30,000 feet view. If necessary, such rules could also come in the flavour of "higher order functions", like a "Number_of_people_to_heal_at_once" function with some parameters. I would recommend to stick to the YAGNI principle here: start with the most simple model currently required, and refactor and extend the model during the development, when the code base grows, whenever things become "too complicated".

The design, however, is also influenced by the place where the actual behaviour of the game is placed, especially the behaviour which is affected by the changing rules. If it is encapsulated in command-like classes or objects like a HealPersonAction, your "observer" idea might be useful:

  • every command object which may be affected registers for a RuleWasChanged event at the ActiveRules object , so latter can notify every registered instance when a rule change occurs

  • when a "command object" gets notified about a rule change, it will ask the ActiveRules object about the state of the current rule parameters which it depends on, and changes its internal state accordingly.

  • in case there are several of such command objects, and you notify a measurable(!) performance hit, as an optimization, one could classify the rules in certain categories, and let command objects register only for certain categories of rule changes.

Finally, let me give you something you should think about: this Observer approach could be pretty much overdesigned. Maybe it is sufficient to have each "command" object a reference to the ActiveRules object, and let the command object query the required rule parameters at the time when the command is "executed" or applied. In case this makes the code simpler and gives you the required performance, I would recommend to try this first before implementing something more complicated.

Qapla‘! >>:-|

1
  • Appreciate the reply. One other notion I had was that the rules could be defined within each card object but applying the rules to the different command objects seemed awkward. – Craig Apr 29 at 0:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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