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