<!DOCTYPE html> <html> <head> <title>endless mines</title> <style type="text/css"> @font-face { font-family: Helsinki; font-weight: normal; src: url(helsinki.ttf); } html { background-color: #272822; margin: 0; height: 100vh; width: 100vw; overflow: hidden; font-family: Helsinki; -webkit-user-select: none; } body { height: 100vh; margin: 0 auto; width: 100vmin; } #game { background-color: #1b1c17; float: left; margin: 0 auto; height: 90vmin; } #stats { height: 10vmin; width: 100vmin; float: left; background: #768087; background: -moz-linear-gradient(top, #768087 0%, #53595e 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#768087), color-stop(100%,#53595e)); background: -webkit-linear-gradient(top, #768087 0%,#53595e 100%); background: -o-linear-gradient(top, #768087 0%,#53595e 100%); background: -ms-linear-gradient(top, #768087 0%,#53595e 100%); background: linear-gradient(to bottom, #768087 0%,#53595e 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#768087', endColorstr='#53595e',GradientType=0 ); } #stats button { margin: 1vmin; float: right; font-family: Helsinki; font-size: 3vmin; border-radius: 2vmin; padding: 1.75vmin; box-shadow: 0.5vmin 0.5vmin 0 #000; border: 0.4vmin solid #454e52; position: relative; top: auto; left: auto; } #stats div { float: left; font-size: 5.4vmin; margin: 2vmin 4vmin; width: auto; } #gameOver, #setup { position: absolute; width: 100vmin; height: 90vmin; background-color: rgba(0,0,0,0.3); } #setup { height: 100vmin; } #setup h1 { } #setup h2 { margin-top: 0; } #setup h2, #setup label { margin: 4vmin 3.5vmin; font-size: 6vmin; color: #fe7ac6; text-shadow: 0.5vmin 0.5vmin 0 #a4f4b4; /* x y blur-radius colour */ } #setup div.centre { position: absolute; left: 10vmin; } input[type="radio"] { display: none; } /* after checked input */ input:checked + label { border: 1vmin dotted #a4f4b4; border-radius: 2vmin; padding: 1vmin 2vmin; } button { font-family: Helsinki; font-size: 6vmin; border-radius: 2vmin; padding: 2vmin; margin: 0 auto; box-shadow: 1vmin 1vmin 0 #000; border: none; position: absolute; left: 38vmin; top: 75vmin; background: #768087; background: -moz-linear-gradient(top, #768087 0%, #53595e 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#768087), color-stop(100%,#53595e)); background: -webkit-linear-gradient(top, #768087 0%,#53595e 100%); background: -o-linear-gradient(top, #768087 0%,#53595e 100%); background: -ms-linear-gradient(top, #768087 0%,#53595e 100%); background: linear-gradient(to bottom, #768087 0%,#53595e 100%); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#768087', endColorstr='#53595e',GradientType=0 ); } button.cancel { right: 20vmin; left: auto; } h1, h2 { text-align: center; } h1 { color: #a4f4b4; font-size: 12vmin; margin-top: 6vmin; margin-bottom: 6vmin; text-shadow: 1vmin 1vmin 0 #fe7ac6; /* x y blur-radius colour */ } ul { float: left; clear: both; list-style-type: none; padding: 0; margin: 0; } li { color: white; font-size: 8vmin; padding: 1vmin; padding-top: 0.5vmin; padding-bottom: 1.5vmin; text-align: center; vertical-align: middle; float: left; height: 8vmin; width: 8vmin; background-color: #454e52; border-radius: 2vmin; cursor: default; } li.revealed { background-color: #1b1c17; } li:not(.revealed):hover { background-color: #a0a9af; } li.flagged { background-color: #b5fe52; } li.flagged:hover { background-color: #c6fe7a; } li.mine.revealed { background-color: #d23000; color: #000; } li.mines1 { color: #c6fe7a; } li.mines2 { color: #7ac5fe; } li.mines3 { color: #fe7ac6; } li.mines4 { color: #b17afe; } li.mines5 { color: #feb27a; } li.mines6 { color: #7afeb2; } li.mines7 { color: #d74600; } li.mines8 { color: #8c4600; } p { color: #fff; font-size: 4.3vmin; padding: 0 10vmin; } strong { color: #fe7ac6; } </style> <script src="jquery-2.1.3.min.js"></script> <script> var gameBoardWidth = 10; var gameBoardHeight = 9; var score = 0; var firstClick = true; var currentlyIterating = false; var mineChance = 0.2; var clickholdMs = 200; var timeout; //hold timer var mouseHeld = false; $(document).ready(function() { function drawGameBoard() { $('#game').html(""); for (var i = 0; i < gameBoardHeight; i++) { $('#game').append("<ul></ul>"); } $('#game ul').each(function() { for (var i = 0; i < gameBoardWidth; i++) { $(this).append(newTile()); } }); } function newTile() { if (Math.random() < mineChance) { return '<li class="mine"></li>'; } else { return '<li></li>'; } } $.fn.checkRow = function() { //unclicked tiles if ($(this).children('li:not(.revealed):not(.flagged)').length > 0) { return false; } //incorrectly flagged tiles if ($(this).children('li.flagged:not(.mine)').length > 0) { return false; } //clicked mines if ($(this).children('li.revealed.mine').length > 0) { return false; } return true; } $.fn.checkColumn = function() { //unclicked tiles if ($(this).column().filter(':not(.revealed):not(.flagged)').length > 0) { return false; } //incorrectly flagged tiles if ($(this).column().filter('.flagged:not(.mine)').length > 0) { return false; } //clicked mines if ($(this).column().filter('.revealed.mine').length > 0) { return false; } return true; } window.removeClearedRows = function() { var rowsToRemove = $('#game ul:not(.removing)').filter(function() { return $(this).checkRow(); }); var numRowsToRemove = rowsToRemove.length; if (numRowsToRemove == 0) return; rowsToRemove.addClass("removing"); rowsToRemove.each(function() { score += $(this).children('.mine').length; }); rowsToRemove.slideUp("slow", function() { $(this).remove(); //add new row on bottom $('#game').append('<ul></ul>'); for (var i = 0; i < gameBoardWidth; i++) { $('#game ul').last().append(newTile()); } refreshMineCounts(); //click blank tiles $('li.revealed:not(.mine):empty').mouseup(); }); } window.removeClearedColumns = function() { var columnsToRemove = $('ul:not(.removing):eq(0) li:not(.removing)').filter(function() { return $(this).checkColumn(); }); if (columnsToRemove.length == 0) return; columnsToRemove.addClass("removing"); columnsToRemove.each(function() { score += $(this).column().filter('.mine').length; }); columnsToRemove.each(function() { $(this).column().animate({width: 0, borderRadius: 0, padding: 0}, "slow", function() { $(this).parent().append(newTile()); $(this).remove(); refreshMineCounts(); //click blank tiles $('li.revealed:not(.mine):empty').mouseup(); }); }); } function refreshMineCounts() { $('li.revealed:not(.mine)').each(function() { $(this).text( $(this).countMinesText() ); //remove "mines1" etc var mine = $(this).hasClass("mine"); $(this).removeAttr("class"); if (mine) { $(this).addClass("mine"); } else { $(this).addClass( "mines" + $(this).countMinesText() ); } $(this).addClass("revealed"); }); } function updateScore() { $('#score').text("Score: " + score); } function isGameOver() { return ($('ul').has('.mine.revealed').length == gameBoardHeight); } function checkGameOver() { if (isGameOver()) { $('#gameOver').show(); } } function updateMinesLeft() { //unflagged mines - revealed mines - flagged not-mines $('#mines').text( "Mines left: " + ($('.mine:not(.flagged)').length - $('.mine.revealed').length - $('li:not(.mine).flagged').length) ); } $.fn.rowScore = function() { return $(this).children('.mine').length; } $.fn.rowAbove = function() { return $(this).parent('ul').prev(); } $.fn.rowBelow = function() { return $(this).parent('ul').next(); } $.fn.column = function() { var x = $(this).getX(); var column = $(''); $('ul').each(function() { column = column.add($(this).children().eq(x)); }); return column; } $.fn.getX = function() { return $(this).index(); } $.fn.getY = function() { return $(this).parent('ul').index(); } $.fn.isMine = function() { return $(this).hasClass("mine"); } $.fn.countMinesAdjacent = function() { var count = 0; $.each($(this).getAdjacentTiles(), function() { if ($(this).isMine()) { count++; } }); return count; } $.fn.getAdjacentTiles = function() { var adjacentTiles = $(''); var x = $(this).getX(); var y = $(this).getY(); if (y > 0) { var tileAbove = $(this).rowAbove().children().eq(x); adjacentTiles = adjacentTiles.add(tileAbove.prev()); adjacentTiles = adjacentTiles.add(tileAbove ); adjacentTiles = adjacentTiles.add(tileAbove.next()); } adjacentTiles = adjacentTiles.add($(this).prev()); adjacentTiles = adjacentTiles.add($(this).next()); if (y < (gameBoardHeight - 1)) { var tileBelow = $(this).rowBelow().children().eq(x); adjacentTiles = adjacentTiles.add(tileBelow.prev()); adjacentTiles = adjacentTiles.add(tileBelow ); adjacentTiles = adjacentTiles.add(tileBelow.next()); } return adjacentTiles; } $.fn.countMinesText = function() { return $(this).countMinesAdjacent().toString().replace("0", ""); } $('input[type="radio"]').on("change", function() { switch ($(this).val()) { case "easy": mineChance = 0.13; break; case "normal": mineChance = 0.2; break; case "hard": mineChance = 0.285; break; } }); $('#gameOver button').on("click", function() { drawGameBoard(); $('#gameOver').hide(); $('button.cancel').hide(); $('button.start').removeAttr('style'); $('#setup').show(); }); $('button.reset').on("click", function() { //prompt user with setup screen $('#setup').show(); $('button.cancel').show(); $('button.start').css('left', '20vmin'); }); $('button.cancel').on("click", function() { //prompt user with setup screen $('#setup').hide(); $('button.cancel').hide(); $('button.start').removeAttr('style'); }); $('button.start').on("click", function() { drawGameBoard(); //reset stats firstClick = true; score = 0; mines = 0; updateScore(); updateMinesLeft(); $('#setup').hide(); }); $.fn.leftClick = function(automated) { if (!automated) automated = false; //don't want first click to be a mine if (firstClick) { var x = $(this).getX(); x = (x >= 1) ? x : 1; x = (x <= gameBoardWidth - 2) ? x : gameBoardWidth - 2; var y = $(this).getY(); y = (y >= 1) ? y : 1; y = (y <= gameBoardHeight - 2) ? y : gameBoardHeight - 2; $('ul').eq(y - 1).children().slice(x - 1).filter(':lt(3)').removeClass("mine"); $('ul').eq(y ).children().slice(x - 1).filter(':lt(3)').removeClass("mine"); $('ul').eq(y + 1).children().slice(x - 1).filter(':lt(3)').removeClass("mine"); firstClick = false; } if ($(this).hasClass("flagged")) { return; } if ($(this).isMine()) { $(this).addClass("revealed"); $(this).html("☠"); //skull & crossbones //game over, or lose a life, or whatever //... } else if (!automated && parseInt($(this).text()) === $(this).getAdjacentTiles().filter('.flagged, .revealed.mine').length) { //already clicked; use middle click reveal functionality //number of flags matches number of adjacent mines $(this).getAdjacentTiles().filter(':not(.flagged, .revealed)').mouseup(); } else { $(this).addClass("revealed"); $(this).text( $(this).countMinesText() ); $(this).addClass("mines" + $(this).countMinesAdjacent()); //if no mines adjacent, cascade! if ($(this).countMinesAdjacent() == 0) { $(this).getAdjacentTiles().filter(':not(.revealed)').mouseup(); } } firstClick = false; } $.fn.middleClick = function() { //number of flags matches number of adjacent mines if (parseInt($(this).text()) === $(this).getAdjacentTiles().filter('.flagged, .revealed.mine').length) { $(this).getAdjacentTiles().filter(':not(.flagged, .revealed)').mouseup(); } } $.fn.rightClick = function() { if (!$(this).hasClass("revealed")) { $(this).toggleClass("flagged"); } clearTimeout(timeout); } $(document).on("contextmenu", "li", function(event) { event.preventDefault(); }); $(document).on("mousedown touchstart", "li", function(event) { var x = $(this).getX(); var y = $(this).getY(); timeout = setTimeout(function() { $('ul:eq(' + y + ') li:eq(' + x + ')').rightClick(); mouseHeld = true; }, clickholdMs); }); $(document).on("mouseleave", "li", function(event) { clearTimeout(timeout); }); $(document).on("mouseup touchend", "li", function(event) { event.preventDefault(); clearTimeout(timeout); if (mouseHeld) { mouseHeld = false; return; } switch (event.which) { case 3: $(this).rightClick(); break; case 2: $(this).middleClick(); break; case 1: $(this).leftClick(); break; default: $(this).leftClick(true); //automated break; } removeClearedRows(); removeClearedColumns(); checkGameOver(); updateScore(); updateMinesLeft(); }); //instantiate the game drawGameBoard(); $('#gameOver').hide(); $('button.cancel').hide(); }); </script> <script> (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-60523398-1', 'auto'); ga('send', 'pageview'); </script> </head> <body> <div id="game"></div> <div id="stats"> <div id="score">Score: 0</div> <div id="mines">Mines left: 0</div> <button class="reset">reset</button> </div> <div id="gameOver"> <h1>game over</h1> <p>a game by max bradbury</p> <p>inspirations include minesweeper, tetris and 2048</p> <p>tell your friends</p> <p>stay in school</p> <button>reset</button> </div> <div id="setup"> <h1>endless mines</h1> <p> <strong>left click</strong> or <strong>tap</strong> to clear a tile. <br> <strong>right click</strong> or <strong>hold</strong> to flag a mine. <br> rows with <strong>exploded mines</strong> cannot be cleared. </p> <h2>Difficulty</h2> <div class="centre"> <input type="radio" name="difficulty" id="difficultyEasy" value="easy"> <label for="difficultyEasy">Easy</label> <input type="radio" name="difficulty" id="difficultyNormal" value="normal" checked> <label for="difficultyNormal">Normal</label> <input type="radio" name="difficulty" id="difficultyHard" value="hard"> <label for="difficultyHard">Hard</label> </div> <button class="start">start</button> <button class="cancel">cancel</button> </div> </body> </html>