Skip to content

Instantly share code, notes, and snippets.

@inDream
Last active July 24, 2017 07:59
Show Gist options
  • Save inDream/c622bb3ff5d36cfd832296278d82a945 to your computer and use it in GitHub Desktop.
Save inDream/c622bb3ff5d36cfd832296278d82a945 to your computer and use it in GitHub Desktop.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style>
body {
font-family: "Segoe UI", Helvetica, Arial, sans-serif;
text-align: center;
}
input[type="number"] {
width: 50px;
}
#container {
margin: 0 auto;
line-height: 0;
}
#container div {
height: 30px;
width: 30px;
background: #ccc;
display: inline-block;
border: 1px solid #000;
}
#container div:after {
position: absolute;
font-size: 25px;
margin: 14px -12px;
}
.covered {
background: #888 !important;
}
.uncovered:after {
margin: 14px -6px !important;
content: attr(data-content);
}
.flag:after {
content: '🚩';
}
.question:after {
content: '❓';
}
.mine:after {
content: '💣';
}
.incorrect:after {
content: '❌';
}
</style>
</head>
<body>
<h1>Minesweeper</h1>
<br><br><br>
<label>Mines: <input id="minesVal" type="number" value="10" /></label>
<label>Rows: <input id="rowsVal" type="number" value="8" /></label>
<label>Columns: <input id="columnsVal" type="number" value="8" /></label>
<pre id="message" onclick="init()"></pre>
<div id="container" oncontextmenu="return false;"></div>
<script>
var width = 30;
var border = 1;
var mines, rows, columns, remaining, revealed;
var tile = [];
var board = [];
// this function returns the value of the tile using the (x1,y1) coordinates. (0,0) is in the upper left corner.
function check(x1, y1) {
if ((x1 >= 0) && (y1 >= 0) && (x1 < columns) && (y1 < rows)) //Verify if coordinates do not fall outside of the board.
return board[x1 + y1 * columns];
}
// This function returns the classname of the tile (uncovered/flag/question mark).
function getType(index) {
return tile[index].className;
}
var dir = [[0, 1], [-1, 1], [1, 1], [0, -1], [-1, -1], [1, -1], [-1, 0], [1, 0]];
function loop(x, y, cb) {
var acc = 0;
dir.forEach(function(e, i) {
var v = check(x + e[0], y + e[1]);
if (v !== undefined) {
acc += cb(v, x + e[0], y + e[1]);
}
});
return acc;
}
function init() {
message.textContent = 'Click on the tiles to reveal them\nClick here to reset';
// Set the number of mines and the size of the board.
mines = minesVal.value;
rows = rowsVal.value;
columns = columnsVal.value;
remaining = mines;
revealed = 0; // The number of revealed tiles.
board = []; // Reset mine in previous round
// Create the tiles.
container.style.width = columns * (width + border * 2) + 'px';
var fragment = document.createDocumentFragment();
for (var i = 0; i < rows * columns; i++) {
tile[i] = document.createElement('div');
tile[i].className = 'covered';
tile[i].addEventListener('mousedown', click); // Function 'click' will be executed when player clicks on a tile.
tile[i].id = i; // The id of the tile is its index.
fragment.appendChild(tile[i]); // Add the tile to the DOM.
}
container.innerHTML = '';
container.appendChild(fragment);
// Place the mines:
var placed = 0;
while (placed < mines) {
i = Math.floor(Math.random() * columns * rows); // Select a random tile.
// Make sure the tile doesn't already have a mine.
if (board[i] != 'mine') {
board[i] = 'mine'; // Set the mine
placed++; // and increase the count.
}
}
for (var x = 0; x < columns; x++) {
for (var y = 0; y < rows + 1; y++) {
// if the cell is not a mine:
if (check(x, y) != 'mine') {
board[x + y * columns] = loop(x, y, function(v) {
return +(v === 'mine');
});
}
}
}
}
function handleLeftClick(id, tileType) {
// if the tile is a mine:
if (board[id] == 'mine') {
for (var i = 0; i < rows * columns; i++) {
if (board[i] == 'mine') tile[i].className = 'mine'; // show all the mines,
if (board[i] != 'mine' && getType(i) == 'flag') tile[i].className = 'incorrect'; // show a strike-through mine where flags were placed incorrectly.
}
message.textContent = 'GAME OVER\nClick here to restart';
} else if (tileType == 'covered') {
// otherwise reveal the tile.
reveal(id);
}
}
function handleRightClick(id, tileType) {
switch (tileType) {
case 'covered': // If the tile is uncovered, set a flag.
if (remaining) {
tile[id].className = 'flag';
remaining--;
}
break;
case 'flag': // If it's a flag, set a question mark.
tile[id].className = 'question';
remaining++;
break;
case 'question': // If it's a question mark, set it to uncovered.
tile[id].className = 'covered';
break;
}
// Update the count of remaining mines.
message.textContent = "Mines remaining: " + remaining;
}
function click(event) {
var id = event.target.id; // The ID of the tile clicked by user.
var tileType = getType(id);
if (event.which == 1) { // On left click:
handleLeftClick(id, tileType);
} else if (event.which == 3) { // On right click:
event.preventDefault();
handleRightClick(id, tileType);
}
// If all tiles revealed:
if (revealed == rows * columns - mines) {
message.textContent = 'YOU WIN!\nClick here to restart';
}
}
// Uncover the tile
function reveal(index) {
if (tile[index].dataset.revealed) {
return;
}
// If it's covered and not a mine:
if (board[index] != 'mine' && getType(index) == 'covered') {
revealed++; // If it was uncovered, increase the count of revealed tiles.
}
tile[index].className = 'uncovered';
tile[index].dataset.revealed = 1;
tile[index].dataset.content = board[index] || ''; // Uncover the tile.
var x = index % columns; // Convert index into (x,y) coordinates.
var y = Math.floor(index / columns);
// Reveal all the neighboring tiles:
loop(x, y, function(v, x, y) {
if (v === 0) {
loop(x, y, function(v, x, y) {
reveal(x + y * columns);
});
} else if (v === 1) {
reveal(x + y * columns);
}
});
}
init();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment