Simulating combat in Dark Fort
A two-part series exploring Dark Fort, the solo/micro game that became MÖRK BORG
Welcome to Skeleton Code Machine, a weekly publication that explores tabletop game mechanisms. Spark your creativity as a game designer or enthusiast, and think differently about how games work. Check out Dungeon Dice and 8 Kinds of Fun to get started!
This is Part 2 of a two part series. Read Part 1 first.
Welcome to Part 2 of the series on Dark Fort, the solo/micro game that became MÖRK BORG.
In Part 1 we looked at the thematic elements of Dark Fort. Specifically, how two illustrations and some careful writing created a thematic experience on a single sheet of paper.
In this part, we explore the combat mechanisms, and use Python to simulate some of the outcomes. We’ll use code to answer the question, “What happens when I fight a Ruin Basilisk one million times in a row?”
Combat in Dark Fort
In Dark Fort, when exploring a room, there is a chance that you will encounter either a Weak or Tough monster. There are eight total types of monsters, four weak and four tough. They range from the weak Blood-Drenched Skeleton (3 points, 6 hp) to the tough Small Stone Troll (5 points, 9 hp). Some have special abilities like the Neco-Sorcerer who has a 17% chance of transforming you into a maggot.
Dark Fort combat takes place in turns using d6 and d4 dice:
Roll 1d6 for attack, adding any weapon modifiers.
If the result is equal to or greater than the monster’s points, you hit and do your weapon’s damage (e.g. d4).
If the result is less than the monster’s point, you miss and you take the monster’s damage (e.g. d6).
This continues until either you die (i.e. 0 hp), the monster dies, or you successfully flee the fight.
There are other things that can make combat more interesting like magic items (e.g. Cloak of Invisibility) and scrolls (e.g. Summon Weak Demon).
Simulating combat with Python
I was curious what the chance of success was vs. each type of monster in Dark Fort. I also wondered if it was better to have a weapon with an attack bonus (i.e. more likely to hit) or a damage bonus (i.e. does more damage when it hits).
Combat is simple enough that it can be simulated in Python:
RUIN BASILISK
ROUND 1: YOU = 15, MONSTER = 11
YOU HIT 5 dmg! Rolled 6 vs. 4
ROUND 2: YOU = 15, MONSTER = 6
YOU MISS AND TAKE 2 DAMAGE! Rolled 3 vs. 4
ROUND 3: YOU = 13, MONSTER = 6
YOU HIT 5 dmg! Rolled 4 vs. 4
ROUND 4: YOU = 13, MONSTER = 1
YOU HIT 4 dmg! Rolled 6 vs. 4
Combat ends. You have 13 HP remaining.
------------------
Win: 100.0 Loss: 0.0 Iterations: 1
In this example some assumptions are made:
The player always starts combat with 15 HP.
The player has no armor (-d4) and no special items.
Combat continues until either the player or monster dies.
Loot, earned points, silver, and progression are all ignored.
After-combat effects (e.g. transformation into a maggot) are ignored.
Running a few examples, it appeared that the chance of defeating most monsters was high. In addition, combat seemed to last maybe three or four rounds before ending.
Fighting a million basilisks
To get a better sense of the actual probability of winning, I simulated each type of combat a million times. Here are the results:
Weak monsters: Combat versus a weak monster almost always results in a victory, especially when using the sword. It makes sense that the results for each type are similar because mechanically they are almost all the same. The skeleton and cultist have the same points, damage, and HP. The goblin and hound have only minor changes. The main difference is in the type of loot that they drop.
Tough monsters: Combat versus tough monsters is quite a bit more challenging, as to be expected. The Stone Troll is the hardest because it has 5 points, making it harder to hit. The Ruin Basilisk is also tough due to its high HP.
Weapons: For short combat, getting a hit matters more than doing a lot of damage. The weapons with +1 attack are the best. Of the two, the sword is better than the dagger.
Note that these results are for combat where the player starts at full health (i.e. 15 HP). In a real game, these encounters would slowly grind down your health. The first fight against a goblin might result in a win, but you would also walk away with less hit points. That next battle against a cultist begins with you at 14 HP instead of 15 HP.
Also, there is a opportunity to level up in the game and increase to 20 HP. For reference, fighting a basilisk with a sword and starting with 20 HP vs. 15 HP increases the chance of winning to about 95.6%.
How long should combat last?
One of the first Skeleton Code Machine articles was Therg Fights a Skeleton, in which we simulated MÖRK BORG combat. In that exercise, combat lasted between 1 and 43 rounds, with an average of 7 rounds. In general, however, combat was short. Over 30% of the time the fight was less than 5 rounds, and over 25% of the time it was over in just one round. That first blow can be deadly!
In Dark Fort, combat is also relatively short. Simulating an encounter with a cultist, combat is usually over in two or three rounds:
This feels about right, and seems like an OK amount of dice rolling.
The combat can, however, extend out to eight or nine rounds. This is more likely after you’ve leveled up and start with 20 HP instead of 15 HP. I’m not sure where the line is, but as combat approaches 10 rounds it might become too long.
I continue to wonder how long back-and-forth combat should last in solo games.
There is no right answer, but I think it is worth thinking about!
Single round combat
Some of the early iterations of Eleventh Beast had turn-based back-and-forth combat like Dark Fort. You would collect your wards and weapons to prepare for combat, and then slug it out until either you or the beast died.
I wanted to try to change that concept, and ended up flipping the traditional combat on its head: Instead of a bit of prep and then a longer combat, what happens when almost the entire game is prep and finishes with brief combat?
In the final version of Eleventh Beast, the player spends most of their time talking to witnesses, collecting rumors, and preparing for battle. The player agency is focused on pushing your luck: How long do you collect more rumors before cashing them in on usable wards and weapons?
Combat usually only happens once at the end of the game. You’ve built you dice pool and now you see if it works. There is a wound system that allows for rerolls, but combat won’t go on for long.
Conclusion
Some things to think about:
Python simulations can be helpful: We’ve used Python before in Therg Fights a Skeleton, Peggy Steals an Artifact, and A Hundred Thousand Burned Hackers. Assumptions need to be made and it requires simplifications, but it’s still a useful tool when doing initial design work. Also, there’s nothing magic about Python. Any language will do.
Try to invert common conventions: As with Eleventh Beast, sometimes you can spark your creativity with trying to flip common ideas. For example, if combat is usually long, think about how short you can make it. Can it be just one round?
Think about the amount of dice rolling: Rolling dice is fun! But doing it over and over can get old pretty fast. One of the early iterations of the upcoming Ratsail had way too much, and needed to be changed. I’ve found the only way to test this is to grab your dice and actually play the game.
Now that you know the stats, which weapon would you choose?
— E.P. 💀
P.S. Skeleton Code Machine has been nominated for the “Best Online Content” ENNIE Award! You can vote now to decide who wins!
Skeleton Code Machine is a production of Exeunt Press. All previous posts are in the Archive on the web. If you want to see what else is happening at Exeunt Press, check out the Exeunt Omnes newsletter.
Perhaps it's the dice goblin in me, but more dice is more fun! That said, I think the line between fun and slog is right around 10 rounds of solo combat. Really enjoyed this two parter on Dark Fort and was inspired to make a small game in the same vein!
It's a good idea to use Python or any other language to play test certain sections of a TTRPG. I'm a programmer by trade and have been looking at proving basic ideas using a combo of Python for fast large tests or using a visual language like Vue/Javascript to build things like random character creators.