Librarian of Alexandria

I'm a strong proponent of designing tabletop games with mechanical rules for social interaction. This is something which is relatively loosely-defined in many existing tabletop games, so I think it's worth digging into what this can mean and why I think it's worthwhile, and in the process address a few of the common issues that people run into with social mechanics. In particular, I want to make it clear that social mechanics can have advantages, but not all social mechanics are good. This blog post is going to describe three properties that good social mechanics can bring to tabletop games, and contrast them to social mechanics that don't necessarily pull their weight.

What do we mean by “social mechanics”?

I think a reasonable definition of 'social mechanics' is, “Any kind of well-defined mechanical rule that dictates some aspect of social interaction.” To give an example of what a social rule looks like, I'm going to turn to Apocalypse World: Burned Over, which features this move:

When you charm or deceive someone, roll+Cool. On a 10+, they have to choose: set skepticism aside and go along with you, or else call you a fool or a liar to your face. On a 7–9, if they don't want to go along with you or call you out, they can choose to ask you for evidence, time, a compromise, or some concrete assurance; they must go along with you if you provide it. On a miss, be prepared for the worst.

Or an even more clear-cut example from Blades in the Dark, where one ability given to the Slide playbook is:

Like Looking into a Mirror: You can always tell when someone is lying to you.

Both of the above are mechanical rules that dictate aspects of social interaction. Beyond that, they're well-defined. If I'm playing Apocalypse World and I try to charm or deceive someone, then I may not know which outcome I'm going to get—I'm rolling dice, after all—but I know for sure what outcomes can result from that move. If I'm playing Blades and I have the ability described above, then I know for certain that no character can lie to me. (Or, at the very least, if someone does and I'm not aware of it, then we're not playing the game according to the rules!)

I also talk about “some aspect of social interaction” rather loosely because I want to make it clear that social mechanics can encompass a wide variety of goals and levels of abstraction. Both of the above moves deal with specific outcomes from either a single exchange or conversation, but social mechanics don't need to be related just to talking: a good example here is the Blades in the Dark system of Status, which uses numerical values to represent how friendly or unfriendly various other factions are towards your own faction. Yet again, these mechanics are social in that they provide mechanical basis for interactions with NPCs, and they are well-defined in the sense that, if I perform actions which lower my status with another faction to -3, that faction will go to war with my own faction, and that will have specific consequences for what we're allowed to do.

Finally, to motivate my requirement that social mechanics be well-defined, here's what the D&D 5E Dungeon Master's Guide says about “Social Interaction”:

During a social interaction, the adventurers usually have a goal. They want to extract information, secure aid, win someone's trust, escape punishment, avoid combat, negotiate a treaty, or achieve whatever other objective led to the interaction in the first place. The creatures they interact with also have agendas.

Some DMs prefer to run a social interaction as a free-form roleplaying exercise, where dice rarely come into play. Other DMs prefer to resolve the outcome of an interaction by having characters make Charisma checks. Either approach works, and most games fall somewhere in between, balancing player skill (roleplaying and persuading) with character skill (reflected by ability checks).

The phrasing here is why I specify that social mechanics should be well-defined, because D&D 5E's rules for social interaction are very much not well-defined. There are rules I can use which affect social interaction—for example, using skills like Deception or Intimidation—but not only is there no fixed outcome for what a “successful Deception roll” looks like, there's no guarantee that the GM would even want you to roll it in the first place, since a given GM might decide that either in general or in this specific circumstance they want you to roleplay the intimidation instead.

A very significant consequence here is that a player can't rely on social mechanics being present or effective, at least not without knowing a specific GM's preferences around how the game runs. If I start playing a fresh D&D character with a new GM, would it be worth putting some points into Charisma? Well, if the GM will call for or allow Charisma checks, then maybe, but if the GM prefers roleplaying these conversations out without letting dice influence the outcomes, then my Charisma is significantly less useful. The lack of definition in the game rules means that trying to engage with those mechanics is risky for players—if there's a chance that my hard-earned XP is going to go a mechanic that might not do a thing, why even spend it?

What do you get out of social mechanics?

One of the most common objections to social mechanics—especially in the context of D&D and closely related games—is that they're simply redundant when compared with role-playing. Imagine, for example, that a rampaging dragon is moving towards a town, and the players demand an audience with the local ruler to request the evacuation of the town. Why roll, when you can ask the players to make their demands as their characters would? If the players are convincing, the guard is convinced: why do you need anything more?

There's a common way that arguments about this usually happen: the person who likes social mechanics will say, “We don't usually ask players to pick up a sword to see if they succeed in combat, so why do we ask players to actually debate to see if they succeed in a debate? How do we account for players whose characters are mechanically charismatic but don't feel they can rely on that same level of charisma in real life?” while the person who dislikes social mechanics will respond, “We're already speaking in real life, so why abstract it out with mechanics? If you're not comfortable fully roleplaying the conversation, then you can describe what you'd want to say instead of saying it word-for-word. And anyway, if the players are convincing during role-play, it's not fair to them to deny them that success just because the dice rolled poorly.”

I think there are some very good reasons to include social mechanics that aren't addressed by the arguments above. One of the first ones, for me, is social mechanics can produce interesting unplanned outcomes, which is something I personally find deeply valuable in tabletop games.

Consider applying the rule from Apocalypse World: Burned Over that I described above to the scenario with the guard blocking access to the town's ruler. The players can roll and succeed, in which case the guard can either agree to let them see the ruler, or explicitly and loudly call them out as liars. Or, the players can roll and partially succeed, in which case the guard might ask for evidence—some proof that a beast is indeed approaching—or a compromise—such as allowing them access to a lower-level functionary rather than the ruler, or demanding some manner of bribe.

When I described the scenario to begin with, I may have had two broad potential outcomes in mind: either the guard agrees and allows the party to see the ruler, or the guard rejects the party is kept outside. I may not have had other outcomes in mind, but having mechanics around social interactions can produce those outcomes. These outcomes can move games into very different directions, which can be a lot of fun and produce amazing stories afterwards.

