endless-mines/index.html

720 lines
16 KiB
HTML
Raw Normal View History

2015-02-28 15:40:37 +00:00
<!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;
2015-03-06 11:30:19 +00:00
-webkit-user-select: none;
2015-02-28 15:40:37 +00:00
}
body {
height: 100vh;
margin: 0 auto;
width: 100vmin;
}
#game {
2015-03-01 15:59:06 +00:00
background-color: #1b1c17;
2015-02-28 15:40:37 +00:00
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 );
}
2015-03-09 19:34:50 +00:00
#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;
2015-02-28 15:40:37 +00:00
}
2015-03-01 19:33:05 +00:00
#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;
2015-03-01 19:33:05 +00:00
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;
}
2015-03-01 19:33:05 +00:00
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;
2015-03-01 19:33:05 +00:00
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 );
}
2015-03-09 19:34:50 +00:00
button.cancel {
right: 20vmin;
left: auto;
}
2015-03-01 19:33:05 +00:00
h1, h2 {
text-align: center;
}
h1 {
color: #a4f4b4;
font-size: 12vmin;
2015-03-09 19:34:50 +00:00
margin-top: 6vmin;
margin-bottom: 6vmin;
2015-03-01 19:33:05 +00:00
text-shadow: 1vmin 1vmin 0 #fe7ac6; /* x y blur-radius colour */
}
2015-02-28 15:40:37 +00:00
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;
2015-02-28 15:40:37 +00:00
text-align: center;
vertical-align: middle;
float: left;
height: 8vmin;
width: 8vmin;
2015-03-01 15:59:06 +00:00
background-color: #454e52;
2015-02-28 15:40:37 +00:00
border-radius: 2vmin;
cursor: default;
}
li.revealed {
2015-03-01 15:59:06 +00:00
background-color: #1b1c17;
2015-02-28 15:40:37 +00:00
}
li:not(.revealed):hover {
background-color: #a0a9af;
2015-02-28 15:40:37 +00:00
}
li.flagged {
background-color: #b5fe52;
}
li.flagged:hover {
background-color: #c6fe7a;
}
li.mine.revealed {
background-color: #d23000;
2015-03-09 13:02:27 +00:00
color: #000;
2015-02-28 15:40:37 +00:00
}
li.mines1 {
color: #c6fe7a;
2015-02-28 15:40:37 +00:00
}
li.mines2 {
color: #7ac5fe;
2015-02-28 15:40:37 +00:00
}
li.mines3 {
color: #fe7ac6;
2015-02-28 15:40:37 +00:00
}
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;
}
2015-02-28 15:40:37 +00:00
</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;
2015-03-06 10:41:53 +00:00
var clickholdMs = 200;
var timeout; //hold timer
var mouseHeld = false;
2015-02-28 15:40:37 +00:00
$(document).ready(function() {
2015-02-28 15:40:37 +00:00
function drawGameBoard() {
$('#game').html("");
for (var i = 0; i < gameBoardHeight; i++) {
2015-02-28 15:40:37 +00:00
$('#game').append("<ul></ul>");
}
$('#game ul').each(function() {
for (var i = 0; i < gameBoardWidth; i++) {
$(this).append(newTile());
2015-02-28 15:40:37 +00:00
}
});
}
function newTile() {
if (Math.random() < mineChance) {
return '<li class="mine"></li>';
} else {
return '<li></li>';
}
}
2015-02-28 15:40:37 +00:00
$.fn.checkRow = function() {
//unclicked tiles
if ($(this).children('li:not(.revealed):not(.flagged)').length > 0) {
return false;
}
2015-02-28 15:40:37 +00:00
//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;
}
2015-02-28 15:40:37 +00:00
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() {
2015-03-06 11:15:32 +00:00
var rowsToRemove = $('#game ul:not(.removing)').filter(function() {
return $(this).checkRow();
});
var numRowsToRemove = rowsToRemove.length;
2015-03-06 11:30:19 +00:00
if (numRowsToRemove == 0) return;
2015-03-06 11:15:32 +00:00
rowsToRemove.addClass("removing");
rowsToRemove.each(function() {
2015-03-01 13:04:59 +00:00
score += $(this).children('.mine').length;
});
2015-03-01 13:04:59 +00:00
rowsToRemove.slideUp("slow", function() {
$(this).remove();
//add new row on bottom
2015-03-01 13:04:59 +00:00
$('#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() {
var mineCount = $(this).countMinesText();
$(this).text(mineCount);
//remove "mines1" etc
$(this).attr(
'class',
$(this).attr('class').replace(
/mines[0-9]/, "mines" + mineCount
)
);
});
}
function updateScore() {
$('#score').text("Score: " + score);
}
2015-03-01 19:33:05 +00:00
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)
);
}
2015-02-28 15:40:37 +00:00
$.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;
}
2015-02-28 15:40:37 +00:00
$.fn.getX = function() {
return $(this).index();
}
$.fn.getY = function() {
return $(this).parent('ul').index();
}
$.fn.isMine = function() {
return $(this).hasClass("mine");
2015-02-28 15:40:37 +00:00
}
$.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();
2015-02-28 15:40:37 +00:00
if (y > 0) {
var tileAbove = $(this).rowAbove().children().eq(x);
2015-02-28 15:40:37 +00:00
adjacentTiles = adjacentTiles.add(tileAbove.prev());
adjacentTiles = adjacentTiles.add(tileAbove );
adjacentTiles = adjacentTiles.add(tileAbove.next());
2015-02-28 15:40:37 +00:00
}
adjacentTiles = adjacentTiles.add($(this).prev());
adjacentTiles = adjacentTiles.add($(this).next());
2015-02-28 15:40:37 +00:00
if (y < (gameBoardHeight - 1)) {
var tileBelow = $(this).rowBelow().children().eq(x);
2015-02-28 15:40:37 +00:00
adjacentTiles = adjacentTiles.add(tileBelow.prev());
adjacentTiles = adjacentTiles.add(tileBelow );
adjacentTiles = adjacentTiles.add(tileBelow.next());
2015-02-28 15:40:37 +00:00
}
return adjacentTiles;
}
$.fn.countMinesText = function() {
return $(this).countMinesAdjacent().toString().replace("0", "");
}
2015-03-01 19:33:05 +00:00
$('input[type="radio"]').on("change", function() {
switch ($(this).val()) {
case "easy":
mineChance = 0.13;
2015-03-01 19:33:05 +00:00
break;
case "normal":
mineChance = 0.2;
break;
case "hard":
mineChance = 0.285;
break;
}
});
$('#gameOver button').on("click", function() {
drawGameBoard();
$('#gameOver').hide();
2015-03-09 19:34:50 +00:00
$('button.cancel').hide();
$('button.start').removeAttr('style');
2015-03-01 19:33:05 +00:00
$('#setup').show();
});
2015-03-09 19:34:50 +00:00
$('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();
2015-03-01 19:33:05 +00:00
//reset stats
firstClick = true;
2015-03-01 19:33:05 +00:00
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");
2015-03-09 13:02:27 +00:00
$(this).html("&#9760;"); //skull & crossbones
2015-02-28 15:40:37 +00:00
//game over, or lose a life, or whatever
//...
2015-03-06 10:41:53 +00:00
} else if (!automated && parseInt($(this).text()) === $(this).getAdjacentTiles().filter('.flagged, .revealed.mine').length) {
//already clicked; use middle click reveal functionality
2015-03-06 10:41:53 +00:00
//number of flags matches number of adjacent mines
$(this).getAdjacentTiles().filter(':not(.flagged, .revealed)').mouseup();
2015-02-28 15:40:37 +00:00
} else {
$(this).addClass("revealed");
2015-02-28 15:40:37 +00:00
$(this).text(
$(this).countMinesText()
);
$(this).addClass("mines" + $(this).countMinesAdjacent());
//if no mines adjacent, cascade!
if ($(this).countMinesAdjacent() == 0) {
2015-03-06 10:41:53 +00:00
$(this).getAdjacentTiles().filter(':not(.revealed)').mouseup();
}
2015-02-28 15:40:37 +00:00
}
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();
2015-02-28 15:40:37 +00:00
}
}
$.fn.rightClick = function() {
if (!$(this).hasClass("revealed")) {
$(this).toggleClass("flagged");
}
2015-03-06 10:41:53 +00:00
clearTimeout(timeout);
2015-02-28 15:40:37 +00:00
}
$(document).on("contextmenu", "li", function(event) {
event.preventDefault();
});
2015-03-06 10:41:53 +00:00
2015-03-06 11:15:32 +00:00
$(document).on("mousedown touchstart", "li", function(event) {
2015-03-06 10:41:53 +00:00
var x = $(this).getX();
var y = $(this).getY();
timeout = setTimeout(function() {
$('ul:eq(' + y + ') li:eq(' + x + ')').rightClick();
2015-03-06 10:41:53 +00:00
mouseHeld = true;
}, clickholdMs);
});
$(document).on("mouseleave", "li", function(event) {
clearTimeout(timeout);
});
2015-03-06 11:15:32 +00:00
$(document).on("mouseup touchend", "li", function(event) {
event.preventDefault();
2015-03-06 10:41:53 +00:00
clearTimeout(timeout);
if (mouseHeld) {
mouseHeld = false;
return;
}
2015-02-28 15:40:37 +00:00
switch (event.which) {
case 3:
2015-02-28 15:40:37 +00:00
$(this).rightClick();
break;
case 2:
$(this).middleClick();
break;
case 1:
2015-02-28 15:40:37 +00:00
$(this).leftClick();
break;
default:
$(this).leftClick(true); //automated
break;
2015-02-28 15:40:37 +00:00
}
removeClearedRows();
//removeClearedColumns();
2015-03-01 19:33:05 +00:00
checkGameOver();
updateScore();
updateMinesLeft();
2015-02-28 15:40:37 +00:00
});
2015-03-01 19:33:05 +00:00
//instantiate the game
2015-02-28 15:40:37 +00:00
drawGameBoard();
2015-03-01 19:33:05 +00:00
$('#gameOver').hide();
2015-03-09 19:34:50 +00:00
$('button.cancel').hide();
2015-02-28 15:40:37 +00:00
});
</script>
2015-03-09 12:28:17 +00:00
<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),
2015-03-09 12:28:17 +00:00
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>
2015-02-28 15:40:37 +00:00
</head>
<body>
<div id="game"></div>
<div id="stats">
<div id="score">Score: 0</div>
<div id="mines">Mines left: 0</div>
2015-03-09 19:34:50 +00:00
<button class="reset">reset</button>
</div>
2015-03-01 19:33:05 +00:00
<div id="gameOver">
<h1>game over</h1>
2015-03-09 19:34:50 +00:00
<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>
2015-03-01 19:33:05 +00:00
<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>
2015-03-01 19:33:05 +00:00
<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>
2015-03-09 19:34:50 +00:00
<button class="start">start</button>
<button class="cancel">cancel</button>
2015-03-01 19:33:05 +00:00
</div>
2015-02-28 15:40:37 +00:00
</body>
</html>