Building Minesweeper for Gemini
2023-01-08 | #cgi #games | @Acidus
Over vacation, I built a complete Minesweeper game on my capsule. I implemented flags, chording, different difficulties, and even custom games. You can play it here!
Why write another game?
I had a lot of fun building Where in the World, a geography game in Gemini last fall. This was the first game I had written just for fun in 20+ years.
The constraints of Gemini created some challenges that were fun to solve:
- How to help the user correctly select from a list of 200+ available countries.
- How to maintain state.
- How to draw graphics in a text-first protocol like Gemini.
I knew I wanted to try making another game, and started thinking about different options in the back of my mind when I had a few moments between life and work. I was thinking about more traditional games, and an idea that kept coming up was Battleship.
I liked a traditional board game like Battleship for a few reasons:
- The simple grid, both because it would be easy to draw on a single screen and the "row, column" format was a clean and simple way to get input from the user.
- The game logic was relatively simple.
- I think the game would be fun as the map is slowly revealed.
However, as I thought about it more, I ran into some issues:
- Having the user initially place their ships would be complicated and clumsy. Since the player must do this to set up the game before the start, it would make the initial experience pretty painful.
- Battleship is a 2-player game! Even once I solved how to have 2 humans play each other, each player would have to wait for the other, and Gemini isn't really suited for auto-updating or "push"-style events.
- If I made it a player-vs-computer game, I would need to create a computer player to play against you, and at least for now, that didn't sound fun to do. It would be easy to end up with a computer player that was either too good or randomly made obviously stupid moves, both of which wouldn't be fun for the player.
Taking ideas from the good parts and bad parts, I decided I needed a game that:
- Used a simple grid
- Didn't require lots of input from the user to set up the game
- Didn't have a 2nd player, human or otherwise
Which led me to think of Minesweeper!
Minesweeper is actually one of the first games I made as a teenager, written in TI-BASIC on a graphing calculator, instead of paying attention in school. Fundamentally, you have a 2D matrix, representing all the tiles of the game. You place mines at random coordinates, then, for all the tiles without a mine, you fill it with the count of mines on adjacent tiles. With some clever bitmasking, you can use the same matrix to also keep track of what tiles have been revealed or have flags.
Graphics
The first version of the game looked something like this:
Flags: 6 Total Mines: 10 ABCDEFGHIJKLMNO A 112★ A B 111 1★2★ B C 12·1 1121 C D 1·21 D E 222 111 E F 12★1 1111★. F G ★211 1..... G H 11 12..... H I 1...... I ABCDEFGHIJKLMNO
The '*' is a flag, and '.' is an area you still need to explore. This is really similar to how my TI-BASIC game looked, since that used text characters and didn't have access to the extended ASCII characters. While a promising start, I wasn't a fan of this. The board was too dense. Characters are taller than they are wide, so the board felt cramped and it was hard to move your eyes up to figure out the column you wanted to select.
Luckily, I solved this by accident when I started to use emojis. I knew emojis could create richer graphics since there is an emoji for a flag, and for a bomb, and even blocks and other things. But the challenge is some emojis have a "screen width" of more than 1 character. This messed up the columns as you can see below:
Flags: 6 Total Mines: 10 ABCDEFGHIJKLMNO A 112🚩 A B 111 1🚩2🚩 B C 12·1 1121 C D 1·21 D E 222 111 E F 12🚩1 1111🚩. F G 🚩211 1..... G H 11 12..... H I 1...... I ABCDEFGHIJKLMNO
The solution was to make columns 2 characters wide, with some blank space, so the emojis would fit, but it also solved the "cramped" and "dense" feeling of the board:
A B C D E F G H I J K L M N O a 1 🚩1 1 · · · · a b 1 1 1 1 1 2 1 1 · · b c 1 1 1 1 🚩1 1 · · c d 1 🚩2 1 1 1 1 1 1 · · d e 1 1 1 1 2 🚩1 1 1 1 e f 🚩2 2 1 1 2 2 2 f g · · · · · · · 1 1 1 1 g h · · · · · · · 2 1 1 · 1 h i · · · · · · · 🚩1 1 · 1 i A B C D E F G H I J K L M N O
Other fun stuff
- Just as with Where in the World, I store game state in the query string, instead of storing it on the backend and requiring a client-side certificate. This creates the "feature/bug" that you can simply use the back button to "undo" a move. Since the input method is not as precise as a mouse, sometimes you select the wrong tile, so being able to "undo" something is actually helpful. The game state is a binary blob representing the size of the board, the start time, and the state of all the tiles. This can be quite large, so I gzip compress it, base64 encoded it, which I keep in the path. Overall this worked well, though there was an edge case with vanilla base64 that led to some issues that I @mozz helped me work around:
- To speed up the game, I implemented a feature used in other versions of Minesweeper, chording, where you can uncover other tiles more quickly. This really helps.
- I put a cheat code in the game too! Something happens.
- Entering coordinates can be a little clunky, so based on @skyjake's feedback, I switched to a system where rows and columns use different letter cases, so you can enter them in either order.
- In both my work, and in personal projects, I've repeatedly learned that the final 5-10% of the work is really what makes the project. Adding things like an elapsed timer, a remaining tiles status line, and a progress percentage
Final thoughts, and my next game?
As a whole, I think my Gemini version of Minesweeper turned out just OK. So while I'm playing Minesweeper, especially at night before I go to bed, it’s not as exciting to me as I would have hoped. That could just be me. I hope you all like it.
The reason I feel this implementation isn't excellent all comes down to the "think-move-look" loop. Minesweeper is a pretty fast game. A typical game involves dozens and dozens of moves. Often as you uncover more of the board, you immediately see several tiles that you need to click or where you want to put flags. Only now you have to do the tedious process of making multiple moves really quickly to implement those moves. That's the bottleneck. In short, Minesweeper has a low "thinking to moves" ratio.
This leads me to think that a game with a higher "thinking to moves" ratio would be better suited for Gemini. And the board game that comes to mind that has a high "thinking to moves" ratio is chess. Specifically, chess against a computer opponent. I'm not sure when I'll do this, but I think that will be my next game.
Of course, I really don't want to implement a chess engine, but there are open-source chess engines of various skills. Even better, these chess engines are decoupled from the user interface. This means I can write a Gemini interface which displays the board and gets the user's input for their move, and pass it to the chess engine for the computer to make a move!
Can you think of another game I should build? Drop me an email or ping me on Station