I started working on a game that my friend Ken Finney pitched back in 2015. It was called Thumbs Down and it was a very simple game. People would all open the game, put their thumb on the screen, starting the game, and whoever can keep their thumb down the longest wins. I immediately liked the idea. I thought it was funny, and I liked the simplicity.
Ken really wanted the game to not be played over the internet though, it was important that you played with real people in person, enabling hijinks like distracting and trying to tell people to put the game down. I agreed, that all sounds like fun.
So I started working on a native iOS app/game that utilized local Bluetooth networking to play the game with other people around you. It was difficult managing the state of the game synced between all the players, and it was difficult working with the local communication Bluetooth libraries. It sort of worked, but it was buggy. Sometimes you could connect to peers and sometimes you just plain couldn't and it was difficult to debug. It was hard to test too.
When my first kid was born, things got busy, other projects came up and I never finished. Sorry Ken.
As for the design, it was pretty cool. We were going for this tough guy/wrestling motif. Here's an example from our mockups:
Around November 2018 I wanted to learn more about Phoenix and Elixir so I decided to give Thumbs Down another go, but cut out the parts that made it not work for me: local Bluetooth networking. Instead I would build it as an online web game. This would free me to work more in my wheelhouse of backend web development and simple React-based frontend work.
I had done a little Phoenix before, so I was able to get a lot of the annoying parts of the Bluetooth implementation (presence and peer state management) done very quickly with the first-class websocket support and the Presense API Phoenix provided.
Elixir's pattern matching made game state management a breeze. I initially thought I would need some sort of state machine to manage the state of the game, but I was able to write handler functions with different game state signatures that made the code super readable and simple.
I got to use GenServers! To track the state of an ongoing game, I stored the game state in a GenServer process. When the game was over, I stuck all the details into postgres and terminated the process.
To give the game a little more incentive to perhaps just play by yourself even, I added the concept of a leaderboard. Keeping track of the length of games and who the winners are, I was able to quickly spin up a leaderboard of the longest thumbs-held-down.
For the design, I kept it as plain and simple with plenty of emoji.
I really enjoyed building it and diving deeper into Elixir.