diff --git a/animations/click.js b/animations/click.js
new file mode 100644
index 0000000..cac53ec
--- /dev/null
+++ b/animations/click.js
@@ -0,0 +1,159 @@
+window.requestAnimFrame = (function (callback) {
+ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || function (callback) {
+ window.setTimeout(callback, 1000 / 60);
+ }
+})();
+
+const overlayCanvas = document.getElementById('minesweeper-overlay');
+const overlayCtx = overlayCanvas.getContext('2d');
+
+const particlesPerExplosion = 50;
+const particlesMinSpeed = 3;
+const particlesMaxSpeed = 6;
+const particlesMinSize = 3;
+const particlesMaxSize = 6;
+const explosions = [];
+
+let fps = 60;
+const interval = 1000 / fps;
+
+let now, delta;
+let then = Date.now();
+
+// Optimization for mobile devices
+if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
+ fps = 29;
+}
+
+// Draw
+function draw() {
+ // Loop
+ requestAnimationFrame(draw);
+
+ // Set NOW and DELTA
+ now = Date.now();
+ delta = now - then;
+
+ overlayCtx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
+
+ // New frame
+ if (delta > interval) {
+
+ // Update THEN
+ then = now - (delta % interval);
+
+ // Our animation
+ drawExplosion();
+
+ }
+
+}
+
+// Draw explosion(s)
+function drawExplosion() {
+
+ if (explosions.length === 0) {
+ return;
+ }
+
+ for (let i = 0; i < explosions.length; i++) {
+
+ const explosion = explosions[i];
+ const particles = explosion.particles;
+
+ if (particles.length === 0) {
+ explosions.splice(i, 1);
+ return;
+ }
+
+ const particlesAfterRemoval = particles.slice();
+ for (let ii = 0; ii < particles.length; ii++) {
+
+ const particle = particles[ii];
+
+ // Check particle size
+ // If 0, remove
+ if (particle.size <= 0) {
+ particlesAfterRemoval.splice(ii, 1);
+ continue;
+ }
+
+ overlayCtx.beginPath();
+ overlayCtx.arc(particle.x, particle.y, particle.size, Math.PI * 2, 0, false);
+ overlayCtx.closePath();
+ overlayCtx.fillStyle = 'rgb(' + particle.r + ',' + particle.g + ',' + particle.b + ')';
+ overlayCtx.fill();
+
+ // Update
+ particle.x += particle.xv;
+ particle.y += particle.yv;
+ particle.size -= .1;
+ }
+
+ explosion.particles = particlesAfterRemoval;
+
+ }
+
+}
+
+// Clicked
+function clicked(e) {
+
+ let xPos, yPos;
+
+ if (e.offsetX) {
+ xPos = e.offsetX;
+ yPos = e.offsetY;
+ } else if (e.layerX) {
+ xPos = e.layerX;
+ yPos = e.layerY;
+ }
+
+ explosions.push(
+ new explosion(xPos, yPos)
+ );
+
+}
+
+// Explosion
+function explosion(x, y) {
+
+ this.particles = [];
+
+ for (let i = 0; i < particlesPerExplosion; i++) {
+ this.particles.push(
+ new particle(x, y)
+ );
+ }
+
+}
+
+// Particle
+function particle(x, y) {
+ this.x = x;
+ this.y = y;
+ this.xv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
+ this.yv = randInt(particlesMinSpeed, particlesMaxSpeed, false);
+ this.size = randInt(particlesMinSize, particlesMaxSize, true);
+ this.r = randInt(113, 222);
+ this.g = '00';
+ this.b = randInt(105, 255);
+}
+
+// Returns an random integer, positive or negative
+// between the given value
+function randInt(min, max, positive) {
+
+ let num;
+ if (positive === false) {
+ num = Math.floor(Math.random() * max) - min;
+ num *= Math.floor(Math.random() * 2) === 1 ? 1 : -1;
+ } else {
+ num = Math.floor(Math.random() * max) + min;
+ }
+
+ return num;
+
+}
+
+draw();
\ No newline at end of file
diff --git a/animations/victory.js b/animations/victory.js
new file mode 100644
index 0000000..24c58d6
--- /dev/null
+++ b/animations/victory.js
@@ -0,0 +1,66 @@
+const overlay2Canvas = document.getElementById('minesweeper-overlay2');
+const overlay2Ctx = overlay2Canvas.getContext('2d');
+
+let W = window.innerWidth,
+ H = window.innerHeight,
+ circles = [];
+
+overlay2Canvas.width = W;
+overlay2Canvas.height = H;
+
+//Random Circles creator
+function create() {
+
+ //Place the circles at the center
+
+ this.x = W/2;
+ this.y = H/2;
+
+
+ //Random radius between 2 and 6
+ this.radius = 2 + Math.random()*3;
+
+ //Random velocities
+ this.vx = -5 + Math.random()*10;
+ this.vy = -5 + Math.random()*10;
+
+ //Random colors
+ this.r = Math.round(Math.random())*255;
+ this.g = Math.round(Math.random())*255;
+ this.b = Math.round(Math.random())*255;
+}
+
+for (var i = 0; i < 500; i++) {
+ circles.push(new create());
+}
+
+function drawVictory() {
+
+ //Fill overlay2Canvas with black color
+ overlay2Ctx.globalCompositeOperation = "source-over";
+ overlay2Ctx.fillStyle = "rgba(0,0,0,0.15)";
+ overlay2Ctx.fillRect(0, 0, W, H);
+
+ //Fill the overlay2Canvas with circles
+ for(var j = 0; j < circles.length; j++){
+ var c = circles[j];
+
+ //Create the circles
+ overlay2Ctx.beginPath();
+ overlay2Ctx.arc(c.x, c.y, c.radius, 0, Math.PI*2, false);
+ overlay2Ctx.fillStyle = "rgba("+c.r+", "+c.g+", "+c.b+", 0.5)";
+ overlay2Ctx.fill();
+
+ c.x += c.vx;
+ c.y += c.vy;
+ c.radius -= .02;
+
+ if(c.radius < 0)
+ circles[j] = new create();
+ }
+}
+
+function animate() {
+ requestAnimFrame(animate);
+ drawVictory();
+}
diff --git a/icons/flag.svg b/icons/flag.svg
new file mode 100644
index 0000000..de11b1f
--- /dev/null
+++ b/icons/flag.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..9f4cee8
--- /dev/null
+++ b/index.html
@@ -0,0 +1,27 @@
+
+
+
+
+ Title
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/canvg.min.js b/lib/canvg.min.js
new file mode 100644
index 0000000..6d6e7ea
--- /dev/null
+++ b/lib/canvg.min.js
@@ -0,0 +1 @@
+!function(t,e){"use strict";"undefined"!=typeof define&&define.amd?define("canvgModule",["rgbcolor","stackblur"],e):"undefined"!=typeof module&&module.exports?module.exports=e(require("rgbcolor"),require("stackblur")):t.canvg=e(t.RGBColor,t.stackBlur)}("undefined"!=typeof window?window:this,function(u,c){var f,m,p,w="undefined"!=typeof module&&module.exports,d=800,y=600;if(w&&"undefined"==typeof window){var t=require("jsdom").jsdom;f=t().defaultView}else f=window;function v(){return w?new p:document.createElement("canvas")}f.DOMParser||(f.DOMParser=require("xmldom").DOMParser);var g,x=function(t,e,i){if(null!=t||null!=e||null!=i){var n=function(t){var A={opts:t,FRAMERATE:30,MAX_VIRTUAL_PIXELS:3e4,log:function(t){}};1==A.opts.log&&"undefined"!=typeof console&&(A.log=function(t){console.log(t)});A.init=function(t){var e=0;A.UniqueId=function(){return"canvg"+ ++e},A.Definitions={},A.Styles={},A.StylesSpecificity={},A.Animations=[],A.Images=[],A.ctx=t,A.ViewPort=new function(){this.viewPorts=[],this.Clear=function(){this.viewPorts=[]},this.SetCurrent=function(t,e){this.viewPorts.push({width:t,height:e})},this.RemoveCurrent=function(){this.viewPorts.pop()},this.Current=function(){return this.viewPorts[this.viewPorts.length-1]},this.width=function(){return this.Current().width},this.height=function(){return this.Current().height},this.ComputeSize=function(t){return null!=t&&"number"==typeof t?t:"x"==t?this.width():"y"==t?this.height():Math.sqrt(Math.pow(this.width(),2)+Math.pow(this.height(),2))/Math.sqrt(2)}}},A.init(),A.ImagesLoaded=function(){for(var t=0;t]*>/,"");var t=new ActiveXObject("Microsoft.XMLDOM");return t.async="false",t.loadXML(e),t}try{var n=new f.DOMParser;return n.parseFromString(e,"image/svg+xml")}catch(t){return(n=new f.DOMParser).parseFromString(e,"text/xml")}},A.Property=function(t,e){this.name=t,this.value=e},A.Property.prototype.getValue=function(){return this.value},A.Property.prototype.hasValue=function(){return null!=this.value&&""!==this.value},A.Property.prototype.numValue=function(){if(!this.hasValue())return 0;var t=parseFloat(this.value);return(this.value+"").match(/%$/)&&(t/=100),t},A.Property.prototype.valueOrDefault=function(t){return this.hasValue()?this.value:t},A.Property.prototype.numValueOrDefault=function(t){return this.hasValue()?this.numValue():t},A.Property.prototype.addOpacity=function(t){var e=this.value;if(null!=t.value&&""!=t.value&&"string"==typeof this.value){var i=new u(this.value);i.ok&&(e="rgba("+i.r+", "+i.g+", "+i.b+", "+t.numValue()+")")}return new A.Property(this.name,e)},A.Property.prototype.getDefinition=function(){var t=this.value.match(/#([^\)'"]+)/);return t&&(t=t[1]),t||(t=this.value),A.Definitions[t]},A.Property.prototype.isUrlDefinition=function(){return 0==this.value.indexOf("url(")},A.Property.prototype.getFillStyleDefinition=function(t,e){var i=this.getDefinition();if(null!=i&&i.createGradient)return i.createGradient(A.ctx,t,e);if(null!=i&&i.createPattern){if(i.getHrefAttribute().hasValue()){var n=i.attribute("patternTransform");i=i.getHrefAttribute().getDefinition(),n.hasValue()&&(i.attribute("patternTransform",!0).value=n.value)}return i.createPattern(A.ctx,t)}return null},A.Property.prototype.getDPI=function(t){return 96},A.Property.prototype.getEM=function(t){var e=12,i=new A.Property("fontSize",A.Font.Parse(A.ctx.font).fontSize);return i.hasValue()&&(e=i.toPixels(t)),e},A.Property.prototype.getUnits=function(){var t=this.value+"";return t.replace(/[0-9\.\-]/g,"")},A.Property.prototype.toPixels=function(t,e){if(!this.hasValue())return 0;var i=this.value+"";if(i.match(/em$/))return this.numValue()*this.getEM(t);if(i.match(/ex$/))return this.numValue()*this.getEM(t)/2;if(i.match(/px$/))return this.numValue();if(i.match(/pt$/))return this.numValue()*this.getDPI(t)*(1/72);if(i.match(/pc$/))return 15*this.numValue();if(i.match(/cm$/))return this.numValue()*this.getDPI(t)/2.54;if(i.match(/mm$/))return this.numValue()*this.getDPI(t)/25.4;if(i.match(/in$/))return this.numValue()*this.getDPI(t);if(i.match(/%$/))return this.numValue()*A.ViewPort.ComputeSize(t);var n=this.numValue();return e&&n<1?n*A.ViewPort.ComputeSize(t):n},A.Property.prototype.toMilliseconds=function(){if(!this.hasValue())return 0;var t=this.value+"";return t.match(/s$/)?1e3*this.numValue():(t.match(/ms$/),this.numValue())},A.Property.prototype.toRadians=function(){if(!this.hasValue())return 0;var t=this.value+"";return t.match(/deg$/)?this.numValue()*(Math.PI/180):t.match(/grad$/)?this.numValue()*(Math.PI/200):t.match(/rad$/)?this.numValue():this.numValue()*(Math.PI/180)};var e={baseline:"alphabetic","before-edge":"top","text-before-edge":"top",middle:"middle",central:"middle","after-edge":"bottom","text-after-edge":"bottom",ideographic:"ideographic",alphabetic:"alphabetic",hanging:"hanging",mathematical:"alphabetic"};return A.Property.prototype.toTextBaseline=function(){return this.hasValue()?e[this.value]:null},A.Font=new function(){this.Styles="normal|italic|oblique|inherit",this.Variants="normal|small-caps|inherit",this.Weights="normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900|inherit",this.CreateFont=function(t,e,i,n,s,a){var r=null!=a?this.Parse(a):this.CreateFont("","","","","",A.ctx.font);if(s=s||r.fontFamily){var o=s.trim();'"'!==o[0]&&0this.x2&&(this.x2=t)),null!=e&&((isNaN(this.y1)||isNaN(this.y2))&&(this.y1=e,this.y2=e),ethis.y2&&(this.y2=e))},this.addX=function(t){this.addPoint(t,null)},this.addY=function(t){this.addPoint(null,t)},this.addBoundingBox=function(t){this.addPoint(t.x1,t.y1),this.addPoint(t.x2,t.y2)},this.addQuadraticCurve=function(t,e,i,n,s,a){var r=t+2/3*(i-t),o=e+2/3*(n-e),l=r+1/3*(s-t),h=o+1/3*(a-e);this.addBezierCurve(t,e,r,l,o,h,s,a)},this.addBezierCurve=function(t,e,i,n,s,a,r,o){var l=[t,e],h=[i,n],u=[s,a],c=[r,o];this.addPoint(l[0],l[1]),this.addPoint(c[0],c[1]);for(var f=0;f<=1;f++){var m=function(t){return Math.pow(1-t,3)*l[f]+3*Math.pow(1-t,2)*t*h[f]+3*(1-t)*Math.pow(t,2)*u[f]+Math.pow(t,3)*c[f]},p=6*l[f]-12*h[f]+6*u[f],d=-3*l[f]+9*h[f]-9*u[f]+3*c[f],y=3*h[f]-3*l[f];if(0!=d){var v=Math.pow(p,2)-4*y*d;if(!(v<0)){var g=(-p+Math.sqrt(v))/(2*d);0=this.tokens.length-1},this.isCommandOrEnd=function(){return!!this.isEnd()||null!=this.tokens[this.i+1].match(/^[A-Za-z]$/)},this.isRelativeCommand=function(){switch(this.command){case"m":case"l":case"h":case"v":case"c":case"s":case"q":case"t":case"a":case"z":return!0}return!1},this.getToken=function(){return this.i++,this.tokens[this.i]},this.getScalar=function(){return parseFloat(this.getToken())},this.nextCommand=function(){this.previousCommand=this.command,this.command=this.getToken()},this.getPoint=function(){var t=new A.Point(this.getScalar(),this.getScalar());return this.makeAbsolute(t)},this.getAsControlPoint=function(){var t=this.getPoint();return this.control=t},this.getAsCurrentPoint=function(){var t=this.getPoint();return this.current=t},this.getReflectedControlPoint=function(){return"c"!=this.previousCommand.toLowerCase()&&"s"!=this.previousCommand.toLowerCase()&&"q"!=this.previousCommand.toLowerCase()&&"t"!=this.previousCommand.toLowerCase()?this.current:new A.Point(2*this.current.x-this.control.x,2*this.current.y-this.control.y)},this.makeAbsolute=function(t){return this.isRelativeCommand()&&(t.x+=this.current.x,t.y+=this.current.y),t},this.addMarker=function(t,e,i){null!=i&&0this.maxDuration){if("indefinite"==this.attribute("repeatCount").value||"indefinite"==this.attribute("repeatDur").value)this.duration=0;else if("freeze"!=this.attribute("fill").valueOrDefault("remove")||this.frozen){if("remove"==this.attribute("fill").valueOrDefault("remove")&&!this.removed)return this.removed=!0,this.getProperty().value=this.parent.animationFrozen?this.parent.animationFrozenValue:this.initialValue,!0}else this.frozen=!0,this.parent.animationFrozen=!0,this.parent.animationFrozenValue=this.getProperty().value;return!1}this.duration=this.duration+t;var e=!1;if(this.begin~\.\[:]+)/g,a=/(\.[^\s\+>~\.\[:]+)/g,r=/(::[^\s\+>~\.\[:]+|:first-line|:first-letter|:before|:after)/gi,o=/(:[\w-]+\([^\)]*\))/gi,l=/(:[^\s\+>~\.\[:]+)/g,h=/([^\s\+>~\.\[:]+)/g;function B(n){var s=[0,0,0],t=function(t,e){var i=n.match(t);null!=i&&(s[e]+=i.length,n=n.replace(t," "))};return n=(n=n.replace(/:not\(([^\)]*)\)/g," $1 ")).replace(/{[\s\S]*/gm," "),t(e,1),t(i,0),t(a,1),t(r,2),t(o,1),t(l,1),n=(n=n.replace(/[\*\s\+>~]/g," ")).replace(/[#\.]/g," "),t(h,2),s.join("")}return"undefined"!=typeof CanvasRenderingContext2D&&(CanvasRenderingContext2D.prototype.drawSvg=function(t,e,i,n,s,a){var r={ignoreMouse:!0,ignoreAnimation:!0,ignoreDimensions:!0,ignoreClear:!0,offsetX:e,offsetY:i,scaleWidth:n,scaleHeight:s};for(var o in a)a.hasOwnProperty(o)&&(r[o]=a[o]);x(this.canvas,t,r)}),x});
\ No newline at end of file
diff --git a/minesweeper.js b/minesweeper.js
new file mode 100644
index 0000000..44249d4
--- /dev/null
+++ b/minesweeper.js
@@ -0,0 +1,333 @@
+const canvas = document.getElementById('minesweeper-game');
+const ctx = canvas.getContext('2d');
+
+const fieldSize = {x: 21, y: 13};
+let tileSize;
+const bombCount = 30;
+const field = [];
+let gameOver = false;
+let victory = false;
+const scaleFactor = .5;
+let isFirstClick = true;
+
+ctx.scale(canvas.width / fieldSize.x * scaleFactor, canvas.height / fieldSize.y * scaleFactor);
+
+/**
+ * Initializes game by creating the game field and setting bombs
+ */
+function initGame() {
+ for(let x = 0; x < fieldSize.x; x++) {
+ field.push([]);
+ for(let y = 0; y < fieldSize.y; y++) {
+ field[x].push({tileValue: 0, clicked: false, flagged: false});
+ }
+ }
+
+ scaleCanvas();
+}
+
+function initBombs(startX, startY) {
+ for(let i = 0; i < bombCount; i++) {
+ const ranX = Math.floor(Math.random() * fieldSize.x);
+ const ranY = Math.floor(Math.random() * fieldSize.y);
+
+ if (ranX === startX || ranX === startX - 1 || ranX === startX + 1 || ranY === startY || ranY === startY - 1 || ranY === startY + 1 || field[ranX][ranY].tileValue === true) {
+ i--;
+ continue;
+ }
+
+ field[ranX][ranY].tileValue = true;
+ }
+
+ for(let x = 0; x < fieldSize.x; x++) {
+ for (let y = 0; y < fieldSize.y; y++) {
+ if (field[x][y].tileValue !== true) {
+ field[x][y].tileValue = countBombs(x, y);
+ }
+ }
+ }
+}
+
+function drawGrid() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ for(let x = 0; x < fieldSize.x; x++) {
+ for (let y = 0; y < fieldSize.y; y++) {
+ ctx.strokeRect(x * tileSize.x, y * tileSize.y, tileSize.x, tileSize.y);
+ if(field[x][y].clicked)
+ drawText(x, y);
+ }
+ }
+}
+
+function getSurroundingTiles(x, y) {
+ const tiles = {};
+ if(x > 0) {
+ tiles["left"] = { tileValue: field[x - 1][y], x: x - 1, y: y };
+ if(y > 0) {
+ tiles["left-top"] = { tileValue: field[x - 1][y - 1], x: x - 1, y: y - 1 };
+ }
+ if(y < fieldSize.y - 1) {
+ tiles["left-bottom"] = { tileValue: field[x - 1][y + 1], x: x - 1, y: y + 1 };
+ }
+ }
+ if(x < fieldSize.x - 1) {
+ tiles["right"] = { tileValue: field[x + 1][y], x: x + 1, y: y};
+ if(y > 0)
+ tiles["right-top"] = { tileValue: field[x + 1][y - 1], x: x + 1, y: y - 1 };
+ if(y < fieldSize.y - 1)
+ tiles["right-bottom"] = { tileValue: field[x + 1][y + 1], x: x + 1, y: y + 1 };
+ }
+ if(y > 0)
+ tiles["top"] = { tileValue: field[x][y - 1], x: x, y: y - 1 };
+ if(y < fieldSize.y - 1)
+ tiles["bottom"] = { tileValue: field[x][y + 1], x: x, y: y + 1 };
+ return tiles;
+}
+
+function countBombs(x, y) {
+ const tiles = getSurroundingTiles(x, y);
+ return tiles.count(true);
+}
+
+function countFlaggedBombs(x, y) {
+ const tiles = getSurroundingTiles(x, y);
+ return tiles.countFlagged(true);
+}
+
+Object.prototype.count = function (val) {
+ let counter = 0;
+ for(let el in this) {
+ if(this.hasOwnProperty(el)) {
+ if (val === this[el].tileValue.tileValue) {
+ counter++;
+ }
+ }
+ }
+ return counter;
+};
+
+Object.prototype.countFlagged = function (val) {
+ let counter = 0;
+ for(let el in this) {
+ if(this.hasOwnProperty(el)) {
+ if(this[el].tileValue.flagged === val) {
+ counter++;
+ }
+ }
+ }
+ return counter;
+};
+
+function tileClickEvent(x, y) {
+ if(gameOver)
+ return;
+ uncoverTile(x, y);
+ if(!field[x][y].flagged && field[x][y].tileValue === true) {
+ gameOver = true;
+ gameOverEvent();
+ }
+}
+
+function tileDoubleClick(x, y) {
+ if(gameOver)
+ return;
+ if(field[x][y].clicked && !field[x][y].flagged && countFlaggedBombs(x, y) === field[x][y].tileValue) {
+ uncoverSurroundings(x, y);
+ }
+}
+
+function tileFlag(x, y) {
+ if(gameOver)
+ return;
+ if(field[x][y].clicked && !field[x][y].flagged)
+ return;
+ field[x][y].flagged = !field[x][y].flagged;
+ field[x][y].clicked = field[x][y].flagged;
+ ctx.clearRect(x * tileSize.x + (1 / scaleFactor), y * tileSize.y + (1 / scaleFactor), tileSize.x - (2 / scaleFactor), tileSize.y - (2 / scaleFactor));
+ drawText(x, y);
+}
+
+
+overlay2Canvas.addEventListener("click", (e) => {
+ const pos = getPositon(e);
+
+ if(isFirstClick) {
+ initBombs(pos.x, pos.y);
+ isFirstClick = false;
+ }
+
+ tileClickEvent(pos.x, pos.y);
+
+ victoryCheck();
+
+ clicked(e);
+});
+
+overlay2Canvas.addEventListener("dblclick", (e) => {
+ const pos = getPositon(e);
+
+ tileDoubleClick(pos.x, pos.y);
+
+ victoryCheck();
+});
+
+overlay2Canvas.addEventListener("contextmenu", (e) => {
+ e.preventDefault();
+ console.log(e);
+
+ const pos = getPositon(e);
+
+ tileFlag(pos.x, pos.y);
+});
+
+function getPositon(e) {
+ const x = e.x - canvas.offsetLeft;
+ const y = e.y - canvas.offsetTop;
+ const fieldX = Math.floor(x / tileSize.x);
+ const fieldY = Math.floor(y / tileSize.y);
+
+ return {x: fieldX, y: fieldY};
+}
+
+function scaleCanvas() {
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+ overlayCanvas.width = window.innerWidth;
+ overlayCanvas.height = window.innerHeight;
+
+ W = window.innerWidth;
+ H = window.innerHeight;
+
+ // tileSize = {x: canvas.width / fieldSize.x, y: canvas.height / fieldSize.y};
+ tileSize = {x: 100, y: 100};
+ drawGrid();
+ if(gameOver) {
+ gameOverEvent();
+ }
+}
+
+function uncoverTile(x, y) {
+ if(field[x][y].clicked || field[x][y].flagged) {
+ return;
+ }
+ field[x][y].clicked = true;
+ drawText(x, y);
+ if(field[x][y].tileValue === true) {
+ gameOverEvent();
+ }
+ if(field[x][y].tileValue === 0) {
+ uncoverSurroundings(x, y);
+ }
+}
+
+function uncoverSurroundings(x, y) {
+ const surrounding = getSurroundingTiles(x, y);
+ for(let tile in surrounding) {
+ if(surrounding.hasOwnProperty(tile)) {
+ uncoverTile(surrounding[tile].x, surrounding[tile].y);
+ }
+ }
+}
+
+const colors = {
+ 1: "blue",
+ 2: "green",
+ 3: "red",
+ 4: "purple",
+ 5: "yellow",
+ 6: "pink"
+};
+
+function drawText(x, y) {
+ ctx.font = "bold 50px Roboto";
+ ctx.textAlign = "center";
+ if(!field[x][y].flagged && field[x][y].clicked) {
+ ctx.fillStyle = "#ddd";
+ ctx.fillRect(x * tileSize.x + 1, y * tileSize.y + 1, tileSize.x - 2, tileSize.y - 2);
+ if (field[x][y].tileValue !== 0) {
+ ctx.fillStyle = colors[field[x][y].tileValue];
+ ctx.fillText(field[x][y].tileValue, (x + .5) * tileSize.x, (y + .5) * tileSize.y + 15);
+ }
+ } else if(field[x][y].flagged) {
+ ctx.fillStyle = "red";
+ ctx.fillRect(x * tileSize.x + 5 / scaleFactor, y * tileSize.y + 5 / scaleFactor, tileSize.x - 10 / scaleFactor, tileSize.y - 10 / scaleFactor);
+
+ ctx.font = "bold 50px FontAwesome";
+ ctx.fillStyle = "white";
+ ctx.fillText("", (x + .5) * tileSize.x, (y + .5) * tileSize.y + 15);
+ }
+}
+
+window.addEventListener("resize", () => {
+ scaleCanvas();
+});
+
+function gameOverEvent() {
+ console.log("Game Over");
+ ctx.fillStyle = "orange";
+ ctx.fillText("Game Over", canvas.width / 2, canvas.height / 2);
+ animateBackground({r: 0, g: 0, b: 0, a: 0}, 0, 0, canvas.width, canvas.height, .75, new Date().getTime(), 2);
+ animateText("Game Over", canvas.width / 2, canvas.height / 2, 0, 100, new Date().getTime(), 200);
+}
+
+function animateText(text, x, y, curFontSize, finalFontSize, startTime, speed) {
+ const time = (new Date()).getTime() - startTime;
+
+ const newFontSize = speed * time / 1000;
+
+ if(newFontSize < finalFontSize) {
+ curFontSize = newFontSize;
+ } else {
+ curFontSize = finalFontSize;
+ }
+
+ // drawGrid();
+ ctx.fillStyle = "orange";
+ ctx.font = "bold " + curFontSize + "px Roboto";
+ ctx.fillText(text, x, y);
+
+ requestAnimFrame(function () {
+ animateText(text, x, y, curFontSize, finalFontSize, startTime, speed);
+ })
+}
+
+function animateBackground(color, x, y, width, height, maxOpacity, startTime, speed) {
+ const time = (new Date()).getTime() - startTime;
+
+ const newOpacity = speed * time / 1000;
+
+ if(newOpacity <= maxOpacity) color.a = newOpacity;
+
+ drawGrid();
+ ctx.fillStyle = "rgba(" + color.r + "," + color.g + "," + color.b + "," + color.a + ")";
+ ctx.fillRect(x, y, width, height);
+
+ requestAnimFrame(function () {
+ animateBackground(color, x, y, width, height, maxOpacity, startTime, speed);
+ });
+}
+
+function countClickedTiles() {
+ let count = 0;
+ for(let x = 0; x < fieldSize.x; x++) {
+ for(let y = 0; y < fieldSize.y; y++) {
+ if(field[x][y].clicked && !field[x][y].flagged)
+ count++;
+ }
+ }
+ return count;
+}
+
+function victoryCheck() {
+ if(!victory && countClickedTiles() === fieldSize.x * fieldSize.y - bombCount) {
+ victory = true;
+ victoryEvent();
+ }
+}
+
+function victoryEvent() {
+ console.log("Win!");
+ animate();
+}
+
+initGame();
\ No newline at end of file