Lessons learned from Elm Game Jam #4

The Elm Game Jam #4 on the theme of animals and nature just ended this week. My submission was an ecosystem simulator where players attempt to balance a population of foxes and rabbits by fiddling with population parameters such as metabolism rates or energy costs for reproduction.

Here are some lessons I learned and concepts that were re-affirmed by building here:

Get to playable ASAP

This is something I’ve talked about before, but didn’t do a good job of putting into practice on this project. I sank too much time into the rules for interactions between foxes and rabbits and ended up a week before the deadline with a “game” didn’t end and had no meaningful player decisions.

For future jams, I would aggressively push for the following milestones before investing in other things:

  1. Show something visual on screen
  2. Have a way for users to interact with the game
  3. Have game end conditions

Refactoring

Elm is so fun to refactor! Part of the fun of a game jam side project is exploring data modeling and changes that have to happen as the game evolves. However, it can easily prevent you from shipping. I feel like I did a really good job on this project at balancing refactoring and feature work.

One really valuable thing I did was keep refactoring and feature work in separate commits. These typically took one of two forms: either cleaning up tech debt after a feature, or “making the change easy” before a feature. Both can be seen on the screenshot below.

Invest in your tools

Some small investments here really payed off. I documented the processes I used to build and release the app as part of the README. In combination with some handy bin/build and bin/server scripts, this made it easy to jump back into the project after a week or two not touching it.

Part-way through the project, I learned about butler, itch.io’s CLI tool. This made it much easier to release a new version to itch than fiddling around on the web UI. I wish I’d started using butler earlier on the project.

I automated deploys to Netlify on pushes to GitHub which is really nice. I wish I’d taken the time to do something similar with itch, or at least written up a bin/release script.

Yak shaving

Part-way through the project, I noticed that foxes were moving to bizarre locations. I quickly realized that this was due to a bug in the way a grid library I was using calculated neighbors. I dug into the source and quickly got lost on a yak-shaving adventure. Eventually, I was able fix the issue and copied over a patched version of the library into my project.

Figuring out this bug was a significant time-sink and also killed my motivation to work on the game for a few weeks. I was hoping that using a grid library would allow me to focus on game logic but between this bug and having to layer on extra behavior to fit my use-case, I’m not sure I saved a lot of time here.

This may have been a case of pulling in a dependency that was the wrong tool for the job since the project was initially created with somewhat different use-cases in mind. On the plus side, I can probably extract several nice patches that can be submitted to the upstream project.

Draw your problems

I often find that expressing problems in a visual medium is a handy debugging tool, particularly for spatial problems. This technique saved my bacon when debugging the incorrect neighbors issue mentioned above.

A 3x3 square contains all the possible configurations of corner, edge, and center pieces so I drew out a series of 3x3 squares showing, for a given cell, what cells I considered neighbors and which cells the library told me were neighbors. With this diagram, I was able to see that the library worked fine on center pieces but broke down when dealing with (literal!) edge and corner cases.

As a follow-up, I turned this diagram into a series of tests to help guide my exploration. Eventually, I was able to track the behavior down to a subtle combination of two different bugs :sweat_smile:

CSS Grid

Game jams like this are a nice chance to try out a new library or technology. On this project, I used CSS grid for the first time. It reminds me a bit of the way elm-ui works. I only did enough to get a basic skeleton layout but would be interested in digging deeper :+1:

Custom types led to a nicer API

There are a lot of numbers going around in the game and eventually I decided to tag all of the energy values with a custom Energy type. You can’t do math directly on custom types and implementing equivalent energyAdd functions was a bit ugly. After some thought, I realized actual things I wanted to do were not math but domain operations. I went from code that looked like:

if fox.food > foxBirthCost + foxCostOfLiving then

to

if canSupportCosts [ foxBirthCost, foxCostOfLiving ] fox then

Turns out I can have my cake and eat it too! :cake:

Dynamic record setters

I have a bunch of different form inputs that set energy values on a record. Since these are all really similar and it was getting annoying to have a message for each one, I created some helper functions to allow me to have a single message and to dynamically set keys in the record. I didn’t want to use a Dict because the keys are fixed.

This pattern isn’t particularly pretty but can occasionally be convenient, particularly if dealing with a combinatorial explosion of messages. In general though I don’t think it’s worth it. In this particular case, it might have been better to keep the verbose approach with one message per field :man_shrugging:

type alias RabbitConfig =
  { costOfLiving : Energy
  , grassNutrition : Energy
  , birthCost : Energy
  }

type RabbitField
  = RabbitCostOfLiving
  | GrassNutrition
  | RabbitBirthCost

setRabbitConfigField : RabbitField -> Energy -> RabbitConfig -> RabbitConfig
setRabbitConfigField field energy config =
  case field of
    RabbitCostOfLiving -> { config | costOfLiving = energy }
    GrassNutrition -> { config | grassNutrition = energy }
    RabbitBirthCost -> { config | birthCost = energy }
27 Likes

Thanks for the writeup!

Re: fields, I found it works really well when you have different messages for every field (say: set value, reset, toggle tool tip,…). For just setting yeah you don’t gain much

1 Like

Nice write-up! As for the separate commits, if you don’t know of it yet, there’s semantic commit messages, I use this approach in many projects, helps a lot to think about what category changes belong to.

You’ll never again be tempted to include a bug fix and a feature in the same commit. My git log is now an easy-to-skim changelog.

EDIT: feat: Provide money quote
EDIT: fix: Wording

1 Like

Thanks for sharing @mfeineis

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.