Time Reversal - Howard Lyon (Wizards of the Coast LLC)

Scenario

In a game of magic, the person with the largest advantage at any given time is said to have the best "board state".  Board state is the term used to describe the cards that player has in play.  There are so many triggers, card types and rules in magic that it's not uncommon for mistakes to be made occasionally.  Depending on how casual the group you're playing with is, most of the time if you notice something you've missed, you can mention it and the rest of the players will allow you to rectify the mistake.  Sometimes in order to do this, it's necessary to reverse the state of the game to an earlier time, and then re-play things again after the mistake has been rectified.  This involves changing a player's board state by undoing actions that were taken after the missed triggered ability should have taken place.

For more information about triggered abilities, check out my previous article on the Observer Pattern.

In order to model an example of this in code we're going to need a scenario to base it on.  For the scenario I've decided to use the following cards: "Beetleback Chief"

a red creature card with an enter the battlefield (etb) triggered ability and "Barter in Blood" a black Sorcery card with a static ability.

The game-based interactions we're going to look at are as follows:

  1. Player 1 plays a "Beetleback Chief", but forgets to add the 2 goblin tokens to the battlefield.
  2. Player 1 declares their turn over and passes the turn to player 2.
  3. Player 2 starts their turn and casts a "Barter in Blood" causing each player to sacrifice 2 creatures.
  4. Player 1 realises that they missed their "etb" trigger to add 2 red goblin tokens.
  5. The other players all agree to allow player 1 to add the tokens before the "Barter in Blood" trigger resolves.
  6. Player 1 can then sacrifice the 2 tokens instead of the only other remaining creature they have, the "Beetleback Chief".

This type of mistake is usually forgiven more easily in the earlier stages of the game, where someone's board state is still being built up.  This would not however be allowed to happen in a professional game of magic.

The magic judge community has a dedicated site on how to deal with missed triggers in professional games.

The Memento Pattern

https://www.dofactory.com/net/memento-design-pattern

How can the memento pattern help us to model this scenario? Well, the memento pattern is a behavioural pattern which allows us to save a particular object's state and restore it at a later date, without the details of that object having to be revealed.  This is good because we want our objects and their data to be encapsulated.  If there are any private variables in our object, we don't want to have to make them public just so we can save the object's state.  In a game of magic you can see why this might be useful if there were state changes which involved the contents of a player's hand.  That is not something you would want to have as a public property, however it is still something we might want to be able to save and restore.

In relation to the diagram above, our implementation of the originator class will be a GameLog class. This class is responsible for keeping a log of the actions taken in the game.  The state of this game log can be persisted and restored by a GameLogMemory object, which is our implementation of a caretaker class.  The state of the game log itself is stored within a memento class, as in the diagram above.

We've looked at how we're going to implement the pattern, so now let's take a look at an example of it being used.

public class Program
    {
        public static void Main(string[] args)
        {
            GameLog log = new GameLog();
            GameLogMemory caretaker = new GameLogMemory();

            log.LogGameAction("1.) Player 1 plays Beetleback Chief");
            caretaker.Memento = log.CreateMemento();

            log.LogGameAction("2.) Player 1 Ends Turn");
            log.LogGameAction("3.) Player 2 plays Barter in Blood");
            log.LogGameAction("4.) Players realise an etb trigger was missed in player 1's turn");
            log.LogGameAction("5.) Players agree to roll back to before player 1's end of turn");

            Console.WriteLine("Current Game Log");
            Console.WriteLine(log.GetGameLog());

            log.RestoreMemento(caretaker.Memento);

            Console.WriteLine("Restored Game Log");
            Console.WriteLine(log.GetGameLog());
        }
    }

The output from running the above Program.cs file is the following:

Program output

In my implementation of the memento pattern, only the last saved board state can be restored.  You could of course implement a caretaker class which stores any number of previous states, which can be restored, perhaps by id or index.  For the purposes of this example, I've manually saved the board state at the moment I know we're going to want to rollback to.  In a real world scenario this would not be known, so you might want to instead trigger a saving of the game's state after every action or turn.

My GameLogMemory class is the caretaker implementation and looks like this, which is why I can only restore the last saved state of the game.

using System;

namespace MagicTheProgramming.MementoPattern
{
    public class GameLogMemory
    {
        public Memento Memento {get; set;}
    }
}

The GameLog class (originator), which is responsible for saving and loading board state is as follows:

using System;
using System.Text;

namespace MagicTheProgramming.MementoPattern
{
    public class GameLog
    {
        private readonly StringBuilder builder;

        public GameLog()
        {
            builder = new StringBuilder();
            builder.AppendLine("0.) Game Started");
        }

        public void LogGameAction(string action)
        {
            Console.WriteLine(action);
            builder.AppendLine(action);
        }

        public string GetGameLog()
        {
            return builder.ToString();
        }

        public Memento CreateMemento()
        {
            Console.WriteLine("Saving Game State...");
            return (new Memento(builder.ToString()));
        }

        public void RestoreMemento(Memento memento)
        {
            Console.WriteLine("Restoring Game State...");
            this.builder.Clear();
            this.builder.AppendLine(memento.State);
        }
    }
}

The code in the RestoreMemento function is where the magic happens (no pun intended) and the previous state of the board is restored.  In my example I'm just clearing the contents of the string builder and appending the old saved state, but it's more likely in a real world example you would re-assign your state object to the object returned by the State property of the memento object.

The memento I want to save in this example is just a string of text which contains the steps the game has gone through so far.  In a more realistic example this would probably be an object, with multiple properties, both private and public, which represents the state of your application.

using System;

namespace MagicTheProgramming.MementoPattern
{
    public class Memento
    {
        private string _state;

        public Memento(string state)
        {
            this._state = state;
        }

        public string State {
            get
            {
                return _state;
            }
        }
    }
}

I hope this straight-forward example has helped demonstrate an implementation of the Memento Pattern in an easy to follow and clear way.  Please feel free to check out all the code in this series from the GitHub repo and please feel free to fork it and play around for yourself.