How to Build Minesweeper with JavaScript
mitchum.blog
mitchum.blog
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.
It is playable here: https://sweeper.games
No jQuery, just straight CSS/HTML/ES6
https://web.dev/proxx-announce
Disclosure: Work on same team as the PROXX people
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.
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'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.
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.
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.
Overall it is code that takes a lot of unnecessary detours.
In minesweeper mines should be generated after first open. You can't hit mine at first click it must empty or number.
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.
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?https://github.com/jonmellman/angular2-minesweeper
https://jonmellman.com/minesweeper/
Grimace
https://github.com/reed-jones/minesweeper_js
And to play it:
Emoji minesweeper: https://codepen.io/nojvek/full/KjLxdx
MineSweeper For Twitch (MSFT) [1]
----
* It is in Czech :(
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.
Such as?
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.
In case you want to play with that - https://codepen.io/onion2k/pen/NQrEKq
The jQuery equivalent for this is $('.foo').addClass('bar');
• 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');
});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.
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.
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.
They only teach modern, relevant technologies? How shocking.
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).
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.