Another reason I find social mechanics valuable is that social mechanics provide player affordances. Here I'm using the word “affordance” specifically in the way it's deployed by designer Donald Norman, who uses it to refer to the possibilities of action that are suggested by an object: objects with handles might suggest being held, objects with feet might suggest being placed, objects with upward-facing padded surfaces might suggest being sat on. Interface designers and human-computer interaction researchers are interested in affordances because they can help make objects usable without specific training: an object with the right affordances (and the right cultural context for those affordances) can by its very design suggest how to use it.

It's worthwhile to think of tabletop games as providing affordances, which are the actions it suggests are available to players. In theory, a player could come up with any course of action—indeed, this freedom is something that makes tabletop games special relative to, say, board games or video games—but in practice there are specific actions which the game suggests strongly by virtue of how it provides mechanical support for them.

For example: I can flip through the D&D rulebook and see extensive rules about how to engage in various kinds of combat, and indeed, most of a player's abilities are combat-related. On the other hand, there's no reason I couldn't have a D&D character who is strongly invested, say, in state-building and creating governments, but there's nothing in D&D's rules that imply that this is a course of action that you're likely to take. Looking at my player sheet, it's clear the game has affordances, actions which it suggests—there's even a list of skills right there!—and by and large players will embrace the affordances of the game most of the time.

I think that many tabletop players share the common experience of coming across a problem and starting to look through their character sheet to see if they've got a specific way of addressing that problem: a relevant piece of equipiment, a proficiency or ability that addresses it, anything which can give a bit of an edge. It's not uncommon to jog your mind in doing so. I might not have considered trying to blast my way through the door instead of picking the lock, but I perused my character sheet and found that I still had dynamite: why not?

So if you check your character sheet and you see skills related to interacting with people—bonuses to specific interactions, conversational stratagems, specific NPC contacts—then social actions become part of the set of affordances provided by the game. A good example I've experienced many times is having a list of contacts in Blades in the Dark: a player is confronted with a problem, checks their sheet, and notices an NPC whose role might give them special insight into the problem at hand. This little affordance helps push that player into a social situation when they might otherwise have picked up a weapon.

It's also worth noting that, in many games, becoming better at combat is a big enticement to, well, doing more combat. In games where advancement can provide bonuses or the ability to exercise social mechanics, this becomes an enticement to engage in social activites more. Yet again, Blades in the Dark provides some good examples, as many playbooks have specific social moves which they can take as upgrades, and that in turn makes certain kinds of social interactions more appealing as players have advantages in those interactions.

Yet another reason is that some social mechanics can situate players into the world more explicitly. Not every kind of social mechanic does this, but an appropriately-designed one can.

One way this might happen is by providing social mechanics around information-gathering. Consider another move from Apocalypse World: Burned Over:

When you read someone in a charged interaction, roll+Sharp. On a 10+, hold 3 against them. On a 7–9, hold 2 against them. During your interaction, spend your hold 1 for 1 to ask the MC or their player questions. They have to answer frankly, from their character's point of view. – Are you telling the truth? – What are you feeling? – What are you thinking of doing? – What do you hope I'll do? – How could I get you to do [x]? On a miss, the MC might have you ask 1 anyway, but be prepared for the worst.

This move is designed to give players access to social information and cues which might be hard to convey through just dialogue. After all, even with the most descriptive prose possible, we're still dealing with an entire world conveyed through a stream of dialogue from one person, and that's significantly less information than we'd get if we could somehow immerse ourselves in a full simulation of that world: a mechanic like this helps players get reliable access to specific social information that might otherwise get lost.

For example, if we apply the above move to the scenario with the guard, we might imagine a player trying to read this person to figure out: what's important to them? Can we deduce anything about the guard's emotional state? What other cues can we read from the guard?

Another way that social mechanics can situate players in the world is by subjecting their players to mechanized versions of social pressures. Consider, for example, augmenting D&D 5E with a factional reputation system: perhaps players who help out specific factions might get extra points which can translate to bonuses on social rolls, but interfering with factions or causing trouble for their members can translate to negative modifiers on those rolls.

How might this affect our guard? Well, if the players are well-respected in that community, the faction modifier means the guard is mechanically more inclined to trust the party: they've helped out the town, after all. On the contrary, if the players had previously caused trouble in the town, now the guard is mechanically less-inclined to help them.

(It's worth noting that this is interesting even if the modifiers don't cause the rolls to swing in the expected direction: if the players have a high reputation but still fail the roll, then it's interesting story-wise that the guard won't let the party see the ruler despite their high status: why might that be? Or if the players have a low reputation but still succeed: why did the guard agree to let this known band of brigands in to the ruler in this case?)

There are other kinds of social mechanics which help here: for example, mechanized relationships or contacts with NPCs help cement PCs as inhabitants in a world, or mechanization of various kinds of group status can help situate players in a world by opening or closing avenues of action to them.

What makes a good social mechanic?

I think one of the biggest objections to social mechanics in practice isn't actually a mark against the idea of social mechanics in general: rather, it's because many people have had exposure to bad or pooly-thought-out social mechanics.

Consider, for example, the not uncommon story of a player who has managed to maximize their character's abilities around social interaction, resulting in a character who can steamroll any conversation and make any NPC agree with them. At some point, this level of forced interaction with NPCs feels less like a reflection of a character with high charisma and more like some kind of mind control or other coercion, and it can remove stakes or drama from the story being told.

This isn't because the idea of a social mechanic is flawed: it's because this social mechanic has a flawed implementation! There's an implicit understanding of how a social mechanic in something like D&D might work. This isn't present in any source book (and consequently the problems with this rule shouldn't be considered a knock against D&D here) but people might imagine D&D's social rolls as working like this:

When you use deception, trickery, or lies to get an NPC to believe or do something specific, make a Charisma (Deception) check against a DM-specified target number. If this succeeds, the NPC will believe you or do what you want. If it fails, then the NPC will not believe you and will not do what you want.

This is actually in some ways more well-defined than the actual D&D 5E rule for deception, which does not specify any specific outcomes for success or failure, only the situation in which it can be used. Unfortunately, though it's a very flawed implementation of that mechanic: once a character has high enough Charisma and if the GM has this understanding of social mechanics, it's difficult not to get NPCs to do what you want all the time.

Contrast the above example with the Apocalypse World: Burned Over example from above. Here's the success case from that roll:

On a 10+, they have to choose: set skepticism aside and go along with you, or else call you a fool or a liar to your face.

There are two things I'd call out here. One of them is that a success on this roll doesn't mean the NPC believes you: they might still reject what you're telling them, but they'll reject it loudly and bring that conversation to a head. That means even with tons of modifiers and a high number in the relevant stat, there's no guarantee that a player using this move will be able to bring every NPC they come across to their side, but it does allow them to move the interaction along by at the very least getting the disagreement to come out in the open.

There's another important detail I'd call out about this rule, as well: even if the NPC doesn't call you out directly, they still don't necessarily believe you, since the rule specifies that they will “…set skepticism aside and go along with you.” This means that, even if the NPC is doing what you want, they might only be doing so provisionally: you haven't convinced them that you're right, you've just given them enough reason that they're just willing to go along with you.

These differences aren't trivial: they're very important both to balancing the mechanic in question and to making sure it doesn't take over the narrative. Making sure that outcomes are well-defined but not unilateral is a key part of making social mechanics which people can rely on but which don't end up amounting to players dominating all social exchanges.

This isn't unique to social mechanics, after all: all parts of a game need playtesting and balancing and so forth. A combat mechanic which allows a player to one-shot every enemy would likely be considered incredibly overpowered and in need of balancing: a social mechanic which allows a player to get what they want out of every conversation should be treated the same way. (If you want to get a good look at what this process might look like, I'd recommend this blog post by Jeremy Strandberg which talks extensively about the revisions to Parley, the primary social move in Dungeon World, and how he revised it for his derived games Homebrew World and Stonetop. It's a long process, and very subtle parts of the wording can have a big effect on how that social move plays!)

Finally, I think it's worth noting that mechanizing social mechanics doesn't mean that all social interaction should turn into a mechanical back-and-forth. I think there's a fear that social mechanics, taken too far, could result in all conversations becoming a sequence of players invoking the mechanics—”I use witty jab! I use clever repartée! I use sardonic gesture!“—instead of actually getting into character and having dialogue. I don't think this is necessarily an unfounded fear, but I don't think it's the end-goal of adding social mechanics to a game. In fact, it runs contrary to one of the advantages of well-designed social mechanics that I describe above: this doesn't help situate players in the social realities of the world, it divorces them from it!

I think the advantages I've described to social mechanics can be used in an inverse way, to evaluate whether a social mechanic is pulling its weight. You can look at a mechanic and ask, “Is this helping me get to outcomes that we might not have come to?” If not, maybe it's not adding much value that wouldn't get from just role-playing those conversations. “Is it giving players social affordances that let them tackle problems via social means?” If not, maybe it's not pulling its weight in complexity, or maybe it's either too broad or too narrow and needs adjusting. “Is it helping situate player characters in the world socially in a way that players understand and react to?” If not, maybe think about how the mechanic might be relaxed, reworked, or refocused to help underscore and mechanically reinforce the social structures of the game world.

And of course, I think it's worth noting that none of these should replace role-playing. Let's go back once more to the example with the guard: if I were running this in a game with social mechanics, I wouldn't simply ask the players to roll: I'd first roleplay out the scene, and ask the players to roleplay out their attempt to convince the guard, and then bring the mechanics down in order to figure out how the scene ends. The social mechanics should inform the narrative, not replace it.

So hopefully this provides a case for why I believe social mechanics are worthwhile to implement, and give some advice in terms of how to implement them in a way that plays towards their strengths!

#tabletop

This is an entry from a list of projects I hope to some day finish. Return to the backburnered projects index.

Edit (2024-09-03): One of the projects described below has something resembling a proper release! The Matzo language has a reasonably usable version available on Github or to try in the browser. For more details, see this blog post about the language.

What is it? Various programming-language-centric approaches to procedural generation. I've done a lot of these, and Potrero and Treant are two of the newest ones, but I'm using this to talk about the whole family of these tools.

My approach here is to build tools for procedural generation as programming languages, and usually specifically as functional or logical languages: the UI is fundamentally text-centric, based on writing bits of structured text, and the operations defined can be broken down into atomic units that can be understood and reasoned about in isolation without having to reason about broader state. Additionally, because they are ideally decidable—i.e. you can ensure that they don't loop forever, but instead will always complete in a finite amount of time—you get a lot of ability to statically analyze them.

I think there's a really interesting design space here, and different experiments have focused on exposing them in different ways. There are some thoughts in common between all of them:

  • I think (as mentioned) that decidability is important here, both because “this will never loop forever” is a nice property but also because it opens the way for some sophisticated static analysis, like, “Will this program ever produce output with such-and-such a property?”
  • I think mostly-pure functional programming is a useful way to think about these things, where the only side effect is non-determinism
  • I think there's some interesting inspiration to be taken from logic programming or answer-set programming (c.f. Smith & Mateas, Answer Set Programming for Procedural Content Generation: A Design Space Approach and also Karth & Smith, WaveFunctionCollapse is Constraint Solving in the Wild)
  • I think there's a lot of API work to be done around exposing common resources (like Darius Kazemi's corpora) as “standard libraries” to draw on
  • I think there's also a lot of API work to be done around the right primitives for structuring output either textually or non-textually (although I'll freely admit that there's research here that I'm not conversant with!)

So the goal of my various tools has been to chip away at this space and experiment with these various theses!

