1. Espiocracy
  2. News
  3. Dev Diary #26 - Simulation Engine 🧳

Dev Diary #26 - Simulation Engine 🧳

What's happening / TLDR: Developer diaries introduce details of Espiocracy - Cold War strategy game in which you play as an intelligence agency. You can catch up with the most important dev diary (The Vision) and find out more on Steam page.

---

Before we proceed to the second part of mechanics making up guerrilla warfare in Espiocracy, we ought to take a detour - Guerrilla Warfare 1.5 - to explore a system that plays a vital role in conflicts and deserves a dev diary on its own (be warned: it's more dev and more diary than usually, as you can judge from this very long first sentence).

Some design questions look like mathematical challenges. Find Z for the following X and Y. Find the outcome of an operation executed by particular operatives against a particular target. Find the result of an ambush given such and such belligerents. Find consequences of a raid, battle, sweep, or negotiation rounds. Given enough similar questions, like in mathematics, human mind recognizes the pattern and solves not just a single example but a larger category, perfecting methods that are useful beyond the original set of problems. This is the simulation engine in Espiocracy.

It's easier to understand it by roughly following the path of development. Originally (7 months ago), operations such as assassination or recruitment ended with one of the five hardcoded outcomes. On paper because in practice players avoid negative consequences and usually cannot afford extremely positive results, narrowing down the outcomes to just two or three variants. Bummer, let's solve this!

  1. We add additional non-exclusive outcomes (eg. you successfully recruited X but you left behind breadcrumbs that can endanger X)
  2. These should logically originate from the course of action (eg. "1" caused by a careless meeting under the eye of surveillance)
  3. Actions should be defined by other actions, including player decisions (eg. "2" caused by rushing to meet X in their homeland instead of waiting until X leaves the country on holidays)
  4. Additional outcomes are no longer always additional, they sometimes should directly affect the main outcome (eg. "3" -> "2" -> "1" blowing up the cover and halting the final approach)

Then, we essentially arrive at chains of actions and outcomes, A -> B -> C. Steps and branching, classic approach in many video games, maybe a little bit more emergent than usually.

Here's where things start to get spicy: there's nothing that prohibits such a simulation from spawning more simulations. On the one hand, we can horizontally proceed from entire simulation A to entire simulation B, for instance from recruiting X to eliminating Y given opportunity, crafting full adventure as we go - the staple of great movies and books. On the other hand, we can vertically flesh out details of any action, adding depth at will. The final pitch during recruitment operation can become a simulation inside simulation, with the course of conversation determining the result and non-linear details, and then mother simulation picking up from its result to, say, pursue hastened exfiltration of an operative because they were seriously threatened during the conversation.

Given that not every interesting set of actions in Espiocracy is an operation, this engine was naturally extrapolated to other parts of the game. It became not only a useful tool for connecting many mechanics in one space but also an interesting design lens: it's not just a procedural generation of the environment (although it can be!), it's usually a competition between active entities, whether it's a car chase, conversation, or nuclear bombing. Generalizing the pattern even further, the engine is really a universal game, intentionally developed as a flexible and efficient von Neumann machine, with inspirations drawn from a wide ensemble of games such as go, football, Nethack, or C:DDA.

[h2]Example: Ambush[/h2]

At their core, most simulations generate organic results in the background. You can always inspect them (if you have appropriate intelligence).

Mockup of ambush simulation.

This ambush consisted of two simulations, macro (daily) level and final approach (minutes). Available actions and course of events depended mainly on available people and weapons. Precise details such as killed and wounded in action, surviving materiel, or even survivors fleeing the scene are determined minute by minute. One can easily imagine many different sequences of events - for instance, without a heavy machine gun and multiple injuries in one sweep, the battle could go on much longer with varied consequences such as more damage to vehicles, rear squad returning with reinforcements, one of the sides running out of ammo, leader killed in action, and so on. All of these are fed back to the world during simulation, changing existing world entities and creating new opportunities (eg. here POWs to be interrogated, exchanged, or even recruited).

[h2]Example: Operation[/h2]

Some simulations can be followed in a more hands-on fashion. Naturally, by default you observe in detail and interact with operations:



When a player participates as one of the sides in a simulation, these are the usual types of available agency:

  • Changing objectives (eg. choosing a more attractive target that became available during operation)
  • Time-related dynamics (eg. accelerating, pausing / staying low, aborting)
  • Optional proactive actions (eg. many ways out, engaging other participants, using more costly methods)
  • Reactions, sometimes also prompted by operatives (eg. discovered three leads - pursue A, B, or C)

We'll certainly return to details of operations in the future.

[h2]Example: Operative Backstory[/h2]

A simulation can be also closer to procedural generation - they are currently used to invent backstories of intelligence operatives, which contribute to their views, traits, motivations, and vulnerabilities.



[h2]Simulationist Tangents[/h2]

In the next dev diary, we'll see more places and modes of interaction with simulations. We will be also returning to them in the future, since simulations find their use also in significant political changes (eg. coup d'etat), interactions between players (eg. negotiations between intelligence agencies), rare events, and so on.

