Game Design, Programming and running a one-man games business…

Balancing Democracy 4 using stats and number crunching

I’m still working away on improving the balance for my political strategy game ‘Democracy 4‘ which is in Early Access, and likely the hardest game to balance in the history of humankind, due to the insane number of interconnecting variables.

Something I was working on today was the likelihood of each dilemma popping up. These are occasional ‘situations’ that are urgent and need the player to make a choice one way or the other. Basically the choices are always hard, because they mean upsetting someone, or causing an effect that may be undesirable given your goals.

For example, if a company is about to collapse, you could bail it out (pleasing the workers, but setting a bad example for other companies, maybe upsetting capitalists, who prefer a free market), or not, and your decision depends on who you can afford to (or choose to) upset at that moment.

Some of the dilemmas are totally random, but most are influenced by some variables so that they seem fairly ‘relevant’ to the way you are playing the game. So they all have inputs based on certain variables, and thus as the game designer I don’t know in advance when they will happen (they are not timed or scripted in a conventional way).

This is all very easy to design and code, but the problem is you can end up with the inputs being unbalanced, and thus maybe everybody is sick of seeing dilemma A, regardless what they do or how they play, whereas interesting and exciting dilemma B hardly ever gets triggered. For a small studio with a limited budget, having any game content that is rarely seen by the player is an efficiency disaster.

Luckily I (anonymously) keep track of every trigger of a dilemma by version number, so I can guild up big databases of such events. The events database is currently about 4GB in size, and is testing the limits of my MySQL optimization abilities. Thus I copy the last 2 versions events into a much smaller (600MB) database for me to query. That lets me get cool charts like this:

Which lets me visualize the extent to which some dilemmas (the ones with long red bars) are triggering WAY more than they would if their distribution was completely random. I’ve been using this data for the last few versions, but I still have the unbalanced issue despite a lot of manual revisions, so I’m working on code today to visualize this better.

I actually found this easier in excel, once I had data that showed me every dilemma, and what percentage of dilemmas each one represented, over the previous 2 versions:

I can then work out what percentage they *should* be, thus the error in terms of over or under representation. Luckily I’m analyzing 100 dilemmas, so its a simple 1%. I calculate an error in percentage terms, for each version, then calculate the change. A positive value is movement towards better balance, a negative one is movement away, (worsening the balance). This can then be conditionally formatted in excel:

Which is a cool trick, but also depressing to see the extent to which I often am having a trivial effect towards balance, or even making things worse (negatives).

However, at least having a nice process to visualize this stuff makes it more obvious to spot where I am being too timid in my corrections. In an ideal world, this would all be integrated into a single web query, and in an ideal, ideal world, some sort of cool machine-learning AI would be making these adjustments instead of me…

But hey, its a big step up from just ‘setting some numbers and then playing it a lot before release’ which is what I did ten years ago :D I’ll gradually get this stuff more and more optimized.

An indie strategy game translation business case. lets do the Math(s)

(My wife is triggered when I say ‘math’ because its American. we say maths here in the land of Monty python)

I have a bit of a rule that I try to follow, that when I am trying to make a business decision, I always calculate forwards not backwards. What I mean by that, is I try not to think ‘I’d like a Polish translation of Democracy 4. is it justified?’. That already sets you on the foot of WANTING it to be. The best way to do this is to set out the number that justifies a greenlight, then to work out the equations as best you can, then unemotionally go with the result.

Democracy 4 is currently in English, French, Italian, German, Spanish and Brazilian Portuguese. The classic strategy game translation pattern is EFIGS. I have added Brazilian Portuguese because it was done by someone who got in contact with me direct, so was affordable.

Anyway. Lets look at Russian, Japanese and Polish.

If I look at Democracy 3, which was translated into Russian and Polish but not Japanese, I get the following full-history revenue breakdown. Note that the game did not *ship* initially with those, the languages were added later.

  • Russian revenue $91k gross
  • Japanese revenue $45k gross
  • Polish revenue $89k

Democracy 3 sold super well, over 6 years with 4 expansion packs, so the total revenue is really good, and obviously Democracy 4 revenue right now is WAY lower, but I’m trying to plan ahead.