Why write it? The big tool in this space right now is Tracery. Tracery is a great tool and I love it. That said, I'm very picky about tools.

To give an example of what I mean, here's an example of a Tracery program which creates a brief description of meeting a person:

{
  "origin": ["#[#setNoun#]story#"],
  "setNoun": [
    "[person:man][them:him]",
    "[person:woman][them:her]",
    "[person:person][them:them]"],
  "adj": ["familiar", "strange", "mysterious"],
  "adv": ["warmly", "cautiously", "profusely"],
  "story": ["You meet a #adj# #person#. You greet #them# #adv#."]
}

You'll notice that the syntax is all JSON, with all the advantages and disadvantages that brings: it's a universally available format, which is good, but it's also more finicky and particular, and also now important syntax is embedded in the string literals in a way that's not (for example) syntax-highlighted by default. Note also the "setNoun" rule above: what this does is actually create new rules effectively by injecting them into the set of rules, so depending on which choice is made, it'll define the rule "noun" as being either "man", "woman", or "person", and define "them" as being a corresponding pronoun.

Here's the same generator expressed in Matzo, a dynamically typed programming language for procedural generation that was also the very first tool I built along these lines:

person := "man" | "woman" | "person";
pronoun :=
  { "man"    => "him"
  ; "woman"  => "her"
  ; "person" => "them"
  };
adj ::= familiar unfamiliar mysterious;
adv ::= warmly cautiously profusely;

n := person; fix n;
puts "You come across a " adj " " n ".";
puts "You greet " pronoun.n " " adv ".";

This has a lot of little convenience features, like the ::= taking a set of bare words that are implicitly understood to be a disjunction. (Indeed, you could rewrite the first line of the program to person ::= man woman person; and it would have the same meaning: I wrote it with := to showcase both approaches.) It also has functions—the value of pronoun is a function which branches on its argument—and the fix operation, which takes a rule which otherwise would be vary, evaluates it once, and modifies its value to be the result of that: i.e. after that line, person will still randomly be one of three values, but n will always deterministically be one of those three values.

After Matzo, I worked on a tool called Latka which was similar but with a light static type system. You'll notice this one relies more heavily on things like (structurally typed) algebraic data types and helper functions to assemble sentences, like pa for a paragraph or se for a sentence. You might also notice that fixed is no longer a thing which changes an existing rule, but a property of the binding site of the variable.

let gender = M | F | N
let noun M = "man"
  | noun F = "woman"
  | noun N = "person"
let them M = "him"
  | noun F = "her"
  | noun N = "them"
let adj = %{familiar unfamiliar mysterious}
let adv = %{warmly cautiously profusely}

let main =
  let fixed n = gender
  pa.[
    se.["You come across a", adj, noun.n],
    se.["You greet", them.n, adv],
  ]

These obviously have tradeoffs relative to Tracery, but they more closely match the way I think about these problems and the way I'd naturally gravitate towards solving them. That said, I also don't think they're competing: in fact, one thing I've wanted to do (but never finished a prototype of) is building cross-calling between them: I'd love to be able to directly include a Tracery program in a Latka program and vice versa.

As with other projects I've described, I think that the affordances of functional or expression-oriented programming have a lot of benefits, and I think decidable programming languages are really useful. And of course, one reason I want to write it is that I love random generation of things.

There's a reason this project is also the last backburner post: it's the project I most regret having left on the backburner, and the one I would love to spend more time on soon once I develop motivation to do so again.1

Why the name? Latka and Matzo were named together but otherwise chosen at random. I was originally gonna call another experimental language Mesa, which is Spanish for 'table' (by analogy with random tables you'd find in an RPG book) but there's already a graphics driver suite called mesa. So I decided to choose the name of a related geographical phenomenon, since a potrero is a kind of mesa. Similarly, the original version of Treant was designed around probabalistic tree rewriting, and treant is a name used in fantasy role-playing games to refer to a public-domain equivalent to Tolkien's Ents.

#backburner #tool #language #procedural


  1. Hell, I've even given a talk about these before—although a bad one which I'm thankful wasn't recorded, since I was feverish and recovering from a throad infection at the time—and I never did release Latka in a usable format afterwards, which is an ambient regret of mine. I would love to go back and release one of these properly instead!

This is an entry from a list of projects I hope to some day finish. Return to the backburnered projects index.

What is it? A video game inspired by classic hard-boiled detective stories.

My idea here is that this is pre-written, a hand-crafted world rather than a procedurally-generated one. My mental image of the setting is a sleeker-than-ever-really-existed fictionalized 1920's, borrowing heavily on the clichés of noir media: night more often than not, smoky with black shadows, Streamline Moderne in blue metals, but dingy as soon as you look past the surface. You'd interact with it as a old-style isometric RPG, talking to people and shuffling through drawers and trash-cans and so forth.

The interesting thing, I think, was how I planned to implement the mystery mechanic. When you're first given a case, you are also given a shape, a deeply irregular one with several crannies and protruding bits, areas of which are labeled: Culprit, Weapon, Motive, and so forth. As you acquire information by talking to witnesses, going to places, and so forth, those pieces of information also get represented as shapes, which can in turn be snapped onto the shape representing the case. The catch, of course, is that only one configuration will fit: the correct one. Sometimes also multiple shapes will snap onto the case, but will conflict with each other: a suspect fits, and a murder weapon fits, but they don't fit _together.

This does mean that it's possible to solve the mystery without thinking about its narrative content: by unthinkingly talking to everyone, casing every place, and then ignoring what the clues mean and simply snapping them together. I'm okay with that! The ideal way of playing is to approach it from both directions: using the narrative to inform how you approach the puzzle-pieces, and using the puzzle-pieces to help you approach the narrative. Ideally, too, the pieces would be designed to fit together in ways that match the story: for example, a given suspect and a given weapon both snap together with the shape of the case, then that suspect could have conceivably been the culprit and could conceivably have used that weapon, but if there's no matching motive, then you've got more work to do: either digging into a motive that fits, or figuring out if another suspect was the one that did it.

Why write it? Partly because I love the genre, and partly because I think the hard-boiled detective genre—despite being a perennial favorite for certain kinds of video games—have a ton of interest space for exploration.

I've had notes for this for a long time, and I recall back when I first saw trailers for L.A. Noire that I figured that maybe I had finally been scooped. I don't think it really succeeded at what I wanted to capture! Since the game's release, people have regularly made fun of the awkward interviews where the player must choose to believe, doubt, or disbelieve a suspect, but where choosing to doubt every question was always a safe strategy and lies were awkwardly telegraphed using exaggerated facial motion capture. (It was especially awkward since it wasn't clear which aspect of the story you were choosing to doubt: the player would notice a tiny discrepancy in a witness's story, choose to doubt the testimony, and the player's character would pound his fist on the table and shout, “You wanted him dead, just admit it!”) More than that, it was hampered by being a AAA action game: the grand climax of the whole affair was… an awkward, plodding shootout.

