Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
How to Build Minesweeper with JavaScript (mitchum.blog)
79 points by maynman on July 25, 2019 | hide | past | favorite | 59 comments


That first image really needs to be the game itself. I had such an instinct to click it as soon as I saw it and was sad when I noticed it was just an image. Overall the article is nice, if a bit dated, but at least it's a change from a new reactive library.


It looks like they may have taken your advice. I came across this thread 9 hours after you, and the image was of a game in progress


Yeah it was like that even before! I want to actually play the game! Not just see an image :)


Good call! I agree, I think that would be better. Unfortunately the WordPress theme I'm using wouldn't let me do that by default. I'll have to do some research to see if there is a good plugin for that :)


Thanks for considering it already! I don't really have any WP knowledge but maybe you could just slap it in an iframe?


I was working on a multiplayer minesweeper game in elixir and react a couple years back to try and get a better understanding of otp.

https://mines.gdf3.com https://github.com/slofurno/minesweepers-ex https://github.com/slofurno/minesweepers-front

Wound up spending most of my time focusing on performance. Large boards (say 4000x4000) naively represented by a map in elixir consumed too much memory and trying to render 16 million dom elements in react didn't fly.

Never got around to basic ui stuff like setting player name and color, but I thought the bots were pretty cool.


An MMO Minesweeper Royale.... Not a bad idea actually! This definitely inspires me to make my own!


I'm sure there's only a few ways to do it, but this code is really similar to the one I wrote back in December: https://github.com/mikehodgson/minesweeper

It is playable here: https://sweeper.games

No jQuery, just straight CSS/HTML/ES6


This playable version seems to be missing the feature of auto-revealing the squares, usually a ctrl-click on a square to compare against placed flags. It's actually rule 5 from the linked page


That feature is implementation specific, not a rule of the game. I've never used it, so I didn't bother implementing it.


I just tried playing! You have a bug, there is no way to unmark a cell, so, when I did one incorrectly, I couldn't click on it :(


Thanks for the feedback, just pushed a fix for it! :)


Google Chrome Labs released a very shiny typescript minesweeper on https://proxx.app The source code is on Github https://github.com/GoogleChromeLabs/proxx


Here’s a technical overview on it. Most interesting thing for me was how they made it accessible and feature phone support.

https://web.dev/proxx-announce

Disclosure: Work on same team as the PROXX people


Thank you for open sourcing this. Across Googles github accounts there are some amazing showcases that offer an invaluable source of inspiration. The web.dev site is awesome! I actually came across proxx after I forked the website. Eleventy seems to be a robust static site framework with nifty template options.

I was a bit disappointed the actual dynamic part of the website was removed a couple of months ago during migration to eleventy. The express server, url fetching of lighthouse results & the option to save this in Firebase. For me this is the most interesting part of the website. How to setup a fetching api & Cloud Firestore with identity features. It would be awesome if devsite.js would be included for the people that want to take a peak under the hood.


Thanks for sharing it here. I saw Proxx on /r/WebGames last week, but didn't know it's open source.


excellent post.

can i make a (hopefully useful) comment about programming style - something that someone shared with me a long time ago when reading my code that i have found to be very valuable over the years?

it can be incredibly beneficial (for readability, catching logic errors, etc.) to "exit early" from "if" statements. meaning, if you find that you're nesting "ifs" more than a couple of levels deep, the code may be a candidate for flattening.

so - your handleClick function could be rewritten (with stuff removed) as:

    var handleClick = function( id )
    {
        if ( gameOver ) return;
        if ( ctrlIsPressed )
        {
            // do stuff...
            return;
        }
        if ( cell.opened || cell.flagged ) return;
        if ( cell.mined )
        {
            // do stuff...
            return;
        }
        // else do stuff...
        if ( cell.neighborMineCount > 0 )
        {
            // ...
            return;
        }

        // else do final stuff...
    }
i may have missed something, but hopefully you get the point. this simple refactoring reduced the depth of the if statements from ~5 to 1. ...many of the other functions could be flattened just like this.

...and how do you know when something can be flattened?

if there is no code after the if statement and the end of the function - just swap the logic and return early.

