diff options
Diffstat (limited to 'mines')
| -rw-r--r-- | mines/index.html | 64 | ||||
| -rw-r--r-- | mines/mines.css | 176 | ||||
| -rw-r--r-- | mines/mines.js | 490 | 
3 files changed, 730 insertions, 0 deletions
diff --git a/mines/index.html b/mines/index.html new file mode 100644 index 0000000..5b27806 --- /dev/null +++ b/mines/index.html @@ -0,0 +1,64 @@ +<!Doctype HTML>
 +<html>
 +	<head>
 +		<meta charset="utf-8">
 +		
 +		<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
 +		<link rel="stylesheet" type="text/css" href="./mines.css"/>
 +		
 +		<script src="./mines.js"></script>
 +	</head>
 +	<body>
 +		<div class="selector">
 +			<label>Width: </label> <input id="x" min="4" max="50" value="6" type="number"/>
 +			<br>
 +			<label>Height: </label> <input id="y" min="4" value="6" type="number"/>
 +			<br>
 +			<label>Mines: </label> <input id="m" min="2" value="5" type="number"/>
 +			<br>
 +			<label>- OR -</label>
 +			<br>
 +			<label>Difficulty: </label>
 +			<select id="d">
 +				<option value="0.125">Easy</option>
 +				<option value="0.15">Medium</option>
 +				<option value="0.2">Hard</option>
 +				<option value="0.25">Impossible</option>
 +				<option value="0">Custom</option>
 +			</select>
 +			<br>
 +			<label>- OR -</label>
 +			<br>
 +			<button id="save" onclick="game.save()">Save Game</button> | <label for="fin">Choose Save File</label><input id="fin" type="file" accept=".mgs"/> <button id="load" onclick="game.loadFromFile()">Load Game</button>
 +		</div>
 +		<br>
 +		<div class="game">
 +			<h1 id="mines">0</h1><button id="circle" onclick="game.reset()"></button><h1 id="time">0</h1>
 +			<br>
 +			<table>
 +				<tbody id="gtable"><tr><td class="wt">◉</td></tr></tbody>
 +			</table>
 +		</div>
 +		<script>
 +			var xIn = document.getElementById("x");
 +			var yIn = document.getElementById("y");
 +			var mIn = document.getElementById("m");
 +			var dIn = document.getElementById("d");
 +			
 +			var sMines = document.getElementById("mines");
 +			var sTime = document.getElementById("time");
 +			
 +			var circle = document.getElementById("circle");
 +			
 +			var table = document.getElementById("gtable");
 +			
 +			var fileSel = document.getElementById("fin");
 +			
 +			var game = new Board();
 +			
 +			mIn.addEventListener("change", function(){
 +				if(dIn.value !== "0") dIn.value = "0";
 +			});
 +		</script>
 +	</body>
 +</html>
