Skip to content

Instantly share code, notes, and snippets.

@Prajjwal
Last active December 12, 2016 21:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Prajjwal/9063b16f7124e7acf064fa43aecf4996 to your computer and use it in GitHub Desktop.
Save Prajjwal/9063b16f7124e7acf064fa43aecf4996 to your computer and use it in GitHub Desktop.
Canvas Minesweeper I wrote back in 2012.
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas Minesweeper</title>
<style type="text/css">
#container { width: 1250px; margin: 0 auto }
</style>
</head>
<body>
<script type="text/javascript" src="mines.js"></script>
<p>'n' starts a new game</p>
</body>
</html>
(function (undefined) {
var d = document,
canvas = d.body.appendChild(d.createElement('canvas')),
c = canvas.getContext('2d'),
mf,
openCount,
marked,
isOpen,
colors = ['yellow', '#00f', 'green', 'red', 'blue', 'magenta', 'red', 'red', 'red', 'black'];
// Set canvas height and width
canvas.height = 700;
canvas.width = 1250;
c.font = '20px serif';
addEventListener('contextmenu', function (e) { e.preventDefault(); });
addEventListener('keypress', handleKeypress, false);
function build2dArray(rows, cols) {
// Constructs a 2d array
var arr = [];
for (var i = 0; i < rows; i++)
arr[i] = [];
return arr;
}
function getRandomInt(x, y) {
// Return random int in the range [x, y]
return Math.floor(Math.random() * (y - x + 1)) + x;
}
function surroundingCells(row, col) {
// Return the coords of all cells surrounding a cell
// eg surroundingCells(0, 0) should return [[0, 1], [1, 0] [1, 1]]
return [[row, col - 1], [row, col + 1],
[row - 1, col - 1], [row - 1, col], [row - 1, col + 1],
[row + 1, col - 1], [row + 1, col], [row + 1, col + 1]]
.filter(function (coords) {
return (coords[0] >= 0 && coords[0] < 16) && (coords[1] >= 0 && coords[1] < 30)
});
}
function countSurroundingMines(row, col, x) {
var surrounding = surroundingCells(row, col),
surroundingCellCount = surrounding.length,
mines = 0;
for(var i = 0; i < surroundingCellCount; i++) {
if (x[surrounding[i][0]][surrounding[i][1]] === 9) {
mines++;
}
}
return mines;
}
function generateMinefield(rows, cols, mines, avoidRow, avoidCol) {
var x = build2dArray(rows, cols);
// Fill array with given number of mines
for(var i = 0; i < mines; i++) {
var col = getRandomInt(0, 29),
row = getRandomInt(0, 15);
if (x[row][col] || (row === avoidRow && col === avoidCol)) {
// Retry if it already is a mine or is the tile the user clicked on the first try
i--;
} else {
x[row][col] = 9;
}
}
// Fill in rest of array
for(var i = 0; i < rows; i++) {
for(var j = 0; j < cols; j++) {
if (x[i][j] !== 9) {
x[i][j] = countSurroundingMines(i, j, x);
}
}
}
return x;
}
function drawMinefield() {
// Draws minefield of a given size on the canvas
var left = 20,
top = 20;
// Original Color of Tiles
c.fillStyle = 'red';
for (var i = 0; i < 16; i++) {
for (var j = 0; j < 30; j++) {
c.fillRect(left, top, 40, 40);
left += 41;
}
left = 20;
top += 41;
}
}
function openTile(row, col) {
// Clear Tile
var x = col * 41 + 20,
y = row * 41 + 20;
// Draw underlying number
if (!marked[row][col] && !isOpen[row][col]) {
c.clearRect(x, y, 40, 40);
if (mf[row][col] === 9) {
// If it is a mine
gameOver(false);
} else {
isOpen[row][col] = 1;
c.fillStyle = colors[mf[row][col]];
if (mf[row][col] === 0)
openMultipleTiles(surroundingCells(row, col));
else
c.fillText(mf[row][col].toString(), col * 41 + 33, row * 41 + 48);
openCount++;
if(openCount === (16 * 30) - 99) { gameOver(true) }
}
}
}
function openMultipleTiles(tiles) {
for(var i = 0; i < tiles.length; i++) {
openTile(tiles[i][0], tiles[i][1]);
}
}
function markTile(row, col) {
if (isOpen[row][col] === undefined) {
if (marked[row][col]) {
// Unmark if already marked
c.fillStyle = 'red';
marked[row][col] = 0;
} else {
// Mark
c.fillStyle = 'blue';
marked[row][col] = 1;
}
c.fillRect(col * 41 + 20, row * 41 + 20, 40, 40);
}
}
function showAllMines() {
for(var i = 0; i < 16; i++) {
for(var j = 0; j < 30; j++) {
if (!isOpen[i][j] && mf[i][j] === 9) {
c.fillStyle = colors[mf[i][j]];
c.fillRect(j * 41 + 20, i * 41 + 20, 40, 40);
}
}
}
}
function firstClick(e) {
canvas.removeEventListener('mouseup', firstClick, true);
canvas.addEventListener('mouseup', handleClick, true);
mf = generateMinefield(16, 30, 99, Math.floor((e.layerY - 30) / 41), Math.floor((e.layerX - 30) / 41))
handleClick(e);
}
function handleClick(e) {
/* Decides what to do when tile is clicked */
var col = Math.floor((e.layerX - 30) / 41);
var row = Math.floor((e.layerY - 30) / 41);
if ((e.buttons === 1 || e.button === 0)) {
// Left Click
openTile(row, col);
} else {
// Any other click is to be treated as a right click
markTile(row, col);
}
return false;
}
function handleKeypress(e) {
if(e.charCode === 110) {
newGame();
}
}
function gameOver(won) {
showAllMines();
canvas.removeEventListener('mouseup', handleClick, true);
}
function newGame() {
canvas.addEventListener('mouseup', firstClick, true);
marked = build2dArray(16, 30);
isOpen = build2dArray(16, 30);
openCount = 0;
drawMinefield();
}
newGame();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment