mirror of
				https://github.com/felixalbrigtsen/minesweeper
				synced 2025-10-30 22:28:02 +01:00 
			
		
		
		
	Upload Source
This commit is contained in:
		
							parent
							
								
									c059159711
								
							
						
					
					
						commit
						e04ff053b9
					
				
							
								
								
									
										148
									
								
								minesweeper/ai.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								minesweeper/ai.js
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
|      | ||||
| } | ||||
							
								
								
									
										53
									
								
								minesweeper/cell.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								minesweeper/cell.js
									
									
									
									
									
										Normal file
									
								
							| @ -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(); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								minesweeper/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								minesweeper/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.1 KiB | 
							
								
								
									
										195
									
								
								minesweeper/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								minesweeper/index.html
									
									
									
									
									
										Normal file
									
								
							| @ -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> | ||||
							
								
								
									
										33029
									
								
								minesweeper/libraries/p5.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33029
									
								
								minesweeper/libraries/p5.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										239
									
								
								minesweeper/sketch.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								minesweeper/sketch.js
									
									
									
									
									
										Normal file
									
								
							| @ -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); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										155
									
								
								minesweeper/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								minesweeper/style.css
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										39
									
								
								minesweeper/timer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								minesweeper/timer.js
									
									
									
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 GitHub
							GitHub