It's pretty common for games to borrow the trappings of noir but end up with yet more violence. (Not that I'm strictly opposed to video games that feature violence, but it's not a good fit for classic noir!) That said, there are games utilize the style and implement the mystery-solving a lot better. Grim Fandango is a spectacular example of the genre, leaning heavily on film noir iconography and using the affordances of a point-and-click adventure game to implement the mystery-solving. More recently, Disco Elysium does a spectacular job of building a deep and compelling mystery by building in the tradition of classic tabletop-inspired role-playing-games.

So I don't want to imply that this is unique or has never been attempted successfully: but that said, I still think there's space to play in this genre in a new and interesting way!

Why the name? I have notes covering the whole trilogy, each with a different protagonist, but each protagonist was going to be—predictably, given the genre—something of an outcast. The idea of the protagonists as “stray dogs” felt appropriate: more than a little cliché, sure, but given that I wanted to draw on the bombastic and cliché-filled movies of the Golden Age of Hollywood, I didn't necessarily think that was bad.

#backburner #videogame

This is an entry from a list of projects I hope to some day finish. Return to the backburnered projects index.

What is it? A tiny ML variant, designed for embedding.

It should of course have closures and anonymous functions and so forth. It should have algebraic data types—named sums, tuples, records for product types—and it should also be garbage-collected. It should have powerful pattern-matching constructs. It shouldn't be pure, in the same way that something like SML isn't pure: mutation would still need to use explicit ref types, but side-effectful functions should be allowed everywhere. It should have a basic exception system. It probably doesn't need a full module system, but some kind of namespacing would be nice.

I'm pretty neutral with respect to syntax, but I was imagining maybe borrowing some ideas from ReasonML. Over time I've become a fan of explicit delimiters for scope, but I also fully admit that syntax is probably the least interesting thing here.

Most importantly, it should be easily embeddable in any language, exposing a C API that makes it easy to initialize the runtime, call into it, and expose callbacks to it. PicoML wouldn't be for writing programs on its own: it would be for writing scripts in larger programs, which means making it accessible to larger programs should be as simple as possible.

Why write it? There are a number of language which are explicitly designed for embedding in a larger program: Lua is one of the most prominent, but there are certainly many others, my personal favorites being Wren and Io.

These embedded languages rarely have types, and the handful that do (like Pike or Mun) often don't embrace functional programming in the way that I wanted. Entertainingly, though, the closest language to what I wanted PicoML to be is Gluon, and my biggest complaint there is that it's too Haskell-ey: it's got monads and GADTs, which feels to me like it goes too far in the other direction. There's a middle ground—an impure functional language with strong SML influence—and that's where I want PicoML to sit.

Why the name? Not that surprising: I wanted to get across the idea that this would be “an extra-tiny ML variant”.

#backburner #software #language

This is an entry from a list of projects I hope to some day finish. Return to the backburnered projects index.

What is it? A tabletop game about fast cars, furious action, and family values.

This is of course inspired by that one series of films. Indeed, the original design goal was to build a game where every player had three core stats: Fast, Furious, and Family.

It's expanded and gone through a number of revisions, including a few that I still think are fascinating, but it's ended up being more or less an adaptation of Danger Patrol. Danger Patrol features an idea called the Action Area, which I like a lot: it's an explicit index-card based representation of the goals and major features of a session. For a lot of games, I think this might be overly restrictive and maybe even a little bland, but for broad-strokes genre storytelling—including both the 50's pulp adventure that Danger Patrol is drawing on and the modern action movies that Guns & Gasoline is drawing on—it feels kind of perfect.

I also put a lot of thought into the right way of representing vehicle chases and races, which are integral to the genre. Making sure that races aren't simply “who can roll the highest number first” but actually feel like moment-to-moment interesting things are happening is integral to the game I want to make. The system I'd been working with most recently involved a three-way distinction where you're always Leading, Tied, or Trailing, and those positions open up different options for actions you can take, including attempting to change your position to one or the other: sometimes that means accelerating to try to take the lead, but sometimes it also makes sense to deliberately let yourself trail behind because that's going to let you address the race in different ways.

Overall, it's not a game designed for long campaigns: it's for one-shots or maybe two or three session campaigns. It should be quick and exciting and ideally have lots of corny one-liners and over-the-top action beats. One guiding principle behind the game is that it should be true to the way a friend of mine once described the Fast & Furious movies: “Imagine a superhero movie, except everyone's superpower is 'car'.”

Why write it? Okay, this is a silly one, and one where I've actually struggled throughout several drafts to actually hit the tone and level of complexity I want: I keep coming up with interesting ideas and then realizing that they're far too elaborate for the goal here.

For example, one draft was a by-the-numbers Powered-by-the-Apocalypse game. I had a set of fighting moves that I liked a lot, including a distinction between Fight Hard (which you rolled with Furious) and Fight Smart (which you rolled with Fast) that not only had their own distinct effects but would interact with each other in a satisfying way. That was a fun idea, but the draft was too complicated.

