This static site is built entirely with HTML and CSS, without any Javascript. It's also built to last. Check out this manifesto on preserving web content for more details.
Built by Jon Choukroun
Do you remember Drug Wars? I spent hours during high school math class playing that game on my TI-83 calculator. I got hooked on the game's simplicity, fascinated by how the combination of RNG and an edgy subject combined to make it infinitely replayable.
So in 2017 when I needed a project to learn the Elixir programming language, Drug Wars came to mind. I had started reading the excellent Pragmatic Programmers book Functional Web Development with Elixir, OTP, and Phoenix, which provides an in-depth tutorial building a Battleship-type game. But instead of following the material to the letter, I decided to build my own game with the book as a guide. I didn't want to rip off John E. Dell's original either, so I spent some time brainstorming a new world.
Right off the bat, I wanted to give Future Butcher a cyberpunk feel. The original title was Slinger, and the player could buy and sell brain implants with different gameplay effects (free turns, greater strength, etc). But the concept was too unfamiliar for players who hadn't read any William Gibson and too derivative for those who had. Instead I took the game in a new direction - cannibalism as a hot trend.
I kept the cyberpunk feel, with a UI intended to remind the player of an old terminal. Some of the events and encounters reinforce that cyberpunk feeling, with mentions of predatory corporations, weak (and possibly failed) governments, and near anarchy.
Following the PragProg book's approach, I designed a stateful game engine in Elixir. It handles turns, player health, transactions, and game rules. Here's an example of how Elixir's pattern matching prevents a transaction if the game is in the wrong state:
# The :buy_cut transaction also requires state to be :in_game
def check(%Rules{state: :in_game} = rules, :buy_cut) do
{:ok, %Rules{rules | state: :in_game}}
end
# Same for the :sell_cut transaction
def check(%Rules{state: :in_game} = rules, :sell_cut) do
{:ok, %Rules{rules | state: :in_game}}
end
# The catch-all match indicates a rules violation
def check(_state, _action), do: {:error, :violates_current_rules}
This engine is a dependency of the server application, which was built with Elixir's Phoenix. This API uses Phoenix Channels (a convenient Websockets wrapper) to communicate with the client. Once the socket is open, the API processes client requests and calls game engine functions as appropriate. This means the client can't cheat by passing false data in its requests.
When the client sends a start game request, the API spins up and supervises an OTP process, using the GenServer. This is how the game manages state. Because the server application runs in an Erlang VM and the process is kept alive by the GenServer, a user can close their browser and later return to their game.
Authentication is non-existent, because I wanted to provide an arcade experience. There's no need for users to create an account, provide an email address, etc.
The original client was an Ember.js application, but in 2022 I rebuilt it entirely with React and Typescript. This web application is much snappier and less buggy. The design has also improved significantly.
Building this game was quite the learning experience. Not only did I become comfortable working with Elixir and the Phoenix framework, I also learned a lot about deployment and server management. One of the biggest headaches was setting up a reliable deployment pipeline for the API and game engine, that's both secure and manageable.
When I launched the original version with the Ember.js web client, I did a minimal amount of marketing to see if the game could build some momentum. I posted it on some relevant subreddits, Product Hunt, and a couple other places I could expect to get interest. For a while, I got a few hundred new users per day - which was mind-blowing for me. But soon only the most hardcore players kept coming back, mostly friends of mine. One player, Doh, continues to play and even has the high score.
I may choose to spend some time marketing the game again, this time more strategically. But since I have no interest in monetizing this game, it's not a top priority. Ultimately this was a fun and rewarding experience and I hope to find time to build another game in the future.
This static site is built entirely with HTML and CSS, without any Javascript. It's also built to last. Check out this manifesto on preserving web content for more details.
Built by Jon Choukroun