Among unusual uses (tested, not universally implemented at the moment), simulations take part in the Monte Carlo approach to AI - hundreds of runs featuring decision P and then decision Q can be compared by outcomes to provide statistical aid, not only universal between simulation types but also potentially predicting wider strategic choices (that's right, simulation approximating entire Espiocracy). This surely requires a very efficient engine, so let's peek under the hood to see one of the optimizations.

From the get-go, the number of available events/steps was recognized as a possible significant bottleneck. If we want really detailed simulations, they should feature hundreds of probabilistic checks as frequently as possible, and if every one more event/step decreases performance, designers and modders would be outright punished for fleshing out depth - an antithesis to the engine's purpose. This issue is also present in the whole game with thousands of grand-strategy-style events multiplied by dozens of players. Although the latter can be usually sidestepped by distributing checks in time (eg. a dozen of different checks every day), it doesn't work for dynamic and relatively short simulation where anything should be available at any tick. Solution? Balanced binary search trees with probabilistic weights updated externally (on change of factors), nodes rotated for optimal search lengths, and then performing usually a single check per tick for all events.

Removing a node from a balanced binary search tree. Attribution: Nomen4Omen

As with most optimizations, it doesn't exactly eliminate the bottleneck, and instead just moves it into a more convenient place - in this case away from the number of events/steps towards much less frequent and less important probability updates. Here's a practical comparison of two approaches, standard vs tree-based, nicely illustrating O(n) vs O(log n) increase in computational complexity:



Even for the vanilla case of 100^3, we're looking at a performance increase from 24 cycles per second to 61 cycles per second, and this does not include other optimizations such as concurrent simulation runs or reuse of similar trees. More importantly, beyond 100^3, designers of simulations are no longer (severely) punished for expanding the number of events or steps.

[h2]Modding[/h2]

Speaking of designers, simulation engine is a prime example of modding capabilities in Espiocracy. You can modify any existing type of simulation, create new ones, and attach them to most of the mechanics and interactions. This is a good excuse to now get really technical.

Type (blueprint) of simulation is defined by XMLs describing:

  • Phases of simulation (eg. preparation, final approach, return - just a guidance instead of limitation, they can be easily shuffled during simulation)
  • Roles and sources of participants (eg. an intelligence agency and a target)
  • "Capabilities" of participants (eg. surveillance skills - mostly fixed parameters calculated from properties of participants)
  • "Developments" of participants (eg. trust - parameters developed or decreased during simulation, serving also as flexible memory)
  • "Objectives" of participants (eg. recruitment, guides autonomous agents and AI, can be null or changed during simulation)
  • Available "actions" assigned to roles (initiated by one of the participants, eg. intelligence agency meeting the target)
  • Possible events, simulation-wide and role-specific (happening to but not initiated by)
  • Optional display details
  • Setup sequence

As you can probably guess, actions form the beating heart of a simulation, suggesting a design lens again, this time: progress usually stems from active agents doing things, not just from sitting and waiting. Their various types are defined by a plethora of parameters (from temporal configuration to tactical intelligence requirements) and many possible effects. On start, when active, or on finish they can:

  • Modify capabilities and developments
  • Directly change probability of events
  • Launch branches of parallel actions (non-interruptive, resolved outside the main thread)
  • Launch another action from a probabilistically weighted set (with weights calculated from base value, capabilities, and developments)
  • Expose a set of reactions, available to defined participants (including the player)
  • Do just about anything because you can always attach a callback to another function in the game

Generally, simulations start with all participants launching a "background" action type appropriate for the role and the phase. In a usual operation, an intelligence agency starts with preparation in the background, the target - unaware of the battle - starts with simple enough "daily life" action in the background, and counterintelligence services go about their usual surveillance activities until they discover something suspicious. These background actions are always present and serve as the hook for an active agent in the simulation, with attached decisions (eg. approach the target or take a vacation), probabilistic steps, progress over time, and so on. When another action is launched, it can branch off as much as you want it to, including launching another simulation.

According to tests so far, this is an advanced enough system (a lot was refactored away) to simulate the gist of most competitions in the world. One of the examples, written for fun in one evening, is a very simple football match engine!

You probably have heard about programmer art but did you hear about programmer logs? Here you already get a seed of (sport) story from this simulation: after 1:1 in the first half, Blues quickly scored after the half-time, and then Reds reacted with two subs which led them to score the equalizer. Now just plug it into an event about West German - East German match...

This is how it roughly looks in the setup:



And from the perspective of the background action:



Note that it's the simplest possible approach - demonstrating how easy it is to boot up a new kind of simulation, and then how it can be elegantly fleshed out to hundreds of actions and events. With regular fallback to background action and unlimited connections between actions, it's also not as scary as it sounds, since we're not really designing a convoluted quest tree and instead we are closer to electric circuits or pseudoprogramming (and if these sound scary, check out quest trees even for fairly linear RPGs).

[h2]Final Remarks[/h2]

Now we're ready for continuation on guerrilla warfare - see you on August 5th.

If you're not already wishlisting Espiocracy, consider doing it:

https://store.steampowered.com/app/1670650/Espiocracy/

There is also a small community around Espiocracy:



---
"Almost every problem that you come across is befuddled with all kinds of extraneous data of one sort or another; and if you can bring this problem down into the main issues, you can see more clearly what you are trying to do and perhaps find a solution. Now in so doing you may have stripped away the problem you're after. You may have simplified it to the point that it doesn't even resemble the problem that you started with; but very often if you can solve this simple problem, you can add refinements to the solution of this until you get back to the solution of the one you started with." - Claude Shannon