I know I am not the only player that has gotten to the end of a game and thought: "What if I played that turn differently?" ZumboSim had this cool feature way back in GD-01 where, if both players agreed, it could rewind the game state. It worked well enough, and I have not followed their development, but it got me thinking... what if a simulator was built for time travelling from the start. So I started some experiments on modelling the data and got a very basic engine running. I could play cards from a preset deck (with no effects) and pass the turn. But critically I could record the game state at every point along the way to jump back to any point in time.
My actual job became a lot busier though and I barely had time for Gundam, let alone work on a side project. Now with some free time it is time to pick it back up. After trying to expand it, it was clear that I was on the wrong path. The goal did not change, but state snapshots were not going to get me to where I wanted. So I started refactoring and the following are some of how I got to what is a mostly functional version of what I really wanted to see built.
Fundamentally games have been reduced down to a series of actions. Specifically though a game log is only interested in input actions. So what are input actions?
Those generally make sense, so what are not input actions?
Effectively, if a game is deterministic, then if you record all of the inputs throughout the game, then you can go from a starting state and play forward to any point in the game. It is also significantly lighter weight than trying to snapshot game states. But the big advantage is that you do not need to answer the question of "when do I take a snapshot?". When snapshotting you can easily get into trouble if you miss any intermediate state. A missing state means that trying to rewind and fast forward will ultimately fail and leave you in an inconsistent state.
The concept of capturing input actions is not just a selective filtering of a larger set of events. Input actions are the only actions that step the engine forward. Everything else is derived from those actions. State watchers, event triggering, phase and step transitions all of them are derived from the action log. This ultimately means that given an action log along with the version of the engine that recorded it we can guarantee that time travel can recreate a game by definition.
A couple notes though for the reader:
Great, so we know how we are recording the data, but what can it actually be used for? The first thing is replays, and it is the most straightforward given the log explanation above.
Since we have a log, whenever a game ends we take the game log and store it. Either player can then load up the log and replay it, step by step.
Loading one back gives you a timeline with one frame per recorded input, plus a frame zero for the opening position. It is pretty fun to see each decision, and no longer do you need to do anything special to record a game; it just falls out of the engine.
Finally back to our original question, what if we could rewind time and start again from any point in the game. Well given an action log and an engine it really is not that hard. Scrub a recording to any frame, press the branch button, and pick a seat. Seriously, that is all there is
...well at least for a user.
So what is going on here? There are a few steps to create a new game, but similar to before it is all driven by the event log. The first step is to pick the frame in the game to start from, which now needs a definition. A frame is a point in the game that is ready to accept an input action. These are defined as the points at which the engine has settled an action. Settling is fundamentally "the rest of the game". Suppose you choose to attack a unit: the game goes through the action phase, and upon the second "Pass" the engine steps forward to the damage phase. This triggers events that need to resolve or settle.
This includes applying damage, triggering an event and resolving any effects, then determining which units to destroy, triggering an event and resolving effects. This process continues until the engine reaches a point that needs input. This might be a choice that comes as a result of an event, such as 「Unicorn Gundam (Unicorn Mode)」 , or if there are no choices to be made the engine will return to the Main phase and wait for input. These frames are the places where we have a game state that can be snapshotted, and they are the frames you can scrub through in a replay. Any intermediate state cannot be snapshotted.
With those parts defined we can describe how a fork gets created.
Now there is one caveat to this that is a design decision, and maybe I revisit it, but for now determinism persists when going from a game to a clone. This means that if you play a game, then fork the replay, you will be able to predict random events that are yet to come in the fork of the game. This is intentional as what I want to model is the "what if" of my decisions. I did not want to fork a game multiple times and then get multiple outcomes due to different re-seeding of random events. Maybe in the future this could be an option, but for now the determinism of a game travels forward to its forks.
There is plenty more work to do for bug fixes, non-functioning card effects, and ensuring that the engine is fully compliant with the comprehensive game rules. I did not go into it here, but the project is built for both player vs player and player vs bot. A bot is treated just like another player and it sends inputs to the server as if it were a player. It makes for a really nice abstraction and ensures the bot does not learn information it shouldn't.
For the time being bot play is the only version enabled on the site. I do not intend to enable player vs player until I get what I believe to be full coverage for card rules and effects. That point in time is not far off, but it is intentional as I do not plan to work on matchmaking or the like until the engine is fully functional.
If this interests you at all and you want to either try it out or help find bugs, you can log in with Discord at duel.gunda.ms and request an invite. The project will be invite-only until I have something that I am confident is correct. Once you have access please be sure to send feedback and let me know what cards do not work as expected, and in particular where the bot makes misplays. There is a lot of tuning to do on the bot, and I want to make the Ace bot truly competitive.