\ No newline at end of file diff --git a/mines/mines.css b/mines/mines.css new file mode 100644 index 0000000..810e74e --- /dev/null +++ b/mines/mines.css @@ -0,0 +1,176 @@ +*{
 +	font-family: 'Open Sans', sans-serif;
 +}
 +
 +body{
 +	text-align: center;
 +}
 +
 +div.selector, div.game{
 +	display: inline-block;
 +}
 +
 +div.selector{
 +	text-align: right;
 +}
 +
 +div.game{
 +	min-width: 50%;
 +	margin: 50px;
 +}
 +
 +input, select, label[for], button{
 +	padding: 5px;
 +	margin: 5px;
 +	background-color: #ddd;
 +	border: none;
 +	border-radius: 5px;
 +	transition-duration: 0.2s;
 +	box-sizing: border-box;
 +	appearance: none;
 +	-moz-appearance: none;
 +	-webkit-appearance: none;
 +	font-size: medium;
 +}
 +
 +input:hover, select:hover, label[for]:hover, button:hover{
 +	background-color: #bbb;
 +}
 +
 +input:focus, select:focus, label[for]:focus, button:focus{
 +	background-color: #bdf;
 +	border: none;
 +}
 +
 +input:active, select:active, label[for]:active, button:active{
 +	background-color: #bdf;
 +	border: none;
 +}
 +
 +input[type="file"]{
 +	width: 0;
 +	height: 0;
 +	
 +	padding: 0;
 +	margin: 0;
 +	
 +	border-radius: 0;
 +	opacity: 0;
 +}
 +
 +h1{
 +	width: calc(50% - 70px);
 +	
 +	display: inline;
 +	
 +	background-color: #ddd;
 +	
 +	border-radius: 50px;
 +}
 +
 +h1#mines{
 +	float: left;
 +}
 +
 +h1#time{
 +	float: right;
 +}
 +
 +/* Table */
 +
 +table{
 +	display: inline-table;
 +	background-color: #ddd;
 +	border-radius: 10px;
 +	padding: 5px;
 +}
 +
 +td{
 +	width: 45px;
 +	height: 45px;
 +	
 +	margin: 5px;
 +	padding: 2px;
 +	
 +	background-color: #aaa;
 +	
 +	box-sizing: border-box;
 +	
 +	border-radius: 25%;
 +	
 +	transition-duration: 0.2s;
 +	
 +	cursor: pointer;
 +}
 +
 +td.wt:hover{
 +	background-color: #ccc;
 +}
 +
 +td.wt:active{
 +	background-color: #999;
 +}
 +
 +td.chkd{
 +	background-color: #777;
 +	cursor: default;
 +}
 +
 +/* Main button */
 +
 +button#circle{
 +	padding: 35px;
 +	border: none;
 +	border-radius: 50%;
 +	box-sizing: border-box;
 +	cursor: pointer;
 +}
 +
 +button#circle:active{
 +	border: none;
 +}
 +
 +button#circle.win{
 +	background-color: #0f4;
 +}
 +
 +button#circle.win:hover{
 +	background-color: #0d2;
 +}
 +
 +button#circle.lose{
 +	background-color: #f33;
 +}
 +
 +button#circle.lose:hover{
 +	background-color: #d11;
 +}
 +
 +button#circle.ingame{
 +	background-color: #fa0;
 +	animation: 1s infinite alternate shrink;
 +}
 +
 +button#circle.ingame:hover{
 +	background-color: #d80;
 +}
 +
 +button#circle.loading{
 +	background-color: #0af;
 +	animation: 1s infinite alternate shrink;
 +}
 +
 +button#circle.loading:hover{
 +	background-color: #08d;
 +}
 +
 +@keyframes shrink{
 +	from{
 +		padding: 35px;
 +		margin-top: 0px;
 +	}
 +	to{
 +		padding: 15px;
 +		margin-top: 20px
 +	}
 +}
