I haven’t done any game development in years. My enthusiasm for game development took a big hit after I struggled to get used to the changes to the IDE that came when GameMaker Studio went from 1.4 to 2.0. I still don’t like the GMS 2 UI, for many reasons. The GMS 1.4 IDE had plenty of UX issues itself, but I thought that YoYoGames threw out too much when they did their ground-up rewrite of the IDE for the 2.x version. A more incremental UX transition from 1.x to 2.x would have been better.
GameMaker 2 is definitely improved over 1 in many other ways and obviously is the way forward for anyone who wants to work in GameMaker. But GameMaker is still rather limited in certain ways that I’ve always found annoying, and so I had the choice: stick with it, switch to something else, or quit.
I opted to quit. After spending nearly 10 years working in GM8 and then GMS1, I didn’t want to start over learning a new tool, and even switching from GMS1 to GMS2 felt enough like learning a new tool that I didn’t want to do that either.
Having taken all that time off, though, I realize that I still have a desire to make games, provided the experience is itself enjoyable more than it is painful.
In 2020, during pandemic lockdown, out of boredom I made a Snake clone in GameMaker, called Tangle, and it made me happy.
The other day, I was feeling nostalgic for the old built-in Windows game Minesweeper, and discovered that it is not included with Windows 10. You can install a version of Minesweeper from the Microsoft Store for free, and so I tried it, but I hated it. They added all this needless tutorial crap to it that tries to teach you how to play minesweeper by telling you how to do every little thing — stuff that’s easy to figure out on your own, and more enjoyable and rewarding to figure out on your own.
There was a fad in game design over the last 10-20 years to build the manual into the game, in the form of tutorials that told you about how every feature in the game works and how to do every single thing. This is a great idea in theory, but in execution they often prove more annoying than useful. There’s so much built-in gamer knowledge that any experienced player will bring to a new game, from the hundreds of games they’re already familiar with. Such conventional knowledge is just tedious to explicitly spell out for a player.
As well, putting up signposts for every single mechanic is insulting to the player’s intelligence. It’s really more delightful to discover things through exploration and experimentation than it to have the experience of someone holding your hand and telling you when and how to interact with every single thing in the world.
At the same time, in a lot of games I find that I can get frustrated when I can’t figure out how to do something, and I spend a long time trying to figure it out, and it’s not obvious how to “do the thing”. Often the game world is dressed up to look like a realistic world that is full of things we’re familiar with, and we bring assumptions and expectations about those things with us. When we see a chair, we think “I know what that is”. In many games, a chair is just a tile that you can’t interact with, that serves to convey to the player that they’re indoors in a furnished room that has a chair. But there’s nothing to do with the chair. You can’t sit on it. You can’t pick it up and use it to tame a lion. You can’t set it on fire. You can’t break it over someone’s head like a pro wrestler. You can’t move it next to the shelf so you can stand on it and get an object on the top. It’s just a tile that you can’t walk over and have to walk around.
And that’s bullshit. Because it creates the expectation of useless or limited usability in the other objects in the world. You see another object in the game, a chest. You know you can open the chest and find a treasure. You know some chests are locked, and some are trapped or trigger encounters. But even though the chest looks like it is made of wood, you can’t break it up and put the pieces into the fireplace, or set a fire and use it to signal an ally far away on the map, or keep yourself warm in a cold zone.
Games like Legend of Zelda: Breath of the Wild amazed us with the greatly broadened scope of things you could do with nearly every object in the game world that you could see. But that amazement was a break in expectation from what decades of more limited games had trained us to know about interactive game worlds.
Minesweeper is super simple and abstract — about as far away from BOTW as you can get. You can figure out how to play it in a few seconds by clicking. There’s no need for a tutorial mode with achievements to track the things the game explicitly taught you to do. You just go directly to the game play, and you figure it out, all on your own. This makes you feel smarter, empowered, and like the game respects your intelligence. The Microsoft Store version of Minesweeper treats you like a baby, insultingly trying to be helpful and explaining things to you that you already knew, or could have figured out faster without their “help”.
It turns out that the OG minesweeper is available for download if you google for it, and you can still run it on modern day Windows. I don’t know how legal it is to download, but it ain’t like anyone’s making a fortune pirating minesweeper, a game that was given away for free for over a decade with every install of Windows.
So I downloaded it and played it and, you know, it’s just minesweeper, but as I was playing it I was appreciating the design of it, the simplicity, the clear logic that it communicates to the user through its iconography. And I have to say, minesweeper, though utterly commonplace, is really well designed as a game.
I thought, “what the hell” and decided to try to make it myself. I just wanted to see if, A) I could do it, and B) if I could find the experience enjoyable.
It turns out, I could.
I fired up GMS1.4, still installed on my PC, because I didn’t feel like being frustrated by the lack of familiarity with where all the controls in the GMS2 IDE are, and how they all look familiar yet work subtly differently.
Minesweeper is really, really simple. You have a grid of cells. Apart from edge cells, each cell has 8 neighbors. A cell can either have a mine in it or be empty. An empty cell is safe to click on, and doing so reveals how many mines there are among the eight neighbors. You can then use this information to deduce the locations of mines. Once you are sure of a mine’s location, you can right-click the cell to mark it with a flag. This makes the cell safe, because you cannot accidentally set off a mine in a cell that has been flagged. You proceed to traverse the entire minefield, until you have identified and marked all of the mines correctly. If you make a mistake, and click a cell containing a mine, you lose the game. When you lose the game, the entire minefield is revealed, and if you mistakenly flagged any empty cells, those flags are marked with a red X to indicate your error; also the mine you clicked on to end the game is drawn with a red background to indicate it blew up.
There’s a bit more to it than that: a timer, which is used to rank players based on how quickly they can complete the game; a scoreboard of high scores; also the first click in the game is always “free” — the position of the mines is determined after the opening move is played, so that the first cell the player clicks on can always be empty, and thus you always have a chance to win the game — there are no unfair first-move deaths. Finally, there are three different levels of play: beginner, intermediate, advanced, with grids of increasing size and more mines to find at the higher difficulties.
I went with the simplest approach I could think of to build the core features. I didn’t bother with the high score, the timer, or the dashboard at the top of the window. I just implemented a mine cell object, a controller object to handle global state and data, and put most of the logic in the cell object. There’s no actual grid data structure; the mines are simply laid out to create a grid, and each mine cell only needs to be aware of its surrounding eight cells, so that’s all they do. The checks for this don’t care if they’re edge or corner cases, either. They check all 8 positions where a cell could be, and check to see there is one; if so, it checks to see if it is empty or a mine, and if it’s a mine it counts the neighbor and adds it to its tally.
When the game starts, every cell is empty, but when you make your first move, the cell you picked is cleared, and the rest of the mine cells that comprise the field randomly determines whether it contains a mine or not, and the state of the minefield is thereby determined. Thereafter, when you click a cell, if you release the button while the mouse is still over the cell, you trigger the sweep script, which will reveal whether there was a mine or not; if not, the cell will reveal how many mines are in its neighboring cells. If you mouse outside of the cell after clicking on it, and then release the mouse button, the mine cell will not trigger. This allows you to think about the move you are about to make while the mouse button is down, and if you decide that you want to take the move back, you can.
If you click in an empty cell that has 0 neighbors with mines, something special happens. Since the game is telling you that all of the neighbors are safe to clear, it does this for you automatically, saving you the time it would take to clear each neighbor manually. To do this, we check when this condition exists, and if there are 0 neighbors for the current cell, we call the “sweep” function for the eight neighbors. This “sweep” function checks each of the 8 neighbors in the same way, so that if any of them also has zero neighbors with a mine, then all of those neighbors are cleared automatically as well. This results in potentially large areas of the filed being cleared by a single click, and is probably the most fun part of the game.
The game conveys all of its information to the user graphically, through sprites. I created one sprite resource, containing the different states for a mine cell: untouched, cleared, mine, flag, and for the number of neighbors containing a mine. Most of my time spent in development was taken up by drawing the sprites. I wanted to get the colors accurate to classic Minesweeper, and but I didn’t otherwise concern myself too much with exact pixel accuracy or font accuracy. Due to my rusty pixeling skills, it took me a bit before I figured out a good workflow that would enable me to create individual images that had consistently centered positioning and sizing. I ended up deciding on 32×32 pixels for my cell size. This enabled me to create attractive, classic-looking cell graphics.
I did the drawing work in paint.net, in a single 32×32 pixel image with one layer for each sub-image in my sprite. The bottom layer had the grey background and outlining needed to convey a sense of bevel and shadow to create the illusion that the un-cleared cells have a raised tile on them. Within these, I drew very rudimentary flag and mine images, and using the text tool I created labeled numbers for the neighbor counts, colored them accurately. Then I imported the graphic into GameMaker and ordered the sub-images in the sprite in such a way to make the coding as convenient as possible.
Coding took the least amount of time out of everything. It was all pretty easy. The only complexity about the code had to do with some order-of-execution details that I worked through at the game start, because I have each cell determining whether it has a mine in it sequentially, and then I have to have each mine check its neighbors for the presence of a mine, in order to count them. Since the counting can’t start until all the cells determine whether they contain a mine, the neighbor counts don’t take place on the first step of game execution.
I also took a lot of time debugging a logic error that I created for myself unwittingly when I tried to code the recursive checking for neighbors that takes place when the player sweeps through a cell having zero neighboring mine cells. This was tricky because I was using the sprite sub-image to contain state information, which made a lot of sense because the sub-images directly convey this information. But there was one case where there was a state ambiguity: when you left click on an un-checked cell, the sub-image briefly switches to the sub-image I used for the cleared cells, because this gives the appearance of the cell being clicked like a button. When the player releases the left mouse button is when the game checks for mines, and then in the event of a zero-neighbor cell, the recursive check that happens used the sprite sub-image to confirm the state of the cell before doing further checking. Because the neighbor cells aren’t clicked on, they don’t switch from the unchecked sub-image to the cleared sub-image, and thus fail the logic test to see if the sweep check should continue, and thus nothing would happen; the if statment would follow a false branch that would stop the recursion from happening.
I had to sleep on the problem but when I woke up I immediately knew what the problem was and what I needed to do to fix it, which is my absolute favorite way to debug my coding mistakes. The only reason this took me so long to debug was that I assumed the cause of the problem was elsewhere in the code, and I tried everything else I could think of first before giving up and going to bed. But then again, if I didn’t do that, I probably wouldn’t have woken up with the answer in my head.
So now I have a fully functional core minesweeper engine. I don’t have the dashboard, timer, high scores, or variable difficulty levels, but what I have will serve as a good base to add those features.
I enjoyed making it.