e.g., this:

    var handleClick = function( id )
    {
        if ( !gameOver )
        {
            // ...lots of code and if statements...
        }
        // ...but no code after the block before returning from the function...
    }
...turns into this:

    var handleClick = function( id )
    {
        if ( gameOver ) return; // NOTE: logic check change...
        // ...do the stuff in the block here...
    }
...and this is also a great pattern for checking input variables (and returning or throwing an exception) at the top of the function, ensuring that the code following it has valid input parameters.

since you're sharing your coding projects on your blog (which are excellent) - hopefully you can share this tidbit about coding style with your readers and they'd find it as useful as i have.


I'm a big fan of early returns; keeping cyclomatic complexity down can help readability. Idiomatic Go captures this too.


Thanks for the kind words, and thanks for sharing! That is a cool pattern. I definitely have a little too much nesting going on in that function, and it could use some refactoring.

I've always felt funny about multiple return statements though, which is probably one reason why a pattern like this doesn't usually come to my mind when considering how to refactor code. I've always liked having one exit point at the end of functions because programmers naturally expect a function to return at the very end. I think minimizing the number of return statements is generally a good thing, but perhaps I have been too strict about applying that rule.


"programmers naturally expect a function to return at the very end"

I disagree. I expect a function to return at the moment the final result is known. The decisions and logic are over. I shouldn't have to glaze over even a single extra line of code, because the work has already been done.


That's a fair point. I guess the main argument in favor of single return statements in modern programming languages is ease of debugging and logging. Like most things in programming, you have to consider the trade-offs. A few return statements isn't the end of the world, but after too many you might just want to make some more functions.


Here is my implementation from 2014 (with jquery...). Watch out. It has lots of bugs!

http://www.ronilan.com/bugsweeper/


Awesome job implementing a single-click clearing when clicking on, say, a 2 with 2 marked mines next to it. The original Windows version where you had to double click was too cumbersome.

https://github.com/AlanCoding/AlanCoding.github.io/blob/mast...

This was my version. I need to go re-host it somewhere, but you would not be much surprised by its functionality. My only unique feature was a wider variety of expressions of the smiley face button.


The game board is a well ordered grid, I can't fathom why you'd not organise it into an array. The cell object has 3 completely redundant fields. Most of the code just checks the mined parameter of cell objects, but for some reason there is also an isMined function. The code placing mines uses its own data structure to keep track of where mines have been placed, rather than the board data, and manages to be O(n^2) instead of O(n) because of that choice.

Overall it is code that takes a lot of unnecessary detours.



Well. it is good tutorial but there is a little problem.

In minesweeper mines should be generated after first open. You can't hit mine at first click it must empty or number.


Is this true? I remember plenty of times unsuccessfully guessing the first block to be a mine. Maybe I'm remembering incorrectly?


The version bundled with Windows has always had a first click check to avoid mines, if I recall, but a lot of clones don't. I remember back in my AOL days in the 1990s it was very much a sign of a bad clone and an immediate uninstall, though I don't recall why I went through so many clones at the time. (I think I was looking for more challenge having got tired of even "Expert Mode"? I was a strange kid.) (The best clones not only did a first click was safe build out, but also a solvability check that the first click opened enough hints that every puzzle was theoretically solvable on given hints alone.)

The version included in Microsoft Entertainment Pack 1 prior to Minesweeper being bundled with Windows might not have had that check, my memory is fuzzy on that. I'd be surprised how many people remember it from the Entertainment packs before Windows 3.1.

The clone that I recall most Mac users had in 90s, such as was on school computers at the time, definitely did not have a first click fix up.


Back in ancient windows, I definitely picked poorly and died on my first click multiple times. However, it is an interesting optimization.


This was a lot of work and you should be really proud.

One suggestion which could simplify things. I noticed this bit of code:

  var getNumberColor = function( number )
  {
      var color = 'black';
      if (number === 1)
      {
          color = 'blue';
      }
      if (number === 2)
      {
          color = 'green';
      }
      // etc        
  }
You mention using a switch statement, but why not just use an array and look it up by the index?


And why not just return the result instead of assigning it to something you eventually return?