\ No newline at end of file diff --git a/mines/mines.js b/mines/mines.js new file mode 100644 index 0000000..5585bb6 --- /dev/null +++ b/mines/mines.js @@ -0,0 +1,490 @@ +var colors = ["blue", "limegreen", "orange", "red", "purple", "cyan", "gold", "black"];
 +
 +var mine = "\u25C9";
 +
 +function Board(){
 +	table.parentElement.addEventListener("contextmenu", function(e){e.preventDefault();});
 +	
 +	this.mines = [[0]];
 +	this.mTotal = 0;
 +	this.mChecked = 0;
 +	
 +	this.boardDim = [1, 1];
 +	this.checked = 0;
 +	
 +	this.time = 0;
 +	this.clock = -1;
 +	
 +	this.running = false;
 +	this.started = false;
 +	
 +}
 +
 +Board.prototype = {
 +	
 +	//Second
 +	
 +	sec: function(){
 +		this.time++;
 +		sTime.textContent = this.time;
 +	},
 +	
 +	//Game states
 +	
 +	win: function(){
 +		if(!this.running) return;
 +		this.running = false;
 +		clearInterval(this.clock);
 +		this.clock = -1;
 +		circle.className = "win";
 +		
 +		this.revealMines("#08d");
 +	},
 +	
 +	lose: function(x, y){
 +		if(!this.running) return;
 +		this.running = false;
 +		clearInterval(this.clock);
 +		this.clock = -1;
 +		circle.className = "lose";
 +		
 +		this.revealMines("black");
 +		
 +		this.getMineEl(x, y).style.color = "red";
 +	},
 +	
 +	revealMines: function(color){
 +		let lp = this.mines.length;
 +		for(let i = 0; i < lp; i++){
 +			let lp2 = this.mines[i].length;
 +			for(let j = 0; j < lp2; j++){
 +				let e = this.getMineEl(this.mines[i][j], i);
 +				e.style.color = color;
 +				e.textContent = mine;
 +			}
 +		}
 +	},
 +	
 +	closeToStart: function(x, y, sx, sy){
 +		let length = Math.sqrt((x-sx)*(x-sx) + (y-sy)*(y-sy));
 +		if(length <  1.5) return true;
 +		return false;
 +	},
 +	
 +	replaceMine: function(x, y, sx, sy){
 +		let flag = false;
 +		if(!this.isMine(x, y)) return;
 +		while(!flag){
 +			for(let i = 0; i < this.boardDim[1] && !flag; i++){
 +				for(let j = 0; j < this.boardDim[0] && !flag; j++){
 +					if(!this.isMine(j, i) && !this.closeToStart(j, i, sx, sy) && Math.floor(Math.random()*4) == 0){
 +						this.mines[i].push(j);
 +						flag = true;
 +					}
 +				}
 +			}
 +		}
 +		this.mines[y].splice(this.mines[y].indexOf(x), 1);
 +	},
 +	
 +	start: function(x, y){
 +		this.started = true;
 +		
 +		let dx = x;
 +		this.replaceMine(dx, y, x, y);
 +		this.replaceMine(dx, y+1, x, y);
 +		this.replaceMine(dx, y-1, x, y);
 +		dx++;
 +		this.replaceMine(dx, y, x, y);
 +		this.replaceMine(dx, y+1, x, y);
 +		this.replaceMine(dx, y-1, x, y);
 +		dx -= 2;
 +		this.replaceMine(dx, y, x, y);
 +		this.replaceMine(dx, y+1, x, y);
 +		this.replaceMine(dx, y-1, x, y);
 +		
 +		this.clock = setInterval(this.sec.bind(this), 1000);
 +	},
 +	
 +	//Event managers
 +	
 +	click: function(e){
 +		let el = e.target;
 +		var x = parseInt(el.getAttribute("x")), y = parseInt(el.getAttribute("y"));
 +		
 +		if(!this.running || el.textContent !== " " || el.classList.contains("chkd")) return;
 +		
 +		if(!this.started){
 +			this.start(x, y);
 +		}
 +		
 +		if(this.isMine(x, y)){
 +			this.lose(x, y);
 +			return;
 +		}
 +		
 +		this.check(x, y);
 +	},
 +	
 +	ctxMenu: function(e){
 +		let el = e.target;
 +		var x = parseInt(el.getAttribute("x")), y = parseInt(el.getAttribute("y"));
 +		
 +		if(el.classList.contains("chkd") || !this.running || !this.started) {
 +			e.preventDefault();
 +			return;
 +		}
 +		
 +		if(el.textContent == "!"){
 +			
 +			el.textContent = "?";
 +			this.mChecked--;
 +		}else if(el.textContent == "?"){
 +			
 +			el.textContent = " ";
 +		}else{
 +			
 +			el.textContent = "!";
 +			this.mChecked++;
 +		}
 +		sMines.textContent = this.mTotal - this.mChecked;
 +		e.preventDefault();
 +	},
 +	
 +	//Recognizing the click
 +	
 +	isMine: function(x, y){
 +		if(y >= this.mines.length || y < 0) return false;
 +		return this.mines[y].includes(x);
 +	},
 +	
 +	isChecked: function(x, y){
 +		if(y >= this.boardDim[1] || y < 0) return true;
 +		if(x >= this.boardDim[0] || x < 0) return true;
 +		return table.children[y].children[x].className == "chkd";
 +	},
 +	
 +	numAround: function(x, y){
 +		let m = 0;let dx = x;
 +		if(this.isMine(dx, y+1)) m++;
 +		if(this.isMine(dx, y-1)) m++;
 +		dx = x+1;
 +		if(this.isMine(dx, y)) m++;
 +		if(this.isMine(dx, y+1)) m++;
 +		if(this.isMine(dx, y-1)) m++;
 +		dx = x-1;
 +		if(this.isMine(dx, y)) m++;
 +		if(this.isMine(dx, y+1)) m++;
 +		if(this.isMine(dx, y-1)) m++;
 +		return m;
 +	},
 +	
 +	getMineEl: function(x, y){
 +		return table.children[y].children[x];
 +	},
 +	
 +	check: function(x, y){
 +		if(!this.running) return;
 +		
 +		let n = this.numAround(x, y);
 +		let e = this.getMineEl(x, y);
 +		
 +		if(e.textContent == "!") return;
 +		
 +		e.className = "chkd";
 +		
 +		if(n !== 0){
 +			
 +			e.style.color = colors[n-1];
 +			e.textContent = n;
 +		}else{
 +			
 +			this.findPath(x, y);
 +		}
 +		
 +		this.checked++;
 +		if(this.checked == (this.boardDim[0]*this.boardDim[1] - this.mTotal)) this.win();
 +	},
 +	
 +	findPath: function(x, y){
 +		let dx = x;
 +		if(!this.isChecked(dx, y+1)) this.check(dx, y+1);
 +		if(!this.isChecked(dx, y-1)) this.check(dx, y-1);
 +		dx = x+1;
 +		if(!this.isChecked(dx, y+1)) this.check(dx, y+1);
 +		if(!this.isChecked(dx, y-1)) this.check(dx, y-1);
 +		if(!this.isChecked(dx, y)) this.check(dx, y);
 +		dx = x-1;
 +		if(!this.isChecked(dx, y+1)) this.check(dx, y+1);
 +		if(!this.isChecked(dx, y-1)) this.check(dx, y-1);
 +		if(!this.isChecked(dx, y)) this.check(dx, y);
 +	},
 +	
 +	//Managing the board
 +	
 +	reset: function(){
 +		setTimeout(this.loading(), 10);
 +		setTimeout(this.setup.bind(this), 100);
 +	},
 +	
 +	loading: function(){
 +		circle.className = "loading";
 +	},
 +	
 +	setup: function(){
 +		let x = xIn.value;
 +		let y = yIn.value;
 +		
 +		let mines = mIn.value;
 +		
 +		if(parseFloat(dIn.value) !== 0) mines = Math.round(x*y*parseFloat(dIn.value));
 +		
 +		if(x <= 3 && y <= 3){
 +			circle.className = "lose";
 +			alert("Board size too small!");
 +			return;
 +		}else if(mines > x*y-9) {
 +			circle.className = "lose";
 +			alert("Too many mines selected!");
 +			return;
 +		}
 +		
 +		this.started = false;
 +		
 +		this.boardDim = [x, y];
 +		
 +		while(table.firstChild){
 +			table.firstChild.remove();
 +		}
 +		
 +		this.mTotal = mines;
 +		this.mChecked = 0;
 +		this.mines = [];
 +		
 +		this.checked = 0;
 +		
 +		sMines.textContent = mines;
 +		
 +		this.time = 0;
 +		sTime.textContent = this.time;
 +		if(this.clock !== -1) clearInterval(this.clock);
 +		this.clock = -1;
 +		
 +		let mRarity = x*y*0.8;
 +		
 +		for(let i = 0; i < y; i++){
 +			
 +			let row = document.createElement("tr");
 +			
 +			this.mines.push([]);
 +			
 +			for(let j = 0; j < x; j++){
 +				
 +				let cell = document.createElement("td");
 +				cell.textContent = " ";
 +				cell.setAttribute("x", j);
 +				cell.setAttribute("y", i);
 +				cell.classList = ["wt"];
 +				cell.addEventListener("click", this.click.bind(this));
 +				cell.addEventListener("contextmenu", this.ctxMenu.bind(this));
 +				row.appendChild(cell);
 +				
 +				if(mines > 0 && Math.floor(Math.random()*mRarity) == 0){
 +					this.mines[i].push(j);
 +					mines--;
 +				}
 +			}
 +			
 +			table.appendChild(row);
 +		}
 +		
 +		while(mines > 0){
 +			for(let i = 0; i < y; i++){
 +				if(mines <= 0) break;
 +				for(let j = 0; j < x; j++){
 +					if(mines <= 0) break;
 +					if(!this.isMine(j, i) && Math.floor(Math.random()*mRarity) == 0){
 +						this.mines[i].push(j);
 +						mines--;
 +					}
 +				}
 +			}
 +		}
 +		
 +		circle.className = "ingame";
 +		this.running = true;
 +	},
 +	
 +	// Loading a previously saved game
 +	
 +	getSaveData: function(){
 +		var data = {};
 +		
 +		data.mines = this.mines;
 +		data.mTotal = this.mTotal;
 +		data.checked = this.checked;
 +		data.time = this.time;
 +		
 +		var boardState = [];
 +		
 +		for(var i = 0; i < table.children.length; i++){
 +			boardState.push([]);
 +			let row = table.children[i];
 +			for(var j = 0; j < row.children.length; j++){
 +				if(row.children[j].textContent == " "){
 +					if(row.children[j].classList.contains("chkd")) boardState[i].push("0");
 +					else boardState[i].push(" ");
 +				}else boardState[i].push(row.children[j].textContent);
 +			}
 +		}
 +		
 +		data.boardState = boardState;
 +		
 +		return data;
 +	},
 +	
 +	loadSaveData: function(e){
 +		if(this.clock !== -1) clearInterval(this.clock);
 +		this.clock = -1;
 +		
 +		var data;
 +		
 +		try{
 +			data = JSON.parse(e.target.result);
 +		}catch(err){
 +			this.loadError(err);
 +			return;
 +		}
 +		
 +		while(table.firstChild){
 +			table.firstChild.remove();
 +		}
 +		
 +		this.mines = data.mines;
 +		this.mTotal = data.mTotal;
 +		this.mChecked = 0;
 +		
 +		this.boardDim = [data.boardState[0].length, data.boardState.length];
 +		this.checked = data.checked;
 +		
 +		this.time = data.time;
 +		
 +		var finFlag = false;
 +		
 +		for(let i = 0; i < data.boardState.length; i++){
 +			
 +			let row = document.createElement("tr");
 +			
 +			this.mines.push([]);
 +			
 +			for(let j = 0; j < data.boardState[0].length; j++){
 +				
 +				let cell = document.createElement("td");
 +				cell.classList = ["wt"];
 +				
 +				switch(data.boardState[i][j]){
 +					case mine:
 +						finFlag = true;
 +					case "!":
 +						this.mChecked++;
 +					default:
 +						cell.textContent = data.boardState[i][j];
 +						let n = parseInt(data.boardState[i][j]);
 +						if(!isNaN(n)){
 +							cell.style.color = colors[n-1];
 +							cell.className = "chkd";
 +						}
 +						break;
 +						
 +					case "0":
 +						cell.className = "chkd";
 +						cell.textContent = " ";
 +						break;
 +				}
 +				
 +				cell.setAttribute("x", j);
 +				cell.setAttribute("y", i);
 +				
 +				cell.addEventListener("click", this.click.bind(this));
 +				cell.addEventListener("contextmenu", this.ctxMenu.bind(this));
 +				row.appendChild(cell);
 +				
 +				if(mines > 0 && Math.floor(Math.random()*mRarity) == 0){
 +					this.mines[i].push(j);
 +					mines--;
 +				}
 +			}
 +			
 +			table.appendChild(row);
 +		}
 +		
 +		sMines.textContent = this.mTotal - this.mChecked;
 +		sTime.textContent = this.time;
 +		
 +		this.clock = setInterval(this.sec.bind(this), 1000);
 +		
 +		circle.className = "ingame";
 +		
 +		this.running = true;
 +		this.started = true;
 +		
 +		if(finFlag){
 +			if(this.checked == (this.boardDim[0]*this.boardDim[1] - this.mTotal)) this.win();
 +			else this.lose();
 +		}
 +	},
 +	
 +	loadError: function(err){
 +		alert("Error loading previously saved game!\nCheck the console for more info.");
 +		throw err;
 +	},
 +	
 +	save: function(){
 +		if(!this.started){
 +			alert("Start a game to save it!");
 +			return;
 +		}
 +		var dat = this.getSaveData();
 +		download(JSON.stringify(dat), "MinesSave.mgs", "text/plain");
 +	},
 +	
 +	loadFromFile: function(){
 +		if(this.running){
 +			this.running = false;
 +			clearInterval(this.clock);
 +			this.clock = -1;
 +			circle.className = "loading";
 +		}
 +		
 +		if(fileSel.files.length < 1){
 +			alert("No file selected!");
 +			return;
 +		}
 +		var file = fileSel.files[0];
 +		var fr = new FileReader();
 +		fr.addEventListener("load", this.loadSaveData.bind(this));
 +		fr.addEventListener("error", this.loadError);
 +		
 +		fr.readAsText(file);
 +	}
 +};
 +
 +//Taken from Kanchu on Sack Overflow
 +//Reference: https://stackoverflow.com/questions/13405129/javascript-create-and-save-file
 +function download(data, filename, type) {
 +	var file = new Blob([data], {type: type});
 +	if (window.navigator.msSaveOrOpenBlob) // IE10+
 +		window.navigator.msSaveOrOpenBlob(file, filename);
 +	else { // Others
 +		var a = document.createElement("a"),
 +				url = URL.createObjectURL(file);
 +		a.href = url;
 +		a.download = filename;
 +		document.body.appendChild(a);
 +		a.click();
 +		setTimeout(function() {
 +			document.body.removeChild(a);
 +			window.URL.revokeObjectURL(url);  
 +		}, 0); 
 +	}
 +}
  |