Steam take 30%, and there are refunds, chargebacks, and sales tax, so we actually get about 60% which gives me:

  • Russian net income $54,600
  • Japanese net income $27,000
  • Polish net income $53,400

The cost of a translation for the game is roughly $7-9k. It can go higher with more interesting languages like Japanese, so lets say the high end and assume $9k. We are likely to add a fair bit of extra policies etc. over time, and need to keep everything up to date so lets budget for a final $10k per language. I also have to implement it, play test it, and deal with any problems that might come up that have not been seen before in earlier languages. This generally isn’t taking me more than a few days each time, but lets push the complete implementation cost for each one to $12k.

This currently assumes that Democracy 4 sells the same as Democracy 3. This may not be true due to the following negatives:

  • People might be sick of politics due to real world events
  • Much more competition in the games market in general
  • Players of democracy 3 may have ‘had their fill’ of politics and decide not to upgrade

However there are also positives:

  • Young people seem much more engaged in the Bernie sanders / alt-right / Jeremy Corbyn / AOC era than they were during the relatively bland time when D3 came out.
  • Steam is bigger, with a larger addressable audience now
  • D3 was a hit, and some people will be easy to persuade to get D4 if they enjoyed the previous game.

I’m not super risk-taking, so lets be pessimistic and assume that the balance of these factors means that the long term income of Democracy 4 (which I should have mentioned is a MUCH better game btw) is likely to be about 66% of that of Democracy 3. That gives us these values:

  • D4 projected Russian net income: $36,036
  • D4 projected Japanese net income: $17,820
  • D4 projected Polish net income: $35,244

There is a big assumption here, and that is that people playing Democracy 3 in country X…were playing it in the local language, and would NOT have bought an English-language version. The way to check this is to look at the percentage of sales in the first year of release that were Chinese for D3, and compare it with the percentage in the most recent year (where Chinese was translated). This is not perfect, and china specific but we get this:

  • Percentage of D3 sales to China year 1: 0.16%
  • Percentage of D3 sales to China last year: 4.33%

Same for Polish:

  • Percentage of D3 sales to Poland year 1: 0.53%
  • Percentage of D3 sales to Poland last year: 2.7%

So we can see that the effect of a native language version boosted Chinese sales and Polish sales, and the relative native-language-applicable revenue was 96% and 80% respectively, so lets split the difference and assign a value of 88% to the revenue:

  • D4 projected income from native Russian: $31,711
  • D4 projected income from native Japanese: $15,681
  • D4 projected income from native Polish: $31,014

This all makes a super-convincing case to do Russian and Polish, and a slim, but arguable case for justifying Japanese. There are of course other factors, and huge margins for error. One factor is the multiplier-flywheel effect. If I could sell an extra 10,000 copies of Democracy 4 tomorrow and get no money at all for it…its kind of in my interests, because then it climbs up the ‘top sellers’ lists and gets more visibility. Also, I may already be selling copies of D4 to english-speaking players in Polad/Japan who cannot recomend it to their non-english speaking gaming friends yet. Thus, the word-of-mouth of the game is currently not at full capacity.

PLUS! Democracy 4 is not just sold on steam, but direct from us (via humble widget), through GoG, Humble Store and the Epic store. Epics royalty rate is famously higher, and that then changes the figures slightly again in my favor…

I shall mull this over, because no indie dev suddenly commissions $30k more contract work without being 100% sure and sleeping on it, but I thought people may be interested in seeing the thought process.

I know that crowd-sourced translations are a thing, we even started it with D4, but progress is too slow, and also too unpredictable. It also runs the risk of multiple contributors having different translations for the same word in various places throughout the game. I think professional translations are a better system. YMMV.

An amazeballs chart of Democracy 4 playtime since initial release.

Thanks to the wonders of excel, and php etc, I can present this amazeballs chart showing both the number of games, and the number of hours in total playtime each day for Democracy 4, my indie political strategy game.

I think people can overanalyze this stuff TBH, and I don’t think there is anything especially amazing to note. This is ALL playthroughs, not just steam copies BTW. There is a very nice bit at the end where the hours played decouples upwards from games played, implying people are playing for longer sessions (which generally you equate with better balance, and happier customers).

I have quite a lot of Democracy 4 stats, but they are much more interesting to me than most people, and the data can be a bit complex. Probably the most interesting stuff is player feedback on what they think the developer should work on next (taken from in-game voting). Here is the data from the latest version:

I do find stuff like that pretty helpful tbh.

Updated Democracy 4 to build 1.19

Here is a full list of changes in this version:

[1.19]
1) Fixed bug on retirement age selecting top slider value.
2) High corruption now impacts the cost of the military.
3) Fixed a bug that caused an unneccesary error message when selecting a language that exists purely in a mod.
4) Re-balancing of some event triggers.
5) Road building changed so that it no longer appears to boost car usage at minimum policy level (moved to default value).
6) Added mouseover information for each point on the historical finance chart.
7) The global economy fluctuates more randomly now.
8) Improvements to some internal balancing systems should make the game swing less suddenly between success and failure.
9) The next turn report now includes a warning that the voters have detected a policy reversal.
10) New policies: Religious Tax, Religious banknotes, State Religion and Ban Women from driving.
11) New country: Spain.
12) Certain event windows such as donor changes, credit rating changes and assassination attempts now have a details button.

Hope ya like em! Some more balancing and country specific tweaks coming over the next few weeks. Don’t forget to vote on your priorities from the main menu, and thanks for supporting the ongoing development of the game!

If for some crazy reason you don’t have a copy yet, grab one here, comes with a steam key:

The war on needless draw calls with GPU profilers

Democracy 4, (my latest game) is quite evidently a 2D game. There is an assumption by players and developers alike that 2D games always run fine, that even the oldest, crappest PC will run them, and that you only need to worry about optimisation when you do some hardcore 3D game with a lot of shaders and so forth.

Nothing could be further from the truth! There are a TON on 2D games that run like ass on any laptop not designed for gaming. I think there are a bunch of factors contributing to this phenomena:

  • The growth in sandbox building/strategy games with huge sprawl and map sizes that can result in a lot of objects onscreen
  • An emphasis on mobile-first computing has resulted in a lot of low-power low-spec but affordable CPUs/GPUs in laptops
  • An emphasis on ‘retina’ display aspiration ahs meant high screen resolutions being paired with low-spec GPUs
  • A lot of game developers starting with unity, and never being exposed to decent profilers, or ground-up programming, without understanding how video cards work

So my contention is that there is a bunch of people out there with mom or dads hand-me-down laptop that was bought for surfing the internet and using word, and is now being pressed into service reluctantly as a games machine. These laptops aren’t about to run Battlefield V any time soon, but they can run an optimized 2D game very well indeed… IF the developer pays attention.

Its also worth noting that markets like Africa/India/China er definitely good targets for future market growth, and a lot of these new gamers are not necessarily going to be rocking up to your game with a 2070 RTX video card…

I recently did a lot of optimizations on Democracy 4 and I’d like to talk through the basic principles and the difference it can make. Specifically how to double the frame rate (from 19 fps to 109fps) on a single screen. The laptop spec in question: Intel core i7 6500U @ 2.50GHZ CPU Intel HD Graphics 520 GPU, screen res 1920×1080. Here is the screen in question:

This is the ‘new policy’ screen in Democracy 4. TBH this screen looks a lot more complex to render than it actually is, because of some trickery I’d already done to get that lofty 19 fps. That background image that is all washed out and greyscaled is not actually rendered as a bunch of tiny icons and text. its drawn once, just as we transition to this screen, and saved in an offscreen buffer, so it can be blapped in one go as the non-interactive background to this screen. I use a shader to greyscale and lighten the original image so this is all done in a single call.

So here we are at 19 fps, but why! To answer that I needed to use the excellent (and free) intel GPA performance tools for intel GPUs. Its easy to use and install, and capture a trace of a specific frame. With the settings i chose, I end up with a screen like this:

The top strip shows time in this frame, and how long each draw call took (X axis) and how many pixels were affected (y axis). A total of 158 draw calls happened in this frame, and thats why this puny GPU could only manage things at 19 fps.

