Aug 4, 2023

I made Conway's Game of Life, except it's multiplayer, runs in the terminal, and accessible via ssh.


multiplayer game of life

The Idea

game of life menu

Somehow I got hoodwinked by @purduehackers into holding a workshop to teach Go. This was good, because it justified the week I spent learning Go.

Conway's Game of Life seemed like a simple interactive program that someone could reasonably code in a new programming language.

After a little work, I ended up with a small (less than 200 line) terminal application, written in Go using the bubbletea library made by @charm, and a slideshow using slides, a terminal slideshow app.

The Workshop

me talking

Not many people showed up, and I think only 1 person actually coded the program from start to finish. Having the finished project available on Github beforehand was a good idea, but the live coding was not very successful.

Talking while typing was hard even though I was very familiar with the code, and the whole experience just wasn't very engaging. People typed along or watched without absorbing much, and I just ended up talking to myself.

At the end, when someone asked what they could actually use Go for, I struggled to give a suggestion outside of "make a web server". Turns out, I'm not an expert in Go (or actual usecases).

Ultimately, I think the code was still too complicated for the given time and the end result was too niche.

This was a canon event learning experience.

Learn more about the workshop

Here is a super brief rundown of all the logic needed for Conway's Game of Life.

Create a new board

Create next board state

Loop over every cell

Check neighbors

Add state transition rules

Full code

func NewBoard(width, height int) [][]bool {
board := make([][]bool, height)
for i := range board {
board[i] = make([]bool, width)
return board

You might be thinking that the code could be more efficient. I also felt the urge to optimize especially after reading about some mind-bending implementations, but I ran a benchmark first.

The code was waaaaaaaay more than fast enough. I guess the real premature optimization was the advancements in hardware made along the way.

SSH & Multiplayer

Inspired by SSHTron, I decided that I had to add multiplayer. The workshop was over, so there was no point, but I wasn't quite ready to let go... of my gol...

This is where the wish library, also made by @charm, came in handy. It handles ssh connections and even comes with built in middleware for bubbletea applications.

With a few lines of code, the workshop project became an SSH app.

s, err := wish.NewServer(
wish.WithAddress(fmt.Sprintf("%s:%d", host, port)),
MiddlewareWithProgramHandler(teaHandler(gm), termenv.ANSI256, gm),

Of course, the true challenge was the multiplayer experience and debugging countless race conditions caused by my poor decision making.

Here is a diagram of the complexity demon I summoned. For simplicity, only one player is shown. In reality, one lobby exists for each game, which has up to 10 players, each with their own playerState. (In actual reality, there are zero players, but you get the point).

complexity demon data flow

manager controls the creation and joining and leaving of game lobbies.

player represents one connected user, which their own goroutine handling input and displaying the board. They send commands to a lobby and update their playerState.

lobby runs one Game of Life board and sends updates to all players.

I'm not proud of it, so I'll save you any further details, but I'm 100% sure I solved 90% of the possible game breaking bugs with careful usage of a few mutexes.

Everything Else

Although the game board has a fixed size, I decided to make it tile infinitely to fill terminals of any size and allow player movement. This was hard, but only in the way math is.

testing tiling and movement

I contemplated summoning more complexity demons for the sake of rendering optimization, but thankfully I gave up and just added more colors.

testing many player colors

And months later, I wrote gave it some polish and wrote this post.


There is no moral to this story. I made a multiplayer ssh Game of Life terminal app, because I was sick of Javascript, but now it's time to let it... Go.

Thanks to @purduehackers and @matthiewstanciu for the idea and motivation and fun times!

original workshop idea dm