Upload Source
This commit is contained in:
parent
c059159711
commit
e04ff053b9
|
@ -0,0 +1,148 @@
|
||||||
|
//Felix Albrigtsen 2019
|
||||||
|
//Auto logic for minesweeper, not required for actual gameplay.
|
||||||
|
|
||||||
|
var botTimer = undefined;
|
||||||
|
|
||||||
|
function autoMove() {
|
||||||
|
//Automatically chose the statistically best move to do. The machine only has the same info as the player.
|
||||||
|
//When it knows nothing: Select by random.
|
||||||
|
//For each untouched cell(not marked and not revealed), generate the probability of the cell being a bomb.
|
||||||
|
//For a cell with 100% chance of being a bomb, instantly mark it.
|
||||||
|
//For a cell with 0% chance of being a bomb, reveal it.
|
||||||
|
//If no cells have 100% or 0% chance, reveal the one with the highest probability
|
||||||
|
//The probability of a cell is calculated by taking the average of each revealed neighbor cell's (neighbor mines - marked neighbors).
|
||||||
|
// Unless one or more of its neighbors gives it a 0% or 100%.
|
||||||
|
|
||||||
|
timer_hasCheated = true;
|
||||||
|
|
||||||
|
if (finished) { clearInterval(botTimer); return; }
|
||||||
|
var probabilityBoard = [];
|
||||||
|
|
||||||
|
for(var index = 0; index < board.length; index++) {
|
||||||
|
probabilityBoard.push(calculateProbability(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
var lowestChance = [1, -1]; // [value, index]
|
||||||
|
|
||||||
|
for (var i = 0; i < board.length; i++) {
|
||||||
|
if (probabilityBoard[i] == 1) {
|
||||||
|
interact(i%cols, (i-(i%cols))/cols, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (probabilityBoard[i] == 0) {
|
||||||
|
interact(i%cols, (i-(i%cols))/cols, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((probabilityBoard[i] < lowestChance[0]) && (probabilityBoard[i] != -1)) {
|
||||||
|
lowestChance = [probabilityBoard[i], i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var lowestChanceIndex = lowestChance[1];
|
||||||
|
var x = lowestChanceIndex%cols;
|
||||||
|
var y = (lowestChanceIndex - x) / cols;
|
||||||
|
|
||||||
|
interact(x, y, false);
|
||||||
|
console.log("Choosing " + lowestChanceIndex + " with a bomb chance of " + lowestChance[0].toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function calculateProbability(index) {
|
||||||
|
if (board[index].revealed) { //Can't be a bomb, don't even consider it
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (board[index].marked) { //MUST be a bomb, ignore it
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
var neighborIndexes = neighborIndexOffsets.map(x => x + index);
|
||||||
|
var usefulNeighbors = [];
|
||||||
|
for (var i = 0; i < neighborIndexes.length; i++) {
|
||||||
|
if ((neighborIndexes[i] < board.length) && (neighborIndexes[i] >= 0) && (abs((index%cols) - board[neighborIndexes[i]].x) <= 1)) {
|
||||||
|
if (board[neighborIndexes[i]].revealed) {
|
||||||
|
usefulNeighbors.push(neighborIndexes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usefulNeighbors.length == 0) { //If we dont know anything, use the random chance value
|
||||||
|
return ((mineCount-markedBombs) / (rows*cols));
|
||||||
|
}
|
||||||
|
|
||||||
|
var neighborChances = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < usefulNeighbors.length; i++) {
|
||||||
|
var markedNeighbors = countMarkedNeighbors(usefulNeighbors[i]);
|
||||||
|
var missingMarks = board[usefulNeighbors[i]].neighbors - markedNeighbors;
|
||||||
|
var untouchedNeighbors = countUntouchedNeighbors(usefulNeighbors[i]);
|
||||||
|
|
||||||
|
if (missingMarks == untouchedNeighbors + markedNeighbors) {
|
||||||
|
return 1; //100 %
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingMarks == 0) {
|
||||||
|
return 0; //0%, all mines are marked
|
||||||
|
}
|
||||||
|
|
||||||
|
neighborChances.push(missingMarks / untouchedNeighbors);
|
||||||
|
}
|
||||||
|
|
||||||
|
//return average(neighborChances);
|
||||||
|
return maxValue(neighborChances);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function countMarkedNeighbors(index) {
|
||||||
|
var neighborIndexes = neighborIndexOffsets.map(x => x + index);
|
||||||
|
var markedNeighbors = 0;
|
||||||
|
for (var i = 0; i < neighborIndexes.length; i++) {
|
||||||
|
if ((neighborIndexes[i] >= 0) && (neighborIndexes[i] < board.length) && (abs((index%cols) - board[neighborIndexes[i]].x) <= 1)) {
|
||||||
|
if (board[neighborIndexes[i]].marked) {
|
||||||
|
markedNeighbors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return markedNeighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function countUntouchedNeighbors(index) {
|
||||||
|
var neighborIndexes = neighborIndexOffsets.map(x => x + index);
|
||||||
|
var untouchedNeighbors = 0;
|
||||||
|
for (var i = 0; i < neighborIndexes.length; i++) {
|
||||||
|
if ((neighborIndexes[i] >= 0) && (neighborIndexes[i] < board.length) && (abs((index%cols) - board[neighborIndexes[i]].x) <= 1)) {
|
||||||
|
if ((!board[neighborIndexes[i]].marked) && (!board[neighborIndexes[i]].revealed)) {
|
||||||
|
untouchedNeighbors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return untouchedNeighbors;
|
||||||
|
}
|
||||||
|
|
||||||
|
function average(list) {
|
||||||
|
var sum = 0;
|
||||||
|
var len= list.length;
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
|
sum += list[i];
|
||||||
|
}
|
||||||
|
return sum/len;
|
||||||
|
}
|
||||||
|
|
||||||
|
function maxValue(list) {
|
||||||
|
if (list.length == 0) { return -1; }
|
||||||
|
|
||||||
|
var largest = list[0];
|
||||||
|
for (var i = 1; i < list.length; i++) {
|
||||||
|
if (list[i] > largest) {
|
||||||
|
largest = list[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return largest;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
//Felix Albrigtsen 2019
|
||||||
|
//Cell object and methods for minesweeper.
|
||||||
|
|
||||||
|
class Cell {
|
||||||
|
constructor(x_, y_) {
|
||||||
|
this.mine = false;
|
||||||
|
this.revealed = false;
|
||||||
|
this.marked = false;
|
||||||
|
this.x = x_;
|
||||||
|
this.y = y_;
|
||||||
|
this.boardIndex = (this.y*cols) + this.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark() {
|
||||||
|
this.marked = !this.marked;
|
||||||
|
}
|
||||||
|
|
||||||
|
countNeighbors() {
|
||||||
|
var neighborIndexes = neighborIndexOffsets.map(x => x + this.boardIndex);
|
||||||
|
var neighborMines = 0;
|
||||||
|
|
||||||
|
for (var i = 0; i < neighborIndexes.length; i++) {
|
||||||
|
if (((neighborIndexes[i] >= 0) && (neighborIndexes[i] < board.length)) && (abs(this.x - board[neighborIndexes[i]].x) <= 1)) {
|
||||||
|
if (board[neighborIndexes[i]].mine) { neighborMines++; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.neighbors = neighborMines;
|
||||||
|
}
|
||||||
|
|
||||||
|
reveal() {
|
||||||
|
if (this.mine) {
|
||||||
|
//Game over, instant loss
|
||||||
|
gameover = true;
|
||||||
|
for (var i = 0; i < board.length; i++) {
|
||||||
|
if (!board[i].mine) { board[i].reveal(); }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.revealed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.neighbors == 0) {
|
||||||
|
var neighborIndexes = neighborIndexOffsets.map(x => x + this.boardIndex);
|
||||||
|
for (var i = 0; i < neighborIndexes.length; i++) {
|
||||||
|
if (((neighborIndexes[i] >= 0) && (neighborIndexes[i] < board.length)) && (abs(this.x - board[neighborIndexes[i]].x) <= 1)) {
|
||||||
|
if (!board[neighborIndexes[i]].revealed) {
|
||||||
|
board[neighborIndexes[i]].reveal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,195 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-153522520-1"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', 'UA-153522520-1');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<script language="javascript" type="text/javascript" src="libraries/p5.js"></script>
|
||||||
|
<script language="javascript" type="text/javascript" src="cell.js"></script>
|
||||||
|
<script language="javascript" type="text/javascript" src="ai.js"></script>
|
||||||
|
<script language="javascript" type="text/javascript" src="timer.js"></script>
|
||||||
|
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Minesweeper</title>
|
||||||
|
<link href="style.css" rel="stylesheet" type="text/css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="gameDiv">
|
||||||
|
<h2 id="titleHead">Minesweeper.no</h2>
|
||||||
|
<span><button id="btn_new_0">Easy</button>
|
||||||
|
<button id="btn_new_1">Medium</button>
|
||||||
|
<button id="btn_new_2">Hard</button>
|
||||||
|
<button id="btn_new_3">Extreme</button></span>
|
||||||
|
<div id="canvasDiv"></div>
|
||||||
|
</div>
|
||||||
|
<div id="leftPane">
|
||||||
|
<button id="btn_start" class="paneBtn">Start Auto-Solve</button>
|
||||||
|
<button id="btn_stop" class="paneBtn">Stop Auto-Solve</button>
|
||||||
|
<button id="btn_step" class="paneBtn">Step Auto-Solve</button>
|
||||||
|
<br><br><br>
|
||||||
|
<button id="btn_help" class="paneBtn">Help</button>
|
||||||
|
<br><br><br>
|
||||||
|
<h2 id="timerText"></h2>
|
||||||
|
<br><br>
|
||||||
|
<p>Felix Albrigtsen</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script language="javascript" type="text/javascript" src="sketch.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div id="helpOverlay">
|
||||||
|
<h1>Instructions</h1>
|
||||||
|
<br>
|
||||||
|
<p class="helpText">The game of minesweeper is a logical puzzle game with an element of chance.<br>
|
||||||
|
Your goal is to "open" all cells, either by revealing them (clicking) or marking them (shift + click).<br>
|
||||||
|
Some squares are mines/bombs, but most are safe to click. If you accidentally click a mine, the game is over.
|
||||||
|
When you fail, all cells will be opened, and you must restart the game(See below).<br>
|
||||||
|
If you click a square where no "neighbors", meaning the 8 squares directly touching it, are bombs, it will be blank
|
||||||
|
and light gray. It will also reveal all it's neighbors. If a square has one or more neighbors that ARE bombs, they will
|
||||||
|
display the number of explosive neighbors. You must use these numbers to find out what sqaures are safe.
|
||||||
|
When you have successfully categorized all the squares as mines or safe, they will be marked in green, and you won.<br>
|
||||||
|
</p>
|
||||||
|
<br>
|
||||||
|
<h2>Inputs / Controls     Difficulties</h2>
|
||||||
|
<div id="tableDiv">
|
||||||
|
<table class="helpTable">
|
||||||
|
<thead>
|
||||||
|
<th>Function</th>
|
||||||
|
<th>Button</th>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td>Reveal</td>
|
||||||
|
<td>W or Click</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Mark as unsafe</td>
|
||||||
|
<td>Q or Shift + click </td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Restart game</td>
|
||||||
|
<td>R</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Automatic / best move</td>
|
||||||
|
<td>A</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Restart and change difficulty</td>
|
||||||
|
<td>Buttons above the play field</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<table class="helpTable">
|
||||||
|
<thead>
|
||||||
|
<th>Difficulty</th>
|
||||||
|
<th>Rows</th>
|
||||||
|
<th>Columns</th>
|
||||||
|
<th>Mines</th>
|
||||||
|
<th>Mine percentage</th>
|
||||||
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td>Easy</td>
|
||||||
|
<td>10</td>
|
||||||
|
<td>10</td>
|
||||||
|
<td>10</td>
|
||||||
|
<td>10%</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Medium</td>
|
||||||
|
<td>16</td>
|
||||||
|
<td>16</td>
|
||||||
|
<td>38</td>
|
||||||
|
<td>15%</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Hard</td>
|
||||||
|
<td>16</td>
|
||||||
|
<td>26</td>
|
||||||
|
<td>83</td>
|
||||||
|
<td>20%</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Extreme</td>
|
||||||
|
<td>30</td>
|
||||||
|
<td>36</td>
|
||||||
|
<td>216</td>
|
||||||
|
<td>20%</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<h2>Auto-Move info</h2>
|
||||||
|
<p class="helpText">
|
||||||
|
This version of Minesweeper is equipped with an auto-move function. This "AI" software does not have access to any more information than the player does.
|
||||||
|
It can only see what cells are revealed, and the numbers inside the revealed cells. Using this data, it will try calculate the probability of each cell
|
||||||
|
being a mine, and mark/reveal it accordingly. It can make mistakes, as minesweeper also is a game of chance. Remember: Using the auto-move function will stop the timer!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="bottomPadding">
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById("btn_new_0").onclick = function() {
|
||||||
|
if (refreshPage) {
|
||||||
|
window.location.href = window.location.pathname + "?d=0";
|
||||||
|
} else {
|
||||||
|
setDifficulty(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.getElementById("btn_new_1").onclick = function() {
|
||||||
|
if (refreshPage) {
|
||||||
|
window.location.href = window.location.pathname + "?d=1";
|
||||||
|
} else {
|
||||||
|
setDifficulty(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.getElementById("btn_new_2").onclick = function() {
|
||||||
|
if (refreshPage) {
|
||||||
|
window.location.href = window.location.pathname + "?d=2";
|
||||||
|
} else {
|
||||||
|
setDifficulty(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.getElementById("btn_new_3").onclick = function() {
|
||||||
|
if (refreshPage) {
|
||||||
|
window.location.href = window.location.pathname + "?d=3";
|
||||||
|
} else {
|
||||||
|
setDifficulty(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById("btn_start").onclick = function() {
|
||||||
|
if (botTimer != undefined) { return; }
|
||||||
|
botTimer = setInterval(autoMove, 700);
|
||||||
|
}
|
||||||
|
document.getElementById("btn_stop").onclick = function() {
|
||||||
|
clearInterval(botTimer);
|
||||||
|
botTimer = undefined;
|
||||||
|
}
|
||||||
|
document.getElementById("btn_step").onclick = function() {
|
||||||
|
autoMove();
|
||||||
|
}
|
||||||
|
document.getElementById("btn_help").onclick = function() {
|
||||||
|
document.getElementById("helpOverlay").style.display = "block";
|
||||||
|
}
|
||||||
|
document.getElementById("helpOverlay").onclick = function() {
|
||||||
|
document.getElementById("helpOverlay").style.display = "none";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,239 @@
|
||||||
|
//Felix Albrigtsen 2019
|
||||||
|
//Main code for minesweeper, dependent on cell.js, and partly on ai.js.
|
||||||
|
|
||||||
|
//Display and board settings
|
||||||
|
var cellSize = 23;
|
||||||
|
var padding = 3;
|
||||||
|
var rows = 16;
|
||||||
|
var cols = 16;
|
||||||
|
var mineCount = 30;
|
||||||
|
var canvWidth = (cols * (cellSize + padding)) + 1.5*padding;
|
||||||
|
var canvHeight = (rows * (cellSize + padding)) + 1.5*padding;
|
||||||
|
|
||||||
|
//Game state variables
|
||||||
|
var finished = false;
|
||||||
|
var gameover = false;
|
||||||
|
var markedBombs = 0;
|
||||||
|
var board = [];
|
||||||
|
|
||||||
|
//Calculate these values once, instead of each time
|
||||||
|
var neighborIndexOffsets = [-cols-1, -cols, -cols+1, -1, 1, cols-1, cols, cols+1];
|
||||||
|
|
||||||
|
var refreshPage = true; //Refresh when changing difficulty
|
||||||
|
var refreshPage_reset = false; // Do not refresh when hitting R, it takes too long
|
||||||
|
|
||||||
|
|
||||||
|
function setDifficulty(level) {
|
||||||
|
switch (level) {
|
||||||
|
case 0:
|
||||||
|
rows = 10;
|
||||||
|
cols = 10;
|
||||||
|
padding = 3;
|
||||||
|
mineCount = 10;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
rows = 16;
|
||||||
|
cols = 16;
|
||||||
|
padding = 3;
|
||||||
|
mineCount = 38;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
rows = 20;
|
||||||
|
cols = 22;
|
||||||
|
padding = 2;
|
||||||
|
mineCount = 88;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
rows = 30;
|
||||||
|
cols = 32;
|
||||||
|
padding = 2;
|
||||||
|
mineCount = 192;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rows = 16;
|
||||||
|
cols = 16;
|
||||||
|
padding = 3;
|
||||||
|
mineCount = 38;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//CanvWidth = cols * (padding + cellsize)
|
||||||
|
//cellsize = canvWidth / cols - padding
|
||||||
|
|
||||||
|
//var boardMaxHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) * 0.75;
|
||||||
|
//cellSize = ((boardMaxHeight / rows)-padding) + (3-level);
|
||||||
|
|
||||||
|
var boardMaxWidth = Math.max(document.documentElement.clientHeight, window.innerHeight || 0) * 0.7;
|
||||||
|
cellSize = ((boardMaxWidth / cols)-padding) + (3-level);
|
||||||
|
|
||||||
|
while (cols * (padding + cellSize) > Math.max(document.documentElement.clientWidth, window.innerWidth || 0)) {
|
||||||
|
cellSize--;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvWidth = (cols * (cellSize + padding)) + 1.5*padding;
|
||||||
|
canvHeight = (rows * (cellSize + padding)) + 1.5*padding;
|
||||||
|
|
||||||
|
neighborIndexOffsets = [-cols-1, -cols, -cols+1, -1, 1, cols-1, cols, cols+1]
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
noLoop();
|
||||||
|
reset();
|
||||||
|
if (getURLParams().d != undefined) {
|
||||||
|
setDifficulty(parseInt(getURLParams().d));
|
||||||
|
} else {
|
||||||
|
setDifficulty(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
createCanvas(canvWidth, canvHeight).parent("canvasDiv");
|
||||||
|
textSize(cellSize);
|
||||||
|
|
||||||
|
board = [];
|
||||||
|
finished = false;
|
||||||
|
gameover = false;
|
||||||
|
markedBombs = false;
|
||||||
|
|
||||||
|
for (var i = 0; i < rows; i++) {
|
||||||
|
for (var j = 0; j < cols; j++) {
|
||||||
|
board.push(new Cell(j, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
placeMines();
|
||||||
|
|
||||||
|
for (var i = 0; i < board.length; i++) {
|
||||||
|
board[i].countNeighbors();
|
||||||
|
}
|
||||||
|
|
||||||
|
draw();
|
||||||
|
|
||||||
|
timer_running = false;
|
||||||
|
if (timerInterval) { clearInterval(timerInterval); }
|
||||||
|
timerInterval = undefined;
|
||||||
|
timer_hasCheated = false;
|
||||||
|
timer_ms = -1;
|
||||||
|
timerTick();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
markedBombs = 0;
|
||||||
|
background(50);
|
||||||
|
|
||||||
|
if (gameover || finished) {
|
||||||
|
stopTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < board.length; i++) {
|
||||||
|
if (board[i].revealed) {
|
||||||
|
fill(200);
|
||||||
|
} else {
|
||||||
|
fill(100);
|
||||||
|
if (board[i].marked) {
|
||||||
|
fill(255, 100, 0);
|
||||||
|
markedBombs++;
|
||||||
|
}
|
||||||
|
if (finished) {
|
||||||
|
fill(0, 255, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameover && board[i].mine) {
|
||||||
|
fill(255,0,0);
|
||||||
|
}
|
||||||
|
|
||||||
|
rect(board[i].x * (cellSize + padding) + padding, board[i].y * (cellSize + padding) + padding, cellSize, cellSize);
|
||||||
|
|
||||||
|
if (board[i].revealed) {
|
||||||
|
fill(50);
|
||||||
|
if (board[i].neighbors != 0) {
|
||||||
|
text(board[i].neighbors, (board[i].x + 0.32) * (cellSize + padding), (board[i].y+0.9) * (cellSize + padding));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function placeMines() {
|
||||||
|
var placed = 0;
|
||||||
|
while (placed != mineCount) {
|
||||||
|
//Place mines, make sure that the same square isn't selected again
|
||||||
|
var i = Math.floor(Math.random() * board.length);
|
||||||
|
|
||||||
|
if (!board[i].mine) {
|
||||||
|
board[i].mine = true;
|
||||||
|
placed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_finished() {
|
||||||
|
for (var i = 0; i < board.length; i++) {
|
||||||
|
if (board[i].mine && !board[i].marked) { return false;}
|
||||||
|
if (!board[i].revealed && !board[i].mine) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function interact(x, y, mark) {
|
||||||
|
if (document.getElementById("helpOverlay").style.display == "block") { return; }
|
||||||
|
|
||||||
|
var index = (y * cols) + x;
|
||||||
|
//console.log("x: " + x.toString() + " y: " + y.toString());
|
||||||
|
if (mark) {
|
||||||
|
board[index].mark();
|
||||||
|
} else {
|
||||||
|
board[index].reveal();
|
||||||
|
}
|
||||||
|
|
||||||
|
finished = test_finished();
|
||||||
|
|
||||||
|
if (!timer_running) {
|
||||||
|
startTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseClicked() {
|
||||||
|
|
||||||
|
if ((mouseX < 0) || (mouseX >= canvWidth)) { return; }
|
||||||
|
if ((mouseY < 0) || (mouseY >= canvHeight)) { return; }
|
||||||
|
|
||||||
|
var ix = Math.floor((mouseX-padding) / (cellSize + padding));
|
||||||
|
var iy = Math.floor((mouseY-padding) / (cellSize + padding));
|
||||||
|
|
||||||
|
interact(ix, iy, keyIsDown(SHIFT));
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyPressed() {
|
||||||
|
if (keyIsDown(82)) { //Reset when you click R
|
||||||
|
if (refreshPage_reset) {
|
||||||
|
window.location.reload();
|
||||||
|
} else {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyIsDown(65)) {
|
||||||
|
autoMove(false); //Automove when you click A
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyIsDown(87)) { //Click with W
|
||||||
|
var ix = Math.floor((mouseX-padding) / (cellSize + padding));
|
||||||
|
var iy = Math.floor((mouseY-padding) / (cellSize + padding));
|
||||||
|
|
||||||
|
interact(ix, iy, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyIsDown(81)) { //Mark with Q
|
||||||
|
var ix = Math.floor((mouseX-padding) / (cellSize + padding));
|
||||||
|
var iy = Math.floor((mouseY-padding) / (cellSize + padding));
|
||||||
|
|
||||||
|
interact(ix, iy, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
@charset "utf-8";
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 5fr 2fr 2fr;
|
||||||
|
grid-template-rows: 1fr 7fr 6fr;
|
||||||
|
|
||||||
|
background: rgb(175,175,175);
|
||||||
|
background: radial-gradient(circle, rgba(175,175,175,1) 0%, rgba(53,53,54,1) 100%);
|
||||||
|
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
#helpOverlay {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 200vh;
|
||||||
|
background-color: rgba(0,0,0,0.85);
|
||||||
|
cursor: pointer;
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.helpText {
|
||||||
|
width: 60%;
|
||||||
|
margin: 0 auto;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.helpTable {
|
||||||
|
display: inline-block;
|
||||||
|
color: white;
|
||||||
|
margin: 0 auto;
|
||||||
|
font-size: 18px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 2px solid white;
|
||||||
|
height: 158px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.helpTable > td {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#titleHead {
|
||||||
|
color: white;
|
||||||
|
font-size: 22px;
|
||||||
|
font-family: "Franklin Gothic Bold", "Arial Black", "sans-serif";
|
||||||
|
text-align: center;
|
||||||
|
margin: auto;
|
||||||
|
margin-bottom: 2%;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
background-color: midnightblue;
|
||||||
|
width: 400px;
|
||||||
|
height: 70%;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#gameDiv {
|
||||||
|
grid-column: 2 / 3;
|
||||||
|
grid-row: 2 / 3;
|
||||||
|
text-align: center;
|
||||||
|
margin: auto 0 600px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#canvasDiv {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#leftPane {
|
||||||
|
grid-column: 3 / 4;
|
||||||
|
grid-row: 1 / 3;
|
||||||
|
|
||||||
|
margin: 10px;
|
||||||
|
padding: 30px;
|
||||||
|
text-align: center;
|
||||||
|
border: 2px solid black;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: whitesmoke;
|
||||||
|
|
||||||
|
max-width: 200px;
|
||||||
|
height: 45%;
|
||||||
|
margin: 20% auto auto 5%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#timerText {
|
||||||
|
font-size: 26px;
|
||||||
|
border: 1px solid black;
|
||||||
|
border-radius: 5px;
|
||||||
|
padding: 10px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bottomPadding {
|
||||||
|
grid-column: 1/3;
|
||||||
|
grid-row: 3/4;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paneBtn {
|
||||||
|
margin: 6px;
|
||||||
|
padding: 3px;
|
||||||
|
font-size: 16px;
|
||||||
|
width: 80%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media (max-width: 750px) {
|
||||||
|
body {
|
||||||
|
grid-template-columns: 1fr 2fr;
|
||||||
|
grid-template-rows: 1fr 3fr;
|
||||||
|
max-height: none;
|
||||||
|
}
|
||||||
|
#gameDiv {
|
||||||
|
grid-column: 1 / 3;
|
||||||
|
grid-row: 1 / 2;
|
||||||
|
margin: 15px auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
#leftPane {
|
||||||
|
grid-column: 1 / 3;
|
||||||
|
grid-row: 2 / 3;
|
||||||
|
float: center;
|
||||||
|
margin: 10px auto 0px auto;
|
||||||
|
min-width: 60%;
|
||||||
|
height: 600px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Temporary */
|
||||||
|
img {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Felix Albrigtsen
|
||||||
|
|
||||||
|
var timerInterval = undefined;
|
||||||
|
var timer_hasCheated = false;
|
||||||
|
var timer_running = false;
|
||||||
|
var timer_ms = 0;
|
||||||
|
|
||||||
|
function startTimer() {
|
||||||
|
if (timer_running) { return; }
|
||||||
|
timer_ms = 0;
|
||||||
|
timer_running = true;
|
||||||
|
timerInterval = setInterval(timerTick, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopTimer() {
|
||||||
|
if (!timer_running) { return; }
|
||||||
|
timer_running = false;
|
||||||
|
clearInterval(timerInterval);
|
||||||
|
timerInterval = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function timerTick() {
|
||||||
|
timer_ms++;
|
||||||
|
var t_centisecond = (timer_ms % 100);
|
||||||
|
var t_second = ((timer_ms - t_centisecond) % (100*60))/100;
|
||||||
|
var t_minute = (timer_ms - (t_second*100) - t_centisecond) / (100*60);
|
||||||
|
|
||||||
|
t_centisecond = ("00" + t_centisecond).substr(-2, 2);
|
||||||
|
t_second = ("00" + t_second).substr(-2, 2);
|
||||||
|
t_minute = ("00" + t_minute).substr(-2, 2);
|
||||||
|
|
||||||
|
document.getElementById("timerText").innerHTML = t_minute + ":" + t_second + ":" + t_centisecond;
|
||||||
|
|
||||||
|
if (timer_hasCheated) {
|
||||||
|
document.getElementById("timerText").style.color = "red";
|
||||||
|
} else {
|
||||||
|
document.getElementById("timerText").style.color = "green";
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue