MineFinder 3D
Drew Strickland on May 1st, 2016 | About 5 minutes to read.This article references a game I made, which you can play for free here.
The source for this game is publicly available on GitHub.
I got into software development because I wanted to make games. Games are software. Games are fun software, but at the end of the day, your average game is not fundamentally different from a reactive single-page web app.
That was nearly two decades ago.
This year I promised myself that I was going to launch at least one game. I was frustrated with the half-finished prototypes I didn't have time for, and the un-started games that only exists as notes in Google Keep; I simply wanted to feel like I could make a game and put it out there and make that happen.
Interviews & Inspiration
On Wednesday, April 27th, I was walking around downtown Denver, simply killing time before I went into an interview for a consulting contract. The interview wound up being a lot different from what I thought it would be. Most of the time, you walk into an interview with a software company, and they'll have you do FizzBuzz, and maybe some OOP with a dog that needs legs, or they hand you Pascal's Triangle and Dining Philosophers in one form or another and tell you to make it work in pseudo-code.
My interview was Minesweeper. On a whiteboard.
Minesweeper is a game I never really bothered with. Sure, I clicked on it a few times, maybe failed to play a handful of games, and I understood the rules of the game, but I never sat down and really thought about the game. I had simply never tried to pull it apart and make it work in my head.
For me, this was a welcome and refreshing change, mainly because I didn't have to re-hash the same 3 ways to do FizzBuzz. I was simply excited to be in an interview and be doing something I hadn't done before, or hadn't made someone else do before.
After the interview ended, I kept going over the code in my head, trying to find ways to improve it, make it better, actively building the game. By the time I got home, I couldn't think about anything else.
For lack of a better word, I was inspired.
Make it Work, Make it Right, Make it Fast
This has been my personal mantra ever since I heard these words stated as a "pick-two" proposition by another senior developer. I have never agreed that this is a "pick-two". Instead, I see this as the focus of each iteration on the code.
Make it work.
Then make it right.
Then make it fast.
It's a great way to work, and I know there are plenty of people who agree with me. I have listened to @lokradu speak, at length, about the dangers and pitfalls of premature optimization. I have competed in (and won) hackathons simply on the back of being able to make the money-features happen, regadless of how embarassingly ugly the code was.
This is a process that works.
When I sat down, ready to make my game, I started by making the board work in a theoretical, ethereal, not-at-all represented by an interface way. Put simply, I began by focusing solely on the game mechanic. I didn't even really have anything to click on.
From there I built out the basic interaction in an evented way, and then finally, built an interface that attached to these events. This is a methodology I have prescribed for applications in the past. It's powerful, and creates minimal cognitive dissonance by allowing you to frame the entire application as a series of "when, then" statements.
JavaScript Garbage Collection: The Paradox of Delete
When I finally got to the point where I had an interface that could start a new game, I quickly discovered that I had a giant memory leak. Where did I go wrong? I was calling delete on everything when I created a new one.
Fun fact: 'delete' in JavaScript doesn't do what it says on the tin. See, 'delete' sounds like it does flat-out garbage collection, almost in a strong arm way. 'Delete' is a word that promotes finality, and conjures images of an orbital laser satellite making small, precision bursts of thrust to reposition itself to point directly at the memory the object lives at, firing, and then nothing of that object remaining, except a few wisps of ash and an empty space where that object once was.
In reality, 'delete' simply de-references. In effect, it just sets the object to null. That's great if your object is only used in one spot, but the entire point of having the object is that it's used all over the place.
If you look at my code, you will note that some of the objects have a method called 'destroy'. Destroy's job is to finish the work that weak-ass delete couldn't handle. See, if an object becomes completely de-referenced (read that as "nothing points to it") then GC will pick it up and discard it. So, destroy simple sets everything on the object to null, which prevents it from pointing to anything, meaning anything it pointed at gets GC'd, and then the object itself get's GC'd in turn.
When Random Can't Be
One of the big problems with writing a test for something that is supposed to be random is that you cannot check the result. Since a series of random numbers shouldn't be predictable, you have no way to tell what that series is without looking at it, and no way to reproduce the series if you need it.
One solution, the one I chose, is to create a pseudo-random number generator which generates random numbers based on a predefined seed. This allows you to generate a two sequences of random numbers that both match as long as their seeds are the same.
Admittedly, my implementation of XorGen128 is based on someone else's, but this is a very important part of being able to generate mine locations in an unpredictable way, that can still be tested. In the game, I simply generate a random seed and use that to feed the PRNG a starting point. In the tests, you'll notice that the seed is a simple string which produces matching results where the keys match.
Final Thoughts
I really enjoyed making this game. I know it's not original, or groundbreaking, or maybe even fun for most people, but it provided me with a deep sense of accomplishment for finally doing something I had meant to do for what feels like forever.
In the future, I might add sound, and possibly mobile support (since right now the 'marking' interaction relies on right-click), but as it stands, I'm very proud of what I have made.