Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to extend states from multiple classes

(Please note: knowledge of the trading card game Magic: The Gathering will be a plus here. Sorry, I don't know how to put it any easier.)

I have hit upon a problem using Java, which I'll describe as follows... I have a fundamental class called Card with all the following attributes:

public class Card{
    String Name;
    String RulesText;
    String FlavorText;
    String Cost;
    int ConvertedCost;
    String Rarity;
    int Number;
}

The Permanent class extends Card, and is extended in turn by the classes Creature, Planeswalker, Artifact, Land and Enchantment. As of yet, among them, only the first two have fields of their own:

public class Creature extends Permanent{
    int Power;
    int Toughness;
}
public class Planeswalker extends Permanent{
    int Loyalty;
}

Problems:

  • Some Permanent objects may be subject to methods affecting an object of its subclasses, for example, both as Artifacts AND Creatures, or as Artifacts AND Lands. I know I can use interfaces to extend behavior, but I cannot do that to extend state unless I use abstract classes, and in some instances I need an object with the state of both Artifacts and Creatures.
  • Most Permanent objects of a given subclass cannot be affected by a method targeting an object of a different subclass. (I.e., a method targeting Enchantment objects only cannot affect Creature objects.)
like image 465
Emiliano De Santis Avatar asked Feb 24 '23 07:02

Emiliano De Santis


2 Answers

I think you have more problems than you know. There's a difference between a card and a Permanent, a card performs an effect, like summoning a creature, while the Permanent resulting from the summoning is the creature in play. The card gets used in the game to represent both things for convenience but during programming you will want to distinguish between them.

MTG has cases where Permanents are not related directly to a card (cards like The Hive generate creatures represented only by tokens, they do not have a card representation), so your inheritance hierarchy does not work for that. While you will need to keep a linkage between a card and the creature summoned by it (so you know when to send the card back to the discard pile), I don't think the specific inheritance relationship you have laid out is going to be useful. You may need one hierarchy for cards and another different one for Permanents. Don't ignore the possibility of hierarchies (because there are many inheritance relationships that can be leveraged usefully), just be careful to keep separate things separate.

I would recommend reading Steve Yegge's blogpost on the Universal Design Pattern, where he talks about using a Properties pattern for his game (which seems about as flexible as MTG). Obviously a lot of characteristics of things in the game are mutable (including what's a creature and what isn't, since there are cards that change lands to creatures) and a system for handling properties and changes to them (including expiring temporary changes) is going to be essential. By itself a class hierarchy is not going to be flexible enough to accommodate the variety of effects in MTG.

What makes MTG interesting is that you have this ostensible subject-matter domain (composed of things like Creatures, Spells, Artifacts, Lands, etc.) that seems understandable and solid, but the rules allow these things to change in unpredictable ways (especially since new cards can always be introduced). The subject-matter domain is implemented through a game domain (the set of rules that manipulate the subject-matter domain). If you hard-code the subject-matter domain into your implementation (by doing things like representing Lands or Artifacts by extending a super class or implementing an interface) there are always going to be things you run into that you can't do, and fixing these problems may be hard or impossible. Read the rules noting the technical terminology introduced (cards, permanents, tokens, effects, etc.), because that's the real domain that you need to be implementing.

like image 60
Nathan Hughes Avatar answered Feb 26 '23 23:02

Nathan Hughes


Consider using a StateMachine for this.

You'd have a CreatureStateMachine as well as an ArtifactStateMachine. An object implementing both interfaces can be passed to either StateMachine. Each StateMachine would be responsible for managing the values of a different set of attributes.

The object itself would generate events and pass them to the StateMachines which would either ignore or deal with them accordingly. The StateMachine updates the State of the object being managed and the Object itself only knows what State it is in.

like image 39
Speck Avatar answered Feb 26 '23 23:02

Speck