games
Photo by Analise Benevides / Unsplash

Today's post in the Magic: The Programming series is all about the State pattern.  According to refactoring.guru the state pattern is as follows:

State is a behavioral design pattern that allows an object to change the behavior when its internal state changes. The pattern extracts state-related behaviors into separate state classes and forces the original object to delegate the work to an instance of these classes, instead of acting on its own.

The diagram above details the basic pattern.  Your context object is the thing whose behaviour changes based on the state that it's in.  The State class is an abstract class which defines the default behaviour of any given state.  The concreteStateA and concreteStateB classes are specific states which override the default behaviour where necessary.

The state pattern is useful because it allows you to define how your application should behave in a given state, and how to transition from one state to another, without having to use a lot of switch, or if else statements in your main 'context' class.  This increases flexibility when it comes to future requirements to add new states or state dependent behaviour.

So how does the concept of state relate to Magic? Well, in Magic, like most other card games there is the concept of a turn.  Each player takes their turn to do some stuff and then passes the turn to the next player.  In Magic there are 5 main phases that make up a turn.  These are as follows:

  • Beginning Phase (untap, upkeep, draw)
  • Pre-combat main phase
  • Combat phase (begin, declare attackers, declare blockers, damage, end)
  • Post-combat main phase (or second main phase)
  • Ending phase (end, cleanup)

The phase of a turn that the game is in can be considered to be the game's current state. There are certain activities that can only be done during certain phases of a turn.  For example, you are only allowed to cast sorcery spells during either of the main phases, but not during combat.  The same can be said for equipping a creature, this can only be done in the pre-combat or post-combat main phases.

In relation to the diagram above, in magic the context is the current turn.  The state represents the phase in which the current turn is in, and the concrete implementations of this state would be the 5 phases of a turn.

The external action which causes the turn to progress from one phase to the next would come from one or more players once they have done all they would like to, or can do in that particular phase.  Let's see what this diagram would look like in relation to a turn in magic.

Turn state in a game of Magic

So first let's examine what the Turn class looks like.

public class Turn
{
    private Phase _phase;

    public Turn()
    {
        this.Phase = new Beginning();
    }

    public Phase Phase
    {
        get { return _phase; }
        set
        {
            _phase = value;
            Console.WriteLine($"State: {_phase.GetType().Name}");
        }
    }

    public void NextPhase()
    {
        _phase.Handle(this);
    }

    public bool CanEquipCreatures()
    {
        return _phase.CanEquipCreature;
    }
}

The calls to both NextPhase() and CanEquipCreatures() are handled by the internal Phase object. The work to transition from one state to the next in response to the NextPhase() call is delegated to the current Phase object and the behaviour relating to the ability to equip a creature is returned from the internal Phase object.

As you would expect, the Phase class is pretty simple.  It has a boolean which each concrete phase will set to say whether or not a creature can be equipped, and an abstract Handle() method which each phase needs to override.

public abstract class Phase
{
    public bool CanEquipCreature {get; set;}

    public abstract void Handle(Turn turn);
}

Let's take a look at one example of a concrete implementation of this abstract Phase class.  The Combat phase.

public class Combat : Phase
{
    public Combat() { this.CanEquipCreature = false; }

        public override void Handle(Turn turn)
        {
            Console.WriteLine("Moving to 2nd Main Phase");
            turn.Phase = new SecondMain();
        }
}

In the combat phase it is not possible to equip creatures, so this property is set to false in the constructor.  The Handle() function takes in the current context, which is the current turn and advances it to the next phase, in this case the second main phase.

The one disadvantage to this approach is that each phase must know about at least one other phase, so there is some coupling between different phases.  This is not such a big problem in the magic example because it is not very often that the phases of a turn are modified.  It's not unheard of, there are certain cards which introduce a second combat phase, or end the turn without going to the end phase, but in the spirit of simplicity we can just ignore those for now.

If you wanted some looser coupling between phases you could introduce some kind of lookup table which each concrete class could use to determine what the next state should be.  This involves a bit more configuration and setup, but does allow for your individual states to not be aware of each other.

Let's look at a final concrete implementation, the SecondMain phase.

public class SecondMain : Phase
{
    public SecondMain() { this.CanEquipCreature = true; }

    public override void Handle(Turn turn)
    {
        Console.WriteLine("Moving to End Phase");
        turn.Phase = new End();
    }
}

The states in this example follow a very similar pattern.  Modify variables which the main context object references to determine its behaviour, and then when Handle() is called, proceed to the next state.

The main flow of the sample program is shown below.

public class Program
{
    public static void Main(string[] args)
    {
        // Start a new turn. Each turn starts with the beginning phase.
        Turn turn = new Turn();

        // A new turn begins. Lets check if we can equip creatures
        // during the beginning phase.
        Console.WriteLine(canEquipCreatures(turn));

        // Nope.  What abobut in the main phase?
        turn.NextPhase();

        // Yup. Creatures can be equipped in the pre-combat main phase.
        Console.WriteLine(canEquipCreatures(turn));

        turn.NextPhase(); // Combat
        turn.NextPhase(); // Second Main
        turn.NextPhase(); // End
    }

    private static string canEquipCreatures(Turn turn)
    {
        return turn.CanEquipCreatures() ?
        "Allowed to Equip Creatures" :
        "Not Allowed to Equip Creatures";
    }
}

In this program we are accessing the CanEquipCreatures() function on the Turn object to determine whether or not a creature can be equipped during this phase of the turn.

As the turn goes through the various stages the answer to whether or not a creature can be equipped or not changes depending on the internal state of the Turn object.

In using the State Pattern we have extracted state specific behaviour away from the main object, thereby reducing its complexity and gaining flexibility in how it behaves.

I hope you enjoyed this post and hopefully learned something.  Please feel free to leave a comment if there's any other patterns you think could apply to Magic that you'd like to see me write about.

Thanks for reading and keep an eye out for more posts in my “Magic the Programming” series.