Later on, I worked on a variation where, instead of using Powered-by-the-Apocalypse-style moves with three possible outcomes, I instead turned every move into a lits of “good outcomes” and “bad outcomes”, and dice rolls would give you points to either prevent bad outcomes or buy good outcomes: in effect, turning every move into a PbtA-style “choose things from a list” move. Because in this system dice would also explode—that is to say, if you rolled their maximum value, you'd keep that but also re-roll the die, possibly multiple times if you got the maximum value again—you could theoretically but rarely get a large number of points to buy outcomes, which could translate into e.g. a single roll letting you take down large numbers of opponents, acquire intelligence, and find useful objects, all at once. This was a fun system that was also far too finicky for the simple game I was trying to jam it into.

There's still a lot I like about the current draft, but I still struggle to make it a game suitable for one-shots. This is one project I deliberately left to the side for a while, because I wanted to come back to it with fresh eyes and figure out what I could cut down on.

Why the name? The working title was The Rapid & The Rageful, which was funny for about fifteen minutes before I found it tedious. Given that there's nice alliteration in both the movie that this game is inspired by and also in the name of the most famous tabletop role-playing game, it felt appropriate to choose an alliterative, so: Guns & Gasoline.

#backburner #tabletop

This is an entry from a list of projects I hope to some day finish. Return to the backburnered projects index.

What is it? A “web framework”, but over SSH instead of HTTP.

What are web frameworks about, on a basic level? You're taking a protocol meant originally for serving simple documents in a hierarchical file system, and instead of serving those documents you're doing various computations and responding with dynamic data. Your web framework needn't interpret a GET /foo/bar as actually looking up a file bar in a directory foo: you could have code attached that does whatever you want!

Cube Cotillion follows the same basic idea except with a different protocol: SSH. Usually, when you establish an SSH connection, you get dropped to a shell and the stuff you type gets interpreted as command-like invocations. Cube Cotillion implements an SSH server, but instead of passing strings to a shell, it instead lets you write arbitrary computations keyed on the commands you've written. If someone writes ssh my-cc-server foo bar, then instead of trying to run the command foo with the argument bar, you can do, well, whatever you want!

Cube Cotillion is implemented as a Haskell library that does pattern-matching over the command passed in over SSH and can invoke callbacks as necessary. Here's a simple example: this server understands the command greet, with or without a parameter, and will respond with a greeting in kind, using the bs command to respond with a ByteString:

main :: IO ()
main = do
  key <- loadKey "server-keys"
  cubeCotillion 8080 key $ do
    cmd "greet" $ do
      bs "Hello, world!\n"
    cmd "greet :name" $ do
      name <- param "name"
      bs (mconcat ["Hello, ", name, "!\n"])

Like a web framework, we can now do whatever we want in response to these commands: there's no underlying shell we're passing commands to, and greet isn't dispatching to another executable, it's just a single process figuring out what to do with the strings and responding in kind. We could build applications on top of this that use structured data (e.g. responding with JSON blobs instead of simple lines) or keep various pieces of internal state. Why not?

Why write it? This is far more on the experimental side: I have no idea if this would be useful for anything at all!

The thing that I like about using SSH as a transport mechanism is that it necessarily has a notion of secure client identity. For certain applications, having a built-in mechanism for verifying client identity would be wonderful, since that's something that need to be built on top of HTTP applications again and again.

That said, I have no idea whether applications built on top of this system would be useful in practice or not. That was sort of the point: I wanted to experiment with using it as a tool for building personal server control, but would it be useful for anything else? Who knows!

I also never got around to designing the right API for doing anything with the client identity, or the ability to do interactive sessions at all. There's a whole design space there that might be fun to explore.

