Mafia game using Elixir + Elm: Backend

Mafia (a.k.a Werewolf) is a party game modeling a conflict between two groups: an informed minority (the mafia), and an uninformed majority (the innocents), while I am trying to model a harmony between Backend (Elixir) and Frontend (Elm):

import Elm
import Elixir

type alias Mafia =
   { backend : Elixir
   , frontend : Elm
   }

type alias Wefewolf = Mafia

In these blog post series I will cover the main idea, the intuition behind the backend architecture and the moments which I found interesting on the frontend. Therefore, I am going to divide the series into two parts: the first one about Elixir and the second one about Elm (mostly because currently only one category can be set to a blog post and I was perplexed which one to choose 😅)

The first working version is ready and recently upgraded to Elm 0.19 and Elixir 1.4.0-rc.2 and can be found on GitHub .

Synopsis

Even though this is a web application, the game is actually offline. The main idea is to replace the narrator which asks a city to sleep/wake up, remembers the choices of the mafia, laughs at the ignorance of the players.

A screen visible to everybody (for example a TV screen) plays the role of a narrator and displays which state of the game it is (Leader window). Players, being in the same room, connect to the game via a mobile device and therefore has a separate screen (Follower window) for viewing the personal data (which role in the game they were assigned, UI for nominating a particular user). Depending on the current state, Leader window either displays call-to-action on the screen and/or plays a voice message.

The following video demonstrates the current game flow (but actually without audio):

Another way to see it in action is to run the acceptance test within demo environment.

Or just play with it: https://mafia-game.gq

Architecture

My personal requirements to the backend architecture were:

From the high-level perspective playing real-world Mafia game reminds transition between states: Day TimeNight TimeDay Time.... From the lower level, it reminds transition between more specific states: City SleepsMafia Wakes Up...

Based on the intuition from the real-world game I decided to reflect each state in the code-level module and transfer from one state to another. If a state contains too much responsibility, it can be split on multiple more specific ones and therefore the logic will be extracted into separate modules at the code-level. For example, Night Time holds too much responsibility for a state, what about creating more specific ones instead: City Sleeps, Mafia Wakes Up, Mafia Sleeps… I’ve been probably too lyrical and named such states as chapters🙃

As a result, it turned out into a linear structure where every chapter executes and at the end calls the next one. Like classic job workers, but with an essential difference: some of the chapters have indefinite time of execution. For example, when mafia makes its choice, the worker must wait for a signal to proceed: this is where Elixir's (Erlang's) processes with messages really shines💡

Below is a diagram with all the chapters that exist at the moment. Arrows define the transitions, which are in some cases conditional (for example, when the winner is chosen).

Code-wise mostly each of these chapters has a separate module created (PlayerN ones are obviously created dynamically). The module represents a GenServer process. During its lifetime the process performs a particular action and/or sends a command to the frontend via websockets in order to control the game flow (display a message, play a sound and etc…). After its execution the process stops normally using {:stop, :shutdown, state} as a return value from a callback.

As a result, it has come down to three types of processes:

Has the architecture satisfied my personal requirements?


Conclusion

I liked the experience of working on this game. Process-oriented paradigm seemed like a natural choice for my type of problem. Elixir has great features as a language itself and growing ecosystem which provides solid tools, such as Phoenix, Hound and etc, which helped and will help me to enhance the application.
Next time I’m going to describe the frontend part of my application written in Elm. Stay tuned 🤙