Its worth setting up the frame analyzer so that you have GPU elapsed time on the X axis, otherwise all draw calls look as bad as each other, whereas in fact you can see that the first bunch of them took AGES. These are the ones where I draw that big full-screen background image, and where I fill the current dialog with the big white and blue background regions. It seems like even fill-rate can be limiting on this card.

By stepping through each draw call I can see the real problem. I am making a lot of really tiny changes in tiny draw calls rendering hardly anything. Here is draw call 77:

This draw call is basically just the text in that strip in the middle of the screen (highlighted in purple). Now this is not as inefficient as a lot of early-years gamdev methods, because the text has actually been pre-rendered and cached, so that entire string is rendered in a single strip, not character-by-character, but even so its a single draw call, as is each icon, each little circular indicator, and each horizontal line under each strip.

So one of these strips is actually requiring 4 draw calls. And we have 18 of them on the screen. So 72 draw calls. Can we fix this?

Actually yes, very easily. I actually do have code in the game that should handle this automatically, by batching various calls, but it can become problematic as limits in buffers can be reached, and textures can be swapped, requiring an early ‘flush’ of such batches, which if they happen in the wrong order, can mean missing text or icons. As such, the simplest and easiest approach is to change the code that roughly goes like this:

for each strip
  Draw horzizontal line
  Draw Icon
  Draw little circle icon
  Draw Text

…to something more like this…

for each strip
  Batch horizontal line
Draw all lines
for each strip
 Batch Icon
 Batch little circle icon
Draw all Icons
for each strip
 Batch text
Draw All Text

Which on the face of it looks like more hassle. isn’t it bad to iterate through the list of strips 3 times instead of 1? Well kinda…but I’m GPU limited here. the CPU has time to do that stuff, and actually there is likely NO TIME LOST at all, because of CPU/GPU concurrency. Something people forget is you are coding 2 chips at once, the CPU and the GPU. Its very common for one to just be waiting for the other. While the GPU is doing ‘Draw All Lines’ my CPU can keep busy by building up the next batched draw call. Concurrency is great!

I guess we nee to take a moment here to ask WHY does this work? Basically video cards are designed to do things in parallel, and at scale. A GPU has a LOT of settings for each time it does anything, different render states different blend modes, all the various fancy shader stuff, culling, z-buffer and render target settings etc. Think of it as a MASSIVE checklist of settings that the GPU has to consider before it does ANYTHING.

When the GPU has finished its checklist, it can then pump a bunch of data through its system. Ideally everything has been set up with huge great buckets of vertices and texels, and they can all blast through the GPU like a firehose of rendered pixels. GPUs are happiest when they are rendering a huge ton of things, all with the same settings. The last thing it wants is to have to turn off the firehose, and go through the darned checklist again…

But with a new draw call, thats pretty much what you do. Unless you are doing fancy multi-texturing, every time you switch from rendering from texture A to texture B, or change the render states in some other way, then you are ‘stalling’ the video card and asking it to reset. Now a GPU is super-quick, but if you do this 150 times per frame… you need to have a GPU that can handle this 900 times a second to get the coveted 60fps. Thats without the actual rendering, flipping or anything else…

So the less resetting and stalling of the card the better, which means BATCH BABY BATCH!

Now with the re-jig and those strips being done with the new algorithm (plus a few other tweaks), the GPA analysis looks like this:

Let me here you shout ‘109 FPS with only 52 draw calls!‘. Its also worth noting that this means less processing in general, and thus less power-draw, so that laptop battery life will last longer for games that have fewer draw calls per frame. Its a missive win.

There is actually a lot more I can do here. Frankly until I get to that tooltip right at the end, ALL of the text on this screen can be done in a single drawn call near the end. The same is true of a bunch of other icons. Essentially all I have done here is optimize the left hand side of the screen, and there is scope to go further (and when I have time I will).

I thoroughly recommend you keep (or get) an old laptop with a low spec video card, and slap the intel GPA tools on there. Other such suites of profilers exist for GPUs, the AMD one is pretty good, and nvidia have nsight as well. It all depends on your GPU make obviously.

I do wish more people were aware of the basics of stuff like this. Its hugely rewarding in terms of making your game more playable. BTW these tools work on the final .exe, you don’t need a debug build, and the engine is irrelevant, so yes, you can use them on your unity games no problem.

Hope this was interesting :D