30
\$\begingroup\$

As Stack Snippets are live on Code Review now, I wanted to do something quick in it as part of rolfl's challenge on meta. And when you make things quick, they probably get dirty. And when things get dirty, you refactor. And when you don't know how to refactor, you post it up for review.

So, as many of my previous questions has been about Minesweeper probabilities, what better thing to make than a Minesweeper game?

This minesweeper game does not have any probability-calculations or other cool stuff though, that will have to wait until version 2.0 :) It's not the multiplayer variant Minesweeper Flags either. Just plain old Minesweeper. I decided to go with the 'Intermediate' setting, i.e. 16 x 16 with 40 mines.

It has been a while since I did any real JS coding, but I don't consider myself a beginner in it.

By the way, don't be afraid if it says 'Kaboom' on your first click. It's a bug feature, not a feature bug.

Stack-Snippet with JS + HTML: (a slightly modified version can be found on github)

var elements = [];
var fields = [];
var allFields = [];

function showField(thisButton) {
  var thisX = Number(thisButton.attr('data-x'));
  var thisY = Number(thisButton.attr('data-y'));
  var button = fields[thisY][thisX];
  var number = Number(button.attr('data-value'));
  thisButton.attr('data-visible', true);
  if (button.attr('data-mine') === 'true') {
    button.text('X');
    alert('KABOOM!');
    allFields.forEach(function(entry) {
      entry.attr('disabled', true);
    });
  }
  else if (number === 0) {
    button.text(number);
    for (var xx = thisX - 1; xx <= thisX + 1; xx++) {
      for (var yy = thisY - 1; yy <= thisY + 1; yy++) {
        if (fields[yy] !== undefined && fields[yy][xx] !== undefined) {
          var innerButton = fields[yy][xx];
          if (innerButton.attr('data-visible') === 'false') {
            showField(innerButton);
          }
        }
      }
    }        
  }
  else {
    button.text(number);
  }
}

var table = $('#board').find('tbody');
for (var y = 0; y < 16; y++) {
  var tr = $('<tr>');
  var row = [];
  for (var x = 0; x < 16; x++) {
    var td = $('<td>');
    var button = $('<button>').text('_').attr('data-value', 0)
     .attr('data-x', x).attr('data-y', y).attr('data-visible', false)
     .attr('data-mine', false)
     .on('click', function() {
       showField($(this));
    });
    td.append(button);
    tr.append(td);
    row.push(button);
    elements.push(button);
    allFields.push(button);
  }
  fields.push(row);
  table.append(tr);
}

// Generate mines
for (var mines = 0; mines < 40; mines++) {
  var random = Math.floor(Math.random() * elements.length);
  var field = elements[random];
  var thisX = Number(field.attr('data-x'));
  var thisY = Number(field.attr('data-y'));
  field.attr('data-mine', true);
  for (var xx = thisX - 1; xx <= thisX + 1; xx++) {
    for (var yy = thisY - 1; yy <= thisY + 1; yy++) {
      if (fields[yy] !== undefined && fields[yy][xx] !== undefined) {
        var innerButton = fields[yy][xx];
        var previous = Number(innerButton.attr('data-value'));
        innerButton.attr('data-value', previous + 1);
      }
    }
  }        
  elements.splice(random, 1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id="board"><tbody></tbody></table>

Primary concerns:

  • Is it a good idea to use data-* attributes to store the data? It kinda makes it quite easy to cheat...
  • Am I using the features of JQuery in a good way?
  • Any other comments welcome! Don't be afraid to hurt my feelings, I'm more of a Java guy anyway...
\$\endgroup\$
2
  • \$\begingroup\$ I can't flag mines? \$\endgroup\$
    – Malachi
    Commented Sep 18, 2014 at 19:16
  • 4
    \$\begingroup\$ @Malachi No, you can't flag mines. But if you find a nice way to implement it that would be a good thing to add in your fork ;) \$\endgroup\$ Commented Sep 18, 2014 at 21:01

1 Answer 1

20
\$\begingroup\$
thisButton.attr('data-x')

You can use jQuery's .data(). It uses an internal cache for storing key-value pairs, but if it doesn't find it there, it looks up the data-* attributes of the associated element. This means you can do thisButton.data('x'). Note that dash-space becomes camelCase.


Number(thisButton.attr('data-x'))

If you use jQuery's .data(), it automatically converts the type for you. If it finds "2", then it converts it to a number.

Also, you can use the unary + to convert strings to numbers. It's close to parseFloat(). Something like:

+"2" === 2

Is it a good idea to use data-* attributes to store the data?

For a regular audience, maybe. For advanced users, you might want to do something else (unless they're just bored and want to play :P)

  • Use jQuery .data(). The data is stored in an internal cache rather than written as a data-* attribute. However, if you have reference to the element and call .data() against it, it could reveal the value. Also, afaik, the cache is accessible via jQuery._cache.

  • Use an internal mapping, and have the buttons only carry an index/coordinates. You can create a 2x2 array inside a closure which only your code can access. On click, you can announce the coordinates freely.

Also, any determined dev/skid can peek through using dev tools, so the best you can do is just make it harder to pick on.


Am I using the features of JQuery in a good way?

Nothing wrong with it :D

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.