DEV Community

Jayaed
Jayaed

Posted on

How I built a multiplayer game inspired by Contexto

Origin

Earlier this year, at a hackathon I attended, the hosts decided to play Contexto with everyone while waiting for contestants to arrive. The game was fun, but because Contexto is single-player, playing it with a whole venue quickly became awkward. Everyone was trying to get their answer inputted at the same time.

That was where the idea for this project came from: a multiplayer version of a Contexto-style word guessing game.

The Process

Initially, I had a basic understanding of what needed to be done, so I started by looking for an open-source word vector dataset. I found Stanford’s GloVe word vectors:

.

I processed and cleaned the word list, then converted the usable vectors into a vectors.npy file. One issue I faced was the size of the original dataset. Loading a large word vector database into memory slowed down startup time, so I needed to reduce the number of words.

After looking through the word list, I noticed that many entries were uncommon or not useful for a casual guessing game. This led me to use words from the 6of12 dictionary instead. Filtering the vocabulary this way drastically reduced the size of the vector file and improved load times. However, the vectors used were still from the GloVe word vector.

The backend also loads the vector file with NumPy memory mapping, which helps avoid loading the entire file into memory all at once.

Once preprocessing was done, I started building the backend.

The Backend

The backend is structured around HTTP endpoints and WebSockets. HTTP is used for actions like creating and joining games, while WebSockets handle live gameplay updates between players.

When a game is created, the backend chooses a target word, either randomly or from the host’s input. It then compares that target word against every valid word in the filtered dictionary.

The similarity calculation uses cosine similarity. In the code, this is implemented as a dot product because the vectors are already L2-normalised. The words are then sorted by similarity, producing a ranked list where rank #1 is the target word.

For each game, the backend creates a ranking map where each word points to its rank. This map is stored in Redis as a hash, so when a player guesses a word, the backend can look up its rank in O(1) time.

WebSockets are then used to broadcast guesses, duplicate guesses, player joins, game starts, and game-over events to everyone in the lobby.

This project was my first time using Redis. After some research, I decided to use Redis Pub/Sub as a lightweight message bus for live game events.

Without Pub/Sub, the backend would only be able to broadcast messages to WebSocket connections held in the current Python process. That works locally, but it becomes limiting once the app is deployed or scaled across multiple processes.

By publishing game events to Redis, every backend process can subscribe to the same channel and forward relevant updates to the players connected to it. This makes the multiplayer system more scalable and avoids tying live updates to a single server process.

I also used Redis to store game state, word rankings and players. This was incredibly useful allowing for players to join an on-going game and be able to see the current game state.

The Frontend

The frontend was built using React, TailwindCSS and Vite. I kept style simplistic, the aim of this project was to focus on the backend of an app rather than the front end. I believe there is a lot of room for improvement on the frontend.

Deploying

When deploying this, I decided to use Railway for its easy integration with Github and deploying instances of Redis being easy. However, I still needed to have a Docker file for it to build correctly. And so began a rabbit hole of learning how to use Docker. Eventually, the front end and backend successfully built and I was able to deploy here:

What I learnt

Over the course of building this project, I learn a lot about technologies such as Redis and Docker. Building reliable, performant applications and a real working backend.

Top comments (0)