Introducing Quaff

Posted on by Owen Lynch

What is Quaff

Quaff is a multiplayer extensible roguelike written in Elixir. Its name is a reference to the fact that the “drink a potion” action in Crawl is called Quaff, and given that I am writing this in Elixir, it seemed appropriate. In this blog post I will go into some of the motivations and concepts behind the creation of Quaff.

First, some hedging

This post might be a bit premature because as of now Quaff is but a proof of concept hack and a twinkle in a couple people’s eyes. The proof of concept hack is merely to show that the technology stack for a multiplayer terminal-based game can be written in Elixir, as to my knowledge no-one has done such a thing before, however I don’t expect a good deal of its code to be put into the next iteration and it is relatively pedestrian. The interesting bit of quaff comes from my ideas on how to make an extremely flexible game engine in a way that I don’t think has been done before. In this post I will talk about some of these ideas, as their theory is worth getting out in the open even if they haven’t been implemented yet. This is to say, the answer to the question “But does that actually work” is a definitive maybe.

Motivation

I have not found a single video game which captures the experience of playing Dungeons and Dragons with a group of friends. Dungeons and Dragons, and in general table-top rpgs, seem to me in many aspects much more fleshed out and complex than any video game. Part of it is that an actual human is controlling the monsters and NPCs, part of it is that the rule system can allow for much more flexibility because it is interpreted by humans instead of computers. Moreover, creators can create much more easily because they can just write in human language instead of having to translate everything to the computer. However, in other ways video games can blow Dungeons and Dragons out of the water. For instance, nothing in a table-top RPG comes close to the digital economies in MMORPGs (massive multi-player online role playing games), or the vast creations in Minecraft, or the strategic maneuvering of a MOBA (don’t actually know what it stands for, think League of Legends or DOTA). This is because the computer can do things that are simply prohibitive to pen and paper.

Barring the development of strong AI, are we stuck with a inherent choice between the constraints of humans and the constraints of computers? Maybe. However, with any complex tradeoff it is unlikely that current systems have struck the optimal balance, so there is certainly room for improvement. Before talking about Quaff, let’s look at a couple of games that try to bridge this gap.

  • Dungeonland

    Dungeonland is a game which has a dungeon master. As the players fight through a dungeon, one player gets to assume the role of dungeon master and summon monsters, lay traps, and cast spells to stop the PCs in their tracks. It’s a great idea, but the game is lacking in execution. The mechanics are a bit flat and the gameplay is repetitive. However, it was this game that got me thinking about having a dungeon master in a computer game.

  • Minecraft

    Minecraft is a revolution. The mechanics are simple, there’s no storyline, the graphics are dull, but anyone can create anything. However, people don’t just make things by themselves, people make things together. The fun of minecraft is in creating art, exploring the world, and interacting with other humans, all of which are things that table-top RPGs typically beat computer games in. However, one big flaw of Minecraft in comparison to table top RPGs is that modding is not very unified with the normal in-game creations. That is to say, in-game creations can be improvisatory. One experience that should be familiar to most people who have played Minecraft is building a very tall pole by jumping and placing building material below you in order to escape some situation. However, you can’t improvise new game mechanics in response to situations, unlike D&D. For instance, what if a player wanted to create rocket fuel using sugar, and then pack it into bamboo tubes to make rockets. In Minecraft there are set crafting recipes, you can’t do this. In D&D, the dungeon master could use their judgement and knowledge of chemistry to make up a mechanic on the spot, and then consult standard tables to figure out how to model the propulsive effect.

  • Dwarf Fortress

    Dwarf Fortress gets around the “not enough complexity and detail without a human behind the scenes” problem by just stuffing insane amounts of complexity and detail into the game from the start. The problem with this approach is the same reason you have probably never played Dwarf Fortress: it can be quite overwhelming. The relevant bit about Dwarf Fortress is that it manages to have a great deal of complexity in mechanics because it has a very simple visual system. Freed from the burden of drawing graphics for every new mechanic, the game designer could put many many more mechanics in. The barebones graphics also allow you to use your imagination to visualize your dwarfs and their caverns, which is arguably a feature rather than a bug. However, Dwarf Fortress was invented and written by a single person, and can only be modified by a single person. Moreover, only one person can play at a time. These characteristics both strongly restrict the ability

When we look at the successes and failures of each of these games (at least, the way I’ve presented them), the conclusion becomes this: to bridge the human vs. computer gap we need to create a game with the following attributes

  • Enough simplicity for humans to understand/modify it (like Minecraft structures or Dwarf Fortress text-only interface)
  • Human driven plotlines (like Dungeonland’s dungeon master)
  • Architected with modifiability as a first-class citizen (unlike Minecraft mods)
  • Has the capability to support immense amounts of complexity (like Dwarf Fortress mechanics or Minecraft structures)

Thus, was the idea for Quaff born, a game that would incorporate all of these features and allow for computer aided adventures of greater depth and humanity than previously possible.

REAL Object Orientation

Before we can talk about Quaff, we should talk about the technology that makes it possible: Elixir. Elixir is a functional language that wraps the Erlang platform in a ruby-like syntax. If that meant anything to you, great. If it didn’t, don’t worry, it’s not particularly essential. The important feature about Elixir with respect to Quaff is that it is object-oriented. Now, some of you will no doubt be thinking “but I thought you said it was functional!” This is because when most programmers hear object-orientated, they think Java, which is very much not functional. However, this was not always the case. Back in the day, object-oriented programming didn’t mean classes and member variables and methods and inheritance, object-oriented programming meant message-passing. This means that it revolved around lots of bits of code running in conceptually isolated islands, sending messages (perhaps in bottles) to each other. In a “modern” object-oriented language, like Java, objects control each other by calling methods or changing instance variables. In a message-passing system, objects don’t control each other, they just send messages to request other processes to do things, and if another process doesn’t feel like responding, it doesn’t have to. Objects are separate and autonomous.

One of the great benefits of this is that failure in one object can be effectively routed around. In Java, if one object throws an exception, it’s game over for a whole thread, which may comprise many many objects. In a message-passing system, one object corresponds to one thread, so individual objects are much more loosely coupled, and it is easy and natural to have designs in which failures in one part of the system can be routed around. Erlang, the language on which Elixir rests, was originally written to manage huge distributed systems that were expected to behave in unexpected ways. Being able to maintain large parts of the system in the face of the failure of small parts was an essential feature.

The other great benefit is that individual objects can be brought online or offline, and can have their code changed, without taking down the entire system. Wired into the message-passing model in general, and specifically Elixir and Erlang, is the idea that the code that you write will never fully solve the problem, so being able to change the code is essential. The ease with which Elixir programmers can change running code is enabled by both the object-oriented features of the language, which isolates different parts of the system from each other, and the functional features, which ensure that the data manipulated by the program is explicitly kept track of, so that it can be saved, manipulated, and restored, ready to be operated on by the next version of the code.

The upshot of all of this for Quaff is that Elixir is the perfect playground for writing a game where changing the rules and experimenting with new monsters, mechanics, spells, and of course potions, is a first-class citizen.

Game Implementation

This is the tricky bit. As one might guess, the game implementation is based around message passing between independent processes. The map is a process, the clock is a process, the UI is a process, each monster is a process, etc. The processes send each other messages related to their function. For instance, the clock manages cooldowns and turns. When a cooldown finishes, the clock sends the player whose ability was on cool down a message. The map manages relating different entities in space. Any process can send a message to the map to query what exists in a certain square, and players or monsters might receive messages when something moves into their field of view.

In the nomenclature of Quaff, the main unit of computation is called a “spirit”.1 Spirits adhere to a standardized interface for events, which are special types of messages that they send to each other and subscribe to. We group events into two categories: blocking events and non-blocking events. When spirit \mathcal{A} sends a blocking event to a spirit \mathcal{B}, \mathcal{B} must respond before \mathcal{A} can keep going, in other words, the event “blocks” \mathcal{B}.2 However, nonblocking events don’t have to be responded to: if \mathcal{A} sends a nonblocking event to \mathcal{B}, \mathcal{B} is not expected to respond.

Special Spirits

There are a couple special spirits in the game.

Chronoqueue

The idea behind the chronoqueue is that we want to combine the benefits of a continuous time system (you can use fine-tuned delays as a game mechanic) and turn-based time system (you have time to make decisions). This is similar to how time works in, for instance, Crawl, except in a multi-player context. The chronoqueue accepts events to be sent after a specific delay. Then, it puts those events into a priority queue, and pops them off one at a time based on their time-ordering. From an in-game perspective, this makes it look like the events are happening in continuous time (because time can be arbitrarily sub-divided), but from an out-of-game perspective it looks like turn-based (because you can pause between each event).

The Map

Just like in Dora the Explorer, the map in Quaff has a life of its own. The map does for space what the chronoqueue does for time: it allows other spirits to interact within a common spatial framework. It also has some additional features, like computing what each player can see and sending it to them, and firing events for traps etc. when spirits walk into some squares. The map is fairly large and complex, so I am still trying to figure out ways to split it up.

UI Spirits

These are special because they are the only spirits that run on the client instead of the server. These spirits handle receiving user input and displaying the map and status changes.

RNGesus and His Disciples

These will be spirits that handle random numbers. One thing I want to try in this game is to have random events with non-uniform distributions, like guassians or exponentials, and computation of those will also happen on RNGesus.

Unknowns

One of the biggest hurdles is whether all of this networking and message-passing and functional programming can lead to a playably fast game. I’m hoping that because the interface is text-only, I won’t have to worry about a lot of the stuff that slows down other games, but on the other hand dwarf fortress (for instance) manages to take a good deal of system resources even though it is text-only. Currently the map is implemented with a quad-tree, and I don’t know how that will affect performance. The other big unknown is whether my dream of being able to live-code in monsters and effects is remotely feasible. It may just not be possible to do stuff that complex on the fly without a human level intelligence there to interpret ambiguities.

Wrap-up

The purpose of this post wasn’t just to lay out a plan, it was to spark discussion. Currently I am fairly busy with other things, so I expect that I won’t get back to this for a while. However, if this inspires you, the code is on github: feel free to fork and experiment but be aware that not much of what I described here actually exists. Once I get around to making this a reality, I will update this blog post with what I have learned!


  1. A spirit is a both an autonomous something and a drink.↩︎

  2. Yes, that is mathcal.↩︎


This website supports webmentions, a standard for collating reactions across many platforms. If you have a blog that supports sending webmentions and you put a link to this post, your blog post will show up as a response here. You can also respond via twitter or respond via mastodon (on your preferred mastodon server); through the magic of brid.gy all tweets or toots with links to this post will show up below (subject to moderation).
div

Site proudly generated by Hakyll with stylistic inspiration from Tufte CSS