The initial assignment is used as a base case. Returns in a conditional statement are often used as an "escape" when you want to get out of a function (e.g. null checks). This is purely a stylistic choice whereas using constant time lookup over conditional evaluation could be argued to have some performance impact (although in this case it would not be meaningful). That said, I don't see anything about this choice that needs to be defended.


The argument against this is usually one return statement is preferred.


Yep, that's why I chose to implement it that way. I generally prefer single return statements. Plus my day job enforces it as part of the coding convention, so force of habit too haha.


Uh oh. Since folks are posting their own implementations, here’s mine, written as an exploratory exercise for Angular2:

https://github.com/jonmellman/angular2-minesweeper

https://jonmellman.com/minesweeper/

Grimace


I guess this is a good time to share my implementation too. Unfortunately it's pretty much desktop only, but includes a high scores board!

https://github.com/reed-jones/minesweeper_js

And to play it:

https://minesweeper.zone/


I built a copy of Minesweeper using AngularJS for fun that may be instructive.

Demo: http://ceasarbautista.com/minesweeper/

Source: https://github.com/Ceasar/minesweeper


One weekend I was bored and made a React version of minesweeper. Needless to say I had a lot of fun.

Emoji minesweeper: https://codepen.io/nojvek/full/KjLxdx


MSFT, our multiplayer Twitch Plays implementation of minesweeper, playable directly with your mouse (left click to dig, middle click to flag):

MineSweeper For Twitch (MSFT) [1]

----

[1] https://www.twitch.tv/bzh314


cool tutorial, love all of the code examples. Some of that code is brutally unreadable with all of the nested statements and loops. I guess it works for the tutorial but ooof!


My friend (Math genius) made a video tutorial about writing Minesweeper in JS in a Notepad https://www.youtube.com/watch?v=OuahlosQ1m0

* It is in Czech :(


Aren't people afraid of being kicked and beaten with sticks in 2019 if they build things with jQuery.


It's a shame jQuery gets demonised so much, it's still a great tool to use even in 2019. Don't use it for the wrong job (web apps) and there's nothing wrong with it. It's actually strange you can meet people who learned something like React/Angular as an intro JavaScript and don't know how to do simple things outside of a framework without a tutorial.


Modern Javascript standards have incorporated a lot of things that have started their life in jQuery. What advantages does jQuery still have?


They've incorporated everything other than the convenience and succinctness of jQuery. And that's fine, they have different goals to jQuery, standards should be more explicit in what functions do and how they behave etc. Maybe it's a good thing I can't do $('something').eq(0).addClass('foo') in ES without first checking the return type of document.querySelector before attempting to add a class to it, but it is convenient.

Looking at stuff like http://youmightnotneedjquery.com/ it's easy to see why there are lots of cases where jQuery just isn't needed anymore. But 10% of them still look very verbose without jQuery and when I'm working on a simple website the convenience of jQuery is worth it to me so I continue to use it where it helps.


Sure, when you need to update 1 single thing once, you can use vanilla js.

But when you need to do more than that, you'll realize jQuery is still useful (assuming you don't use a framework or whatever). Either you will use something like jQuery or you'll reinvent by writing the methods yourself.

IMO it's still relevant, despite the hype of other frameworks.


There's really nothing you need jQuery for any more except supporting old jQuery components. You can do things like Array.from(document.querySelectorAll('.foo')).map((fooEl)=>{ fooEl.classList.toggle('bar'); }) in a browser (with no Babel-style transpile step) these days.

In case you want to play with that - https://codepen.io/onion2k/pen/NQrEKq


Some notes:

• Array.from is comparatively recent to the web platform, and cuts out IE support.

• Learn when to use Array.prototype.forEach, and when to use Array.prototype.map: use forEach when you aren’t deliberately constructing a new array but are just wanting to call a function on each value, and map when you are applying a function to each value and care about the result. Otherwise you’re creating a new array unnecessarily.

• You can (and should) avoid Array.from when you just want to call an array method on an array-like object (e.g. NodeList, HTMLCollection, Arguments): instead, call the method directly on the instance; it’s OK, all the Array.prototype methods are deliberately designed to work on array-like objects, not just arrays. That is, instead of `Array.from(x).forEach(y)`, use `Array.prototype.forEach.call(x, y)`. Otherwise you’re creating a new array unnecessarily.

