Photo by Chinh Le Duc / Unsplash

This article is going to introduce the design pattern known as the Observer Pattern, but first, let's find out how it applies in a game of magic.

In Magic there is a lot of interactions between cards. Some of these are automatic, some are manual and some are in response to something else happening. These interactions between cards are the result of what are known as abilities.

There are three types of abilities in Magic: static, activated and triggered.

If you don't care about these types of abilities and want to get stuck into the pattern, feel free to skip ahead to the Observer Pattern section.

Static

The first type, static, is an ability which always applies.  It is constant. An example of a static ability can be seen on Akroma's Memorial.

Akroma's Memorial from Magic: 2015

This is quite a wordy ability but it basically means that a lot of effects are given to creatures you control.  As soon as this card enters the battlefield those effects apply.  There is no further cost to be paid and the ability remains active until the card is removed from play.

Activated

The second type is activated abilities, which are where the ability only applies if the player is willing to pay a cost for them.  For example, the creature Kenrith, the Returned King has a number of activated abilities, with varying different costs and colour requirements.

Kenrith, the Returned King from Throne of Eldraine

A player can choose to spend 1 red mana in order to give all creatures trample and haste until the end of turn, or they can pay 1 colourless and 1 green mana to put a +1/+1 counter on a target creature and so on.  If you don't know what any of that means it's not important.  But what is important to note is that nothing happens automatically.  These abilities must be activated by the player who controls that creature.

Triggered

The final type of ability, and the type we're most interested in, in relation to the Observer Pattern, is triggered abilities.  A triggered ability is an ability which happens automatically in response to something else happening in the game and uses the words "when", "whenever" or "at".

The creature card Ajani's Pridemate has a triggered ability which gains it a +1/+1 counter whenever its controller gains life.

Ajani's Pridemate from War of the Spark

In order to codify this link between a user gaining life and putting a +1/+1 counter on Ajani's Pridemate, we can use the Observer Pattern.

The Observer Pattern

According to dofactory.com the Observer Pattern is

A one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Do Factory Observer Pattern

The observer pattern is a useful pattern to know if you have a lot of objects which care about an event or an update happening elsewhere in your code.  Instead of using a lot of CPU resources to continuously poll for that information from each dependent object, instead we can use the observer pattern to "push" the update to the interested parties.  This saves on CPU and ensures that objects are notified the instant an update is made, rather than polling on a given time interval.

This pattern is also a good fit for codifying our triggered abilities in magic because it allows us to notify the affected cards when something happens that they may have to automatically respond to.

To fit the above diagram into the context of a game of Magic the Gathering, we can say that the Subject is the player, and a ConcreteSubject would be a specific player. It's the life total property of this player we're interested in though for this example.

An Observer in this case is a creature card which has a triggered ability present on it, and which knows how to update itself when triggered.

The ConcreteObserver in this case would be a specific creature card, e.g. Ajani's Pridemate, which has an ability triggered by a change to the ConcreteSubject, i.e. his controllers life total.

The relationship between Subject and Observer is one to many, as there can be multiple cards on the battlefield which respond to the same trigger.  For instance, another card which cares about life total increases is Heliod, Sun-Crowned.

Heliod, Sun Crowned from Theros Beyond Death. This card actually has all 3 ability types.

For this reason it is important to have an Interface which defines a common method all ConcreteObservers must have, so the ConcreteSubject (via the Subject Class) can notify them without knowing which specific card they are.

So if we were to redraw the above diagram but in the context of a game of magic, it would look like the following:

To code this pattern I've reused some of the classes defined earlier in the series, such as the ICreature and Creature classes which were defined in Magic The Programming: Decorator Pattern.

The basic principle is that whenever a player casts a creature, you "attach" that creature to the player.  Then, whenever you update any properties on the player object, you can invoke the Notify() function locally if you want any attached cards to be notified of that change.

Let's see the output from the simple console app I put together so we can see what we're aiming for:

Gareth casts Ajani's Pridemate
Gareth casts Heliod, Sun-Crowned
Gareth gained life
[Ajani's Pridemate]: Gareth has been updated.
[Ajani's Pridemate]: New Life Total: 45
[Ajani's Pridemate]: Put a +1/+1 counter on Ajani's Pridemate.
[Heliod, Sun-Crowned]: Gareth has been updated.
[Heliod, Sun-Crowned]: New Life Total: 45
[Heliod, Sun-Crowned]: Put a +1/+1 counter on target creature or enchantment you control.

So as you can see, after casting the creature cards and updating my life, each card reacts to this life total change.

The most important class in this pattern is the Player class which defines how to add, remove and notify observers, or in this case creatures.

public abstract class Player : IPlayer
{
    private int _lifeTotal;
    private List<ICreature> _creatures { get; set; }

    public string Name { get; set; }

    public int LifeTotal
    {
        get
        {
            return _lifeTotal;
        }
        set
        {
            if (value > _lifeTotal)
            {
                Console.WriteLine($"{this.Name} gained life");
                _lifeTotal = value;

                Notify();
            }

            _lifeTotal = value;
        }
    }

    public Player(string name, int startingLifeTotal)
    {
        this.Name = name;
        _creatures = new List<ICreature>();

        _lifeTotal = startingLifeTotal;
    }

    public void Attach(ICreature _creature)
    {
        _creatures.Add(_creature);
    }

    public void Detach(ICreature _creature)
    {
        _creatures.Remove(_creature);
    }

    private void Notify()
    {
        foreach (ICreature creature in _creatures)
        {
            creature.Update(this);
        }
    }
}

In this case we're only interested if the player has gained life, which is why there is a check to see if the new value is greater than the existing life total.  If so, then the Notify() method is called.  This method is responsible for calling the Update() function on each of the creatures who have subscribed to changes in the Player's state.

An example of one such create is Ajani's pridemate which is detailed below.

public class Ajani : Creature
{
    public Ajani(string name, int power, int toughness)
    : base(name, power, toughness)
    {

    }

    public override void Update(IPlayer subject)
    {
        Console.WriteLine($"[{this.Name}]: {subject.Name} has been updated.");
        Console.WriteLine($"[{this.Name}]: New Life Total: {subject.LifeTotal}");
        Console.WriteLine($"[{this.Name}]: Put a +1/+1 counter on Ajani's Pridemate.");
    }
}

This class must implement the Update() function, to decide how it reacts to any change in the ConcreteSubject's state.

In my code I actually created a ConcreteSubject class which was a CommanderPlayer class which just inherits from Player and gives the player a starting life total of 40.

The code for the overall program is listed below.  As we can see, everything that happens after updating the life total is handled by the individual creature cards as they are updated of the change.

public static void Main(string[] args)
{
    var player1 = new CommanderPlayer("Gareth", 40);

    Console.WriteLine($"{player1.Name} casts Ajani's Pridemate");

    ICreature ajani = new Ajani("Ajani's Pridemate", 2, 2);
    player1.Attach(ajani);

    Console.WriteLine($"{player1.Name} casts Heliod, Sun-Crowned");

    ICreature heliod = new Heliod("Heliod, Sun-Crowned", 5, 5);
    player1.Attach(heliod);

    player1.LifeTotal += 5;
}

This pattern shows us a nice way to react to changes in another objects state in a more "push" oriented manner, instead of the more CPU intensive "pull" method which involves periodically checking an object to see if it has updated or not.

This pattern and all the others can be found in my git repo on GitHub.  Please feel free to submit pull requests, create issues or leave a comment below.