Why the name? I wrote this in Haskell, and there are multiple web frameworks in Haskell named after Star Trek characters, including Spock and Scotty. Given that this was an analogous but wrong project, I decided to choose a name from [Neil Cicierega's guide to the alien races in the Star Trek universe.

#backburner #software #experimental

This is an entry from a list of projects I hope to some day finish. Return to the backburnered projects index.

What is it? A video game about procedurally-generated under-equipped spy missions.

The player is a nameless spy with almost nothing on them: maybe a tiny amount of money, an object or two. They have an objective: a document to steal or replace, a photograph to take, a target to kill. They are in a randomly-generated city with a roughly 1980's level of technology: computers exist but are rare, mobile phones aren't practically obtainable, and only some sections of the city are accessible but within those sections you can (with some difficulty) get almost anywhere: apartments, offices, basements, and so forth. Go.

The intention is that there should be multiple ways of tackling the objective, but you need to be resourceful. You need to find your own disguises, hotwire cars or take trains, pickpocket money from people, do research in the handful of places where you can. You have no spy agency to rely on to bail you out or drop you resources: if you get caught, it's over, and all-out firefights are a failure condition. You're on your own, with only the infrastructure of the city to help you.

My mental image here was that this game would have the graphical sophistication of a roguelike: that is to say, very very simple grid-based graphics, no strong detail. The detail should instead be in the density of the simulated urban environment, ensuring that buildings do indeed have dozens of accessible and usable rooms (probably generated on-demand: after all, with that much detail, 95% of the map would never even get accessed!)

Why write it? I can pinpoint the exact blog post that inspired this game, which is this post about the “Bourne Infrastructure”. I read this around the same time that my brother and I had been discussing a very different kind of spy game—the James Bond-inspired kind of spy game, with massive explosions and firefights and stunts and whatnot—and it got me thinking, “What would a Jason Bourne game—one that really captured the feeling of the movies, not just a reskinned stealth game—look like?”

There's a lot of complexity here, and a big part would be managing to implement new and different ways of tackling the objectives. There's also an economy of detail that I don't off-hand know the right way to manage. I want players to be able to lockpick an apartment, sneak in, rummage through a drawer, steal a suit, and go off to blend in at an office, but that could easily grow and grow until there are unwieldy inventories and far too much stuff simulated. Finding out the right amount of fidelity here is key, and I don't yet have an instinct around how to do that.

Something I'm super interested in, though, is using stuff like shape grammars to create the world. It'd be a lot of fun to build the basic mechanics and then just keep revisiting new stuff to add to the world, making the city deeper and denser!

Why the name? It's a temporary name—I don't want to actually name it after a real place!—but it's named not for Schengen, Luxembourg itself, but rather for the Schengen Area since the setting is a roughly-defined European setting.

#backburner #videogame #procedural

This is an entry from a list of projects I hope to some day finish. Return to the backburnered projects index.

What is it? A reverse proxy and web server interface.

Aloysius was originally inspired by the old academic servers where a person could host static pages under their username. What I wondered was, how could I design a system like that for dynamic sites, where a given user account could run their own services via a reverse proxy but not conflict with any other service on the machine?

So the approach was based on directories and symlinks. The top-level configuration is a directory that contains zero or more subdirectories, each of which uses different files to describe both filters and forwarding logic. Filters are based on matching on either path (i.e. the HTTP requst path) or domain (i.e. the server) or both, and then they can forward in one of three ways—maybe more over time, too, but these were the first three I experimented with—as specified by a mode file: either http (which forwards the request to host host on port port), or redir (which uses HTTP response code resp and redirects to host host), or aloysius, where you specify another directory by the symlink conf, and then recursively check configuration there.

That last one is important, because it allows you to symlink to any readable directory. One idea here is that different subdomains can map to different users on the same machine. For example, let's say you're keeping your Aloysius configuration in $ALOYSIUSDIR, and you've got a user on the machine yan who doesn't have root access but wants to be able to run some dynamic sites. You can set up something like the following:

# create a dir to forward yan's config
$ mkdir -p $ALOYSIUSDIR/00-yan
# match against yan.example.com
$ echo yan.example.com >$ALOYSIUSDIR/00-yan/domain
# use a recursive aloysius config
$ echo aloysius >$ALOYSIUSDIR/00-yan/mode
# and forward it to yan's home directory
$ ln -s /home/yan/aloysius $ALOYSIUSDIR/00-yan/conf

Now yan can write their own configuration for whatever dynamic web services they want—at least, if supported by the ability for them to run user-level services—and it doesn't require giving them root access to change the other configuration for the reverse proxy. It falls directly out of Unix symlinks and permissions!

Why write it? Honestly, it's not terribly useful anymore: I run my own servers using mostly nginx and am pretty comfortable with configuration thereof. But I still think it's a cool idea, and I clearly think this directory-and-symlink-based system of configuration has legs (which I know I've written about before).

I still think it'd be convenient! After all, I have for a long time used a directory of files (along with a wildcard) to configure Nginx: this simply makes that the first-class way of doing configuration! But also, there's a lot of work that goes into writing a good web server, and this is also solving a problem which the tech world seems to no longer have: namely, how to shunt around requests to different web services all living on the same machine, since instead we abstract those away into their own containers and treat them like separate machines.

Why the name? The name Aloysius was chosen pretty much at random. I originally called it Melvil after Melvil Dewey due to a loose metaphor between the tiny scraps of information used in configuration and the card catalogues that Melvil Dewey had helped popularize, but only because at the time I didn't realize what an absolutely awful human being Melvil Dewey was.

#backburner #software #tool #web

This is an entry from a list of projects I hope to some day finish. Return to the backburnered projects index.

What is it? A map-drawing game about a village.

That is to say: it's a game for several players where you begin with a near-blank map. Over time, you take turns, where each turn involves developing the story of the place, filling in details, deciding on events that move the place forward. The place in particular is a rural village being rebuilt by a small community, and the game is otherwise neutral with respect to setting: maybe it's a little ancient village in the desert, or maybe it's a village in the snows of another planet in the far future, or maybe something else entirely!

There are many of these kinds of games now—something which I'll discuss in a bit—but the specific thing I want to do with Yan Tan Tethera is build a tiny, manageable-but-still-important resource economy into the game. Actions in the game will have two aspects: they move the story forward narratively but they also spend and gain resources, and losing too many resources will damage the village. I'm still torn on whether it's possible to straight-up lose, but I'm not taking it off the table. If losing is possible, then it should be unusual and difficult, but one reason I'm still open to losing is because it gives some weight and some bite to the resource economy.

Each player's turn is composed of two halves: one that's a random event, one that's an explicit action by the player. Random events might be windfalls, they might be problems, or they might be simply new things that add detail and color to the world. Actions can involve constructing buildings, making tools or resources, undertaking projects or journeys… they're somewhat free-form in terms of their effect on the story, but their effects are specified and play into the resource economy, helping you gather resources. And they can fail! There's an element of randomness, and players can help reinforce projects if they're important, but the randomness means that things might simply not go the way you want. Your village is growing, but it should regularly feel like it's growing against the odds.

Why write it? You've might have noticed that this description is heavily, heavily indebted to another game: Avery Alder's The Quiet Year, a game that I cannot understate my love for. The Quiet Year is a vaguely post-apocalyptic game, in that it specifies that the focus is a place being rebuilt after some kind of terrible catastrophe, but it's not well-specified what the apocalypse was or, indeed, any of the setting details. In the game, you draw cards to find out an event, and then you have various actions you can take.

I love The Quiet Year, but I'll also admit that I've had some weirdly directionless games of it in the past. A big part of it is that the game asks a lot of players. If the players are in the right mindset and have the right experience with improvisation, it can be supremely rewarding, because a player really has carte blanche to do just about anything they want. For example, they can start projects, which can be anything, and they can take about any amount of time and have any effects! They can discover anything! It's supremely flexible, but I've played games with players who have really struggled when given a full blank page. I distinctly remember one game of The Quiet Year where a friend of mine drew a card that told them to make up a second project, and they immediately became upset, because they were struggling to imagine up just one project, and now they had to imagine two.

So what I want is a game which tries to facilitate some of the tone and style of The Quiet Year but builds in a bit more restriction, which on one hand will limit the possibilities of the game, but on the other hand will provide more guidance and affordances to players. The biggest thing is that, instead of projects being free-form, I want projects to address specific resource problems and use specific resources, so that a player should never arrive at their turn and be unsure of what to do: there are problems, and they should be solving them! In this, it ends up borrowing from some of the other map-drawing games I've played, like Martin Nerurkar and Konstantinos Dimopoulos' Ex Novo and Everest Pipkin's The Ground Itself but also from more resource-management-focused games like Cecil Howe's Do Not Let Us Die In The Cold Night Of This Dark Winder. (Perhaps surprisingly, another game that's on my mind as an inspiration here is Matt Leacock's collaborative board game Pandemic, which is another resource-management board game in which you constantly feel like you're on the razor's edge!)

I also should be clear that this project is not a criticism of The Quiet Year. I'm not trying to “fix” it in any way, and I still love it dearly and encourage people to play it! It's especially worth noting that the game I'm describing here, in trying to address one specific design point, loses out on so much of what makes The Quiet Year such an effective and poignant game: in particular, you'll notice that I've glossed over the mechanics around community dynamics which make up such a central part of The Quiet Year, and that's because I legitimately am not sure how to build the game I want to build while preserving those. I'm playing around in the design space, but making something which tackles the issues I want to tackle ends up being a very different kind of game.

Why the name? The original name was Those Who Return, and it was part of a conscious (if somewhat lazy) attempt to avoid imbuing the game with difficult-to-avoid imperialist themes by reinforcing the idea that the protagonists of the game are native to the region and consequently not engaging in imperialism. In retrospect, I believe this was very naïve: after all, there' no shortage of imperialist projects—including ongoing ones—that use the idea of an “ancestral homeland” as an excuse to violently evict current inhabitants. It's an effort, but it's by no means sufficient.

The newer name, Yan Tan Tethera, comes from a system of sheep-counting in the northern parts of England: those three words specifically are the numbers “one, two, three” in the variation used in Lincolnshire. Many areas in Northern England once used a separate set of number words which were used by shepherds for counting sheep: these words were originally descended from the Celtic languages once spoken in those areas. There are some wildly different variants, as well, although it's pretty apparent that they all share a common root! I liked this name because it emphasized a kind of historical and agrarian tone to the game.

#backburner #tabletop

This is an entry from a list of projects I hope to some day finish. Return to the backburnered projects index.

What is it? A protocol for writing RSS readers, heavily inspired by the maildir format.

The core idea of Lektor is that you separate out the typical operation of an RSS reader into two programs which can work concurrently over a specific directory. One program is called a fetcher, and its purpose is to read remote feeds (of any format) and write them into the lektordir format. The other program is called a viewer, and its purpose is to take the files inside the lektordir and view them.

I've got the full format specified elsewhere but the core idea is that you've got a specified directory called your lektordir, which in turn contains four directories: src, tmp, new, and cur. The src directory contains representations of feeds, new contains unread posts, and cur contains read posts. (The tmp directory is an explicit staging place: fetchers will create posts in there and then atomically move them to new, which means that it's impossible for a viewer to ever observe an incomplete post. Even if a fetcher crashes, it will only be able to leave garbage in tmp, and not in new.)

The representations of feeds and posts are themselves also directories, with files and file contents standing in for a key-value structure. That means a given feed is represented by a directory which contains at least an id (its URI) a name (its human-readable name), and a post is a directory which contains at least an id (its URI), a title (its human-readable name), a content file (which is its content represented as HTML), and a feed (a symlink to the feed that produced it.) In both cases, there are other optional files, and also ways of representing fetcher- or viewer-specific metadata.

The use of directories means that fetchers and viewers don't even need a specific data serialization or deserialization format: unlike maildir, you don't even need to “parse” the format of individual entries. This means fetchers need only a bare minimum of functionality in order to write feeds—I had test programs which used basic shell scripts that wouldn't have been able to safely serialize JSON, but were able to write this format trivially.

My intention is not just to design the format—which has already been pretty thoroughly specified, albeit in beta format and without having been subjected to much testing—but also to write the tools needed to write an RSS and maybe ActivityPub reader on top of this format.

Why write it? Well, I wanted to write an RSS reader, because none of the existing ones are exactly what I want, and this seemed like a good way of splitting up the implementation into bite-sized chunks.

There are some interesting features you get out of separating out the fetcher and viewer operation. For one, fetchers become ridiculously simple: they don't even need to be full services, even, they can just be cron jobs which hit an endpoint, grab some data, and toss it into the lektordir. Instead of a reader which needs to support multiple simultaneous versions (e.g. Atom, various versions of RSS, newer ActivityStreams-based formats) you can have a separate fetcher for each. For that matter, you can have fetchers which don't strictly correspond to a real “feed”: for example, you can have one which uses a weather API to include “posts” that are just sporadic weather updates, or one which chunks pieces of your system log and puts one in your feed every day to give you status updates, or whatnot.

Separating out viewers means that you can now have multiple views on the same data as well. Maybe one viewer only cares about webcomics, so you can pull it up and read webcomic pages but let it omit blog posts. Another gives you a digest of headlines and emails them to you, leaving them unread (but of course omits anything you happen to have read during the day.) Another does nothing but pop up a message if you let unread posts pile up too long. Hell, you could have one that takes your combined posts and… puts them together into an RSS feed. Why not?

The cool thing about lektordir to me is that it lowers the barrier to entry to writing RSS-reader-like applications. It takes the parts and splits them behind a well-defined interface. But there's a lot of cool stuff that you get from trying to apply a one-thing-well philosophy to problems like this!

Why the name? It's for reading, and lektor means “reader” in several languages. I'll probably change the name at some point, though, because there's also a plain-text CMS called Lektor.

#backburner #software #web #tool