• A slight word of caution about classList: IE and very old browsers don’t implement it on SVG elements. Just take that into account if you support IE at all.

So then, you might end up with this:

  Array.prototype.forEach.call(document.querySelectorAll('.foo'), (fooEl) => {
      fooEl.classList.toggle('bar');
  });
Or if you hate the `Array.prototype.` and `.call` bits,

  const forEach = Function.prototype.call.bind(Array.prototype.forEach);

  forEach(document.querySelectorAll('.foo'), (fooEl) => {
      fooEl.classList.toggle('bar');
  });
And if you’re happy to drop a little more older browser support including IE, you can just use NodeList.prototype.forEach, which incidentally is normally the same function object as Array.prototype.forEach:

  document.querySelectorAll('.foo').forEach((fooEl) => {
      fooEl.classList.toggle('bar');
  });


I should have expected a reply like this. Those are all good points.


Although I usually agree with the 'jQuery is overused' side of the crowd, I think your example is one of the times when I see that jQuery wins in ease and readability.

The jQuery equivalent for this is $('.foo').addClass('bar');


I really dislike the way jQuery makes no distinction between mutations on one or many elements. I view the native APIs’ differentiation of the two, requring you to be explicit (`document.querySelector('.foo').classList.add('bar')` versus `document.querySelectorAll('.foo').forEach(foo => foo.classList.add('bar'))`) to be a feature.


My version doesn't need a 30Kb library though.


> But when you need to do more than that, you'll realize jQuery is still useful

Such as?


See here: https://medium.com/@mattburgess/in-defence-of-jquery-4a8b20f...

In general, jQuery is an amazing wrapper around native JS with a saner API and countless hours of different browser quirk workarounds to make sure that what you try to do works in all modern browsers and IE.


Browser compatibility.

Ajax calls are one example. JQuery ajax works everywhere, while fetch() will never work in IE without polyfils.

Not to mention all the other ES6 features IE refuses to support. Not every project wants or needs a webpack/ babel build process.


Unless you are doing things like modifying a Google-style speadsheet of 5000+ columns and rows, you won't see much of a performance loss from using jQuery.

It does add a microscopic bit of weight to your page and some milliseconds to your load time, but that's it. It's a ressource that is smaller and faster to load than the content of the page. In my opinion, this argument is meaningless. Modern SPA frameworks are slowing the web way more. Loading your jQuery scripts async will make this transparent for the user.

In the edge-cases where jQuery is slower than native JS (because of polyfills, mostly), there's nothing stopping you from using those directly.

Unless you are Netflix and micro-optimizing your homepage to the millisecond, there's no hard reason to avoid jQuery. I believe that those slight performance losses are worth the trade for the benefits of using a strong and well tested framework that has browser support and lowers development time. I'd trust non JavaScript experts to use jQuery on my projects, I can't say the same for raw JavaScript.

Trend-driven development is a pitfall. Boring technologies are where it's at.


I still use it for most websites I build. Ruby + jquery are still great!

I’ve inherited a ton of backbone, angular, react projects and it’s always a pain sourcing devs for whatever random thing a project was written in 1-8 years ago. Jquery is just jquery and you can put anyone on it.

EXCEPT I reached out to the lambda school contractor program for a work project and was pretty shocked they only do node+react.


> EXCEPT I reached out to the lambda school contractor program for a work project and was pretty shocked they only do node+react.

They only teach modern, relevant technologies? How shocking.


This is a bad take. Save your snark for somewhere else.

I didn’t say it was the wrong choice or even a bad choice, just that I was surprised. Ruby and jquery are certainly modern technologies.

My surprise was more a factor that maybe I had too high of an impression of LS (and I’m still a fan!). The blogs/tweets I’ve seen from afar made it seem more like a rigorous “be a working software engineer” curriculum. If their candidates are ONLY suited for a node/react stack that seems like a pretty dangerous/inflexible hire. TBF this was for folks who haven’t graduated yet, so maybe they go wider on their final few months. As a person who hires engineers, I don’t want someone who only knows one stack. I want smart people who can solve problems in any stack that gets thrown at them (even if they’re junior and I need to hold their hands a bit).




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: