Everything should be made as simple as possible, but not simpler.

Maybe not literally Albert Einstein

What writing on complexity would be complete without such a trite lead-in as above? Indeed, I am hard-pressed to think of any pretense to sagacity that would omit such a cute extract, that would disavow stapling oneself to the coattails of other, greater, thinkers with a blockquote and an unresearched attribution. Let it never be said that I disregard the proper forms!

I assure you first that I have seen some things. They’re probably nothing remarkable in the grand scheme, but as relates to computer programming I mean this as a lamentable indictment of how people generally understand problems, think about solving problems, understand computers and programming languages, and program computers to solve problems. By people here I mean the educated professionals whose daily responsibility it is to do these things. Yes, they are hard things, but somehow we manage to make it harder for ourselves anyway.

Boo complexity

A briefly enumerated rant to illustrate the above point:

  • Someone wrote a fully compiled set of programs to manage creating our release, and executes them in the pre/post actions of various MSBuild projects, when a much simpler Powershell script that delegated to a few existing utilities, and then invoked the build would have accomplished the same task. Since it’s still in development, the cost for each tweak is several times what it could have been. For added points, it’s three programs, each of which jumbles several unrelated functions together, distinguished via command line arguments (but no documentation).
  • Someone (who in my opinion is the best person we have on our team hands down, but this just goes to show) wrote a no-kidding state machine (enum states and giant switch-case-in-a-loop logic) to handle starting up a form. Why? I don’t know, the logic it encoded could instead have been represented by a bunch of code executed sequentially, with an error handling block at the bottom.
  • Same guy (which just goes to show) had a problem: a master list of records, and a set of annotations that can attach to those records. There are millions of records; the annotation set is by comparison extremely sparse (at worst a few hundred) but could in theory be changed outside the application. He wants to detect this and live-update with the new annotations.
    • His proposed solution? Add a new column to the master list, and any time there’s a change in the annotation set, increment the new column’s value. Then to live-update, recheck all the loaded records (and there will be thousands of them) and compare these versions; if they differ, load the new annotation.
      He was designing this, outlining it to another developer, and was getting ready to approve its implementation when he explained this to me.
    • My first contribution: Don’t bother, it’s unlikely that an actual user will hit this scenario, and even if they do, the consequence of not having the newest annotation is not severe.
    • Somehow, this failed to persuade, so I made my second point: Tacking a word of memory onto each entry in our already enormous master list, and then re-querying thousands of records and comparing attached values is not a fast and low-memory operation. Observing that the last-modified-date of our file-based database has updated, then querying mere hundreds of annotations, dropping them into a map keyed on the associated record, then doing a constant-time (amortized) check per open record to see if any have an annotation is by comparison fast and cheap (since the annotations will be in memory anyway, the map really only costs us the space of the index + reference).

So complexity is terrible and we should never do it, right?

Yay complexity

Let me tell you about the Big Man On Campus (BMOC). BMOC once tried to chew me out in front of my manager and my manager’s manager for being insufficiently respectful of him, based on my criticism of all the terrible technical decisions he was making. In addition, he hated the software I worked on, had no idea what it was or did or how it was designed anyway, and generally resisted any calls to write C# programs in C#, preferring instead to continue pretending that everything worked just like C and Visual Basic.

Unrelated to the purpose of the post, but to conclude the anecdote, I do not recommend as general career advice answering the statement, by someone with more clout than you, of “Don’t act like I’m an idiot, I’m not an idiot” with a carefully enumerated list of all the idiotic things said and done by such person, along with a technical explanation of why each thing was not smart, what the inevitable consequence of such a thing will be, and what the better choice would have been. But if you can pull it off, it is glorious.

Also unrelated to the post, I need to figure out how to make this platform format asides, because I wander off terribly.

One of BMOC’s favorite complaints was that something is “overengineered”. One friend who left the group would snidely say that this meant any engineering at all had been done. One day, I decided to play a game [1]: any time something was called overengineered, I would ask why. Then I would explain what each piece of it accomplished, how they interacted, and what you’d have to lose in functionality or gain in complexity to do it differently. Then I would ask: If you want it simpler, what features would you like to lose in exchange?

So… yay, complexity? Still not quite.

SHOPPING

Complexity is a currency – its purpose is to allow you to purchase functionality. Inherent complexity is the amount that’s necessary to get something done. Accidental complexity is the result of doing something wrong or unnecessary or maybe even just a little fancier than you need – but this too is a judgement call. Dependency injection with an IoC framework is complex – there’s a price paid for all the reflection necessary to build up objects, never mind the intellectual complexity added for someone who doesn’t understand these concepts. It would be simpler to just directly new up all the objects you need… until your object graph is sufficiently complex itself, until you realize you need the ability to shim things in at boundaries to test, or to add caching or logging strategies, or swap out behaviors for different environments by specifying everything in a central location so that the changes aren’t smeared across your entire code base…

