7DRL Retro 2024

kyleperik.com

I submitted a game to 7DRL this year, and it was a slog.

I've enrolled a couple times previously, Nookrium played through my 2022 submission, Blunder Crawl, a Chess inspired Roguelike game.

Before that, I ventured to create a traditional Roguelike with octopus tentacle mechanics: Octorogue, my personal favorite.

This year, I submitted a quick and challenging alchemy and identification themed roguelike: Flask Cannon.

Rather than utilizing an existing js framework built for making games in the browser, like rot.js and pixi js, I decided to use this as an opportunity to test out my new borderline esoteric language Beans.

This language relies mostly on it's host virtual machine, uxn for it's lower level semantics. On the higher level is an explicit declarative graph of dataflow throughout the system.

I took the liberty of graphing the dataflow of Flask Cannon:

2024-03-12_20-00-29_7drl2024-graph.png

I generated this by processing the main "beanpod" file here: https://git.sr.ht/~kylep/7drl24/tree/master/item/src/game.bpod

Some things of note: console feeds controls/keyboard, which is the one source of user input to the system. From here, messages are sent to station/caldron, field/entities and system. From there, it becomes a bit of a mess to parse through. Eventually, many of the beans feed data to screen, which is what manages drawing things (mostly sprites and rectangles). The point is, you at least can reason about how data flows from module to module.

A debugging technique that was often was useful was to pipe the output of one bean into console.x, which effectively printed out every message sent through that port.

I just like the feeling of knowing that no data is left unaccounted for, but maybe that's just me.

On the other hand, the biggest challenge, perhaps unsurprisingly, was race conditions.

It was something that snuck up on me, it's not that it created bugs which rendered the game unplayable, they materialized as just oddities about attack and effect timing. It crushed my spirit when I realized there was no easy workaround. I'd expect the enemies to take a turn after you've made your move, instead, you'll notice enemies attack before you make your next move, which can just be annoying. Worse, I was seeing status effects being applied after an enemy dies, in some cases resulting in a second death, and extra damage notifications in the corner of the screen.

Now these problems are not unsolvable, they just are not made straightforward with to address with beans. While the dataflow is strictly well defined, this doesn't mean the propagation of these messages apply in a way that can be relied on. This is something I'll have to give more thought to, clearly this kind of problem has been solved in thousands of other domains, it can be at least made easier to work with in beans.

Beyond that, I found I was continually envying higher level graphics functions that I've grown accustomed to with other frameworks. Writing my own circle function or others would have taken up too much time. Instead, I compensated by using sprites for most things, and developed some mechanisms for drawing larger sprites along the way, the player character and the caldron for example.

The idea of persistent data structures were on my mind as well. While beans ensures no shared mutable data by simply not allowing any shared data at all, this can be limiting and inefficient.

Essentially each byte of data must be copied onto the queue to be processed by a bean, and copied into "local" memory again if it's to be stored. Persistent data structures could alleviate this issue, while maintaining the constraint of no shared mutable data, by allowing shared immutable data. This would incur a decent amount of complexity and logic into the now lightweight beans framework, but I'm thinking it might pay off in the end.

Lastly, I was hoping I'd be able to make some better abstractions, as beans supports nested "beanpods", which can be really conceptually helpful to break down the pieces of a game. Instead, I kept it as a single flat beanpod, which many connections between them. After all, it's better to have to rewrite than to prematurely abstract, and I only had 7 days to make something.

I'm glad I was able to complete 7DRL this year despite the challenges, I'm proud of what I made, hopefully you can have a bit of fun playing it.