rss
hexsweeper - a retrospective
Hexsweeper is my reinterpretation of the Microsoft classic, "Minesweeper", inspired by the show-don't-tell teaching of The Witness. It's a puzzle game about finding bombs with sparse information, and I think it's both elegant and confusing, so I'd like to share a few of my thoughts.

History

I think I was midway through playing through "The Witness" when I decided that I'd been inspired enough to make a `good` game. I wasn't expecting to emulate a 3D puzzle game that encapsulates the beauty of exploration and the depth of simplicity, but I thought I could try to enact a few of the lessons it taught.
Foremost among these lessons, was the power of player-directed learning. In The Witness, the player slowly learns to complete esoteric puzzles, without ever having their hand held. On thinking a little further, I came up with a few design goals to help guide me.

Design Goals

  • language agnostic - even aliens should be able to play this game
  • clear interface - all mechanics should be apparent from a glance
  • intuitive interactions - players should never struggle with the controls
  • global accessibility - everyone should be able to play
  • hexagons - there should be hexagons
The first three goals fall out of my desire for elegance. The second-to-last really pushed me towards making a browser game, which was lucky, because I'd had a bit of experience with javascript and HTML5. As for the last goal, I just think hexagons are cool.

Remaking Minesweeper

There are three key elements to a game of Minesweeper.
The first, is a grid containing hidden mines (click on these and you lose). I settled on a hexagonal grid because I like hexagons and don't you tell me they aren't nice. This means we have 6 nearest neighbours, which changes Hexsweeper from a 100% clone of Minesweeper to a 75% clone. There are some interesting design implications for this; it means that you can clear the board a lot quicker than regular Minesweeper. I'm not sure if this makes it easier or more difficult. Personally, I found it easier, but players have told me that they found it harder, so who knows.
The second element of Minesweeper, is the information that is shown when you click on a grid element that isn't a mine, telling you how many of it's neighbours have mines. In Minesweeper, this manifests as a number between 0 and 8 (the number of next-nearest neighbours available to a square). With my goal to be language agnostic, I couldn't emulate this. However, I had the handy aspect of having 0-6 mines surrounding any given grid element. It felt evident that I should just use some sort of a hexagon-slice to show the number of neighbours. I ended up using a doughnut-like hexagon, and I think it turned out quite nice looking.
Finally, the third Minsweeper element is the "bomb remaining" count, which is a godsend in some ambigious situations. At this point, I really felt like the correct solution was to display this number using hexagon slices, and I went for nesting hexagon outlines to indicate the count. Each white line, a bomb unfound, each red, a bomb thought discovered.

Hexagonal Grids

They're neither as simple, nor as hard as you might expect. When you're thinking about moving on a hexagonal grid, you just say to yourself "go up", "go left" or perhaps "go diagonally". However, monitors (and therefore Canvas) excel at drawing square grids, so there is no "go diagonal". Furthermore, the distance you need to move up in a hexagonal grid is not the same as the distance you need to move left, so the entire unitary system is complicated.
Luckily, there is a solution. If we think about the hexagonal coordinates as (j,k,l) where j is up, k is right and l is diagonal, we actually find that we can describe the "right" motion as a combination of "up" and "diagonal". E.g. moving right is equivalent to moving "diagonally up and right" and "reverse up" (a.k.a down).In unitary terms, I mean (0,1,0)=(-1,0,1). In mathematics, this is described as a constrained axis, meaning our hexagonal grid has two degrees of freedom. Much like our monitor units (x,y). Thus, with a relatively simple unitary conversion, we can convert hexagonal positions to monitor positions, and suddenly we know where to draw our hexagons.
The only setback is converting mouse position to hexagonal position (for hover-over effects and, uh, clicking on stuff). This requires a little bit more work, but overall it's not too bad.

Efficiency and Scaling

Canvas is not efficient and I, initially, did not use it efficiently. I start off drawing the hexagonal grid (upon which we play), by drawing lines the lines of a hexagon around each point. Then I fill a hexagon based on the information known to the player (is it marked as a mine, is it marked as not a mine). Next, for each spot that is known not to be a mine, I draw the neighbouring mine count. Finally, depending on mouse position (and if a click is held or not), I draw some effects around the relevant grid position. Sadly, in a 3hex grid (radius of 3), this means in each frame, we draw lines 114 lines, and do an unspecified number of fills. Line drawing requires lots of non-linear (ironically) memory accesses, which take up precious time.
Smartening up a bit, we can pre-render the hexagons, and copy a block of canvas over and over. This is considerably faster (semi-linear memory access, see?), and allows us to draw the grid AND marked elements much faster. However, for a 3hex grid, we're still doing 19 relatively large block memory copies each frame. For larger grids (e.g. 20hex), we're looking at 1141 copies, which is terrible scaling.
Finally, we can really smarten up. There is no need to draw the background each frame, and there's no reason to draw the information each frame. This stuff can be saved, and recalled, and we only need to update the mouse position each frame, and perhaps new information when a click has been registered. With this, we have an initial frame that takes a long time (drawing and storing the background), and then for each subsequent frame we only have to do one very large, very linear memory copy. Which is a dream for a memory access.

Successes and Failures

  • Success - looks elegant
  • Failure - apparently gameplay is not intuitive to people who don't play games - an unforeseen issue!
  • Success - puzzles scale well
  • Failure - canvas doesn't though, we can't zoom without seeing pixelated hexagons.
  • Success - marking a position as not a bomb is intuitive
  • Failure - not everyone understood how flag a grid element a a bomb
  • Success - lots of hexagons
  • Failure - bomb count was too many hexagons and nobody knew what it was

Conclusion

I like this game, and I'm pretty happy with it as a first game and a first pass. In the future, I'd like to remake this using a WebGL (for efficiency), or perhaps WebGL/Unity/Unreal for a 3D version. I'd also consider expanding the remit of Hexsweeper, perhaps exploring different definitions of mines; exploring the puzzle mechanic entirely.