Complexity is currency. I like buying awesome functionality. But I am also stingy. [2] So I will spend complexity to get things that I decide I want. I will also resist overpaying, and the grosser the upcharge, the harder back I will push. There are trade-offs even when deciding that I want to accept a complexity charge – I can make life easy for myself at the expense of making the code harder to maintain (this is making someone else pay later so that I can have cool things now, also called going into debt), or I can balance having simpler logic relying upon a bunch of complexity hidden in a library, versus doing harder work myself but having only what I need. I can trade between space and time (and what physicist can ever claim that?).

It all comes down to this… a dungeon, and a dragon

And all of this is great, but it’s how I think about things generally, so this is also something I regularly try to weigh when designing the game.

By way of leading principle, forcing a decision is interesting. A game engages the people playing it when it takes a position that has multiple potential subsequent positions, and forces one to be chosen and the rest to be discarded.

Rolling dice is one way to force a decision. And we love doing it, which is why we do so much of it. But rolling dice can be more complex than not rolling dice – you have to establish your thresholds, cast the bones, perform arithmetic, and then interpret the omens. So in any given design, the question of “Should I roll some dice here?” must balance that cost against how hard it would be to force a decision some other way, and what the benefit of dice-as-neutral-mediator would be versus whatever the consequence of the alternative is.

Consider the general notion of a skill check, or other general action resolution. I’ve done my best to simplify establishing thresholds – there are only five possible difficulties, and you just pick one. The arithmetic is slimmed down too – standard bonuses are very small numbers (e.g. 0 and 1), and there are very few sources of additional bonuses and penalties. The omen interpretation is somewhat open, but there are guidelines – something good happens, something kind of unpleasant happens, or something really unfortunate happens. The effect of dice-as-neutral-mediator here is to give something like but very very less than that stomach jolt at the earliest moment of free-fall, the sudden sensation that you’ve attempted something, and the consequences of it are now out of everyone’s hands, player and GM alike.

Why not roll dice, and what would the alternative be? Consider: if the outcome to all this deliberate tension-building is “nothing happens” or worse, “nothing interesting happens”, then why go through the rigmarole? Why permit the constant and consistent deflation? Maybe you’re talking through a scene and it’s fairly obvious what the consequence should be. Why not just go with it? That’s the rule – if you don’t need the moment of tension, and there’s no room for terrible consequences, leave the dice alone, because rolling is slower than not rolling. If you do need it, it’s there, and it’s kept as simple as I can make it while still doing what I want it to do.

I eventually get around to a similar calculus on the various parts. For example, being wounded used to track which level the wound filled, and had a gradient for recovery times and bonuses for pushing them. This was meant to add some tension – the closer to death you came, the stronger the temporary benefit you could gain in exchange for sabotaging your recovery. Mostly what it accomplished was adding extra information for players to track, while dramatically increasing the utility of magical healing over non-magical healing, and split adventuring groups into two tiers of wildly different capability – groups with a healing magician, and groups without, and it was a split that didn’t seem comparable with the divide caused by any other Art selection. So I overpaid for that feature, and decided to trade it in for something simpler. Now wounds have the same recovery time, and the same consequences for pushing them.

Footnotes

[1]: I am an asshole.

[2]: And an asshole.

Advertisements

2 thoughts on “Complexity as Currency

  1. That’s a great framework for thought. I briefly considered, “can a bit of complexity ever be valuable in itself?”, as “we’ve included this as a fun thing for players to tinker with”–but that still fits the rubric. Can we afford to pay a bit of maintenance overhead to keep gearhead players engaged at such-and-so a time, or would it be more effective to move the game quickly to a complex *and* meaningful stage?

    Generations of mechanics with a particular narrow simulation mindset have entrenched that you don’t roll if it’s *easy*, not that you don’t roll if it’s *boring*. But the principle of only rolling dice when all possible outcomes are exciting is slowly dawning on mainstream RPG designers. Mutant: Year Zero, for instance, a recent game with a pretty classic attribute+skill dice pool mechanic, includes the rule “If you roll no [successes], something goes wrong. You’re now at the GM’s mercy. The only thing she can’t say is ‘nothing happens’.” Buuuut… in an adventure published for the same game, there’s an area where there’s no chance of the PCs being detected by enemies while they explore, but the text advises the GM to “let them roll for Sneak if you want” as a means of keeping the players guessing about whether there’s danger present. Two steps forward, one back?

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s