const numbers=['1️⃣','2️⃣','3️⃣','4️⃣','5️⃣','6️⃣','7️⃣','8️⃣'] const iDevise=navigator.platform.match(/^iP/) const feedback=document.querySelector('.feedback') var Game=function(cols,rows,number_of_bombs,set,usetwemoji){this.number_of_cells=cols*rows this.map=document.getElementById('map') this.cols=Number(cols) this.rows=Number(rows) this.number_of_bombs=Number(number_of_bombs) this.rate=number_of_bombs/this.number_of_cells this.emojiset=set this.numbermoji=[this.emojiset[0]].concat(numbers) this.usetwemoji=usetwemoji||false this.init()} Game.prototype.init=function(){this.prepareEmoji() if(this.number_of_cells>2500){alert('too big, go away, have less than 2500 cells');return false} if(this.number_of_cells<=this.number_of_bombs){alert('more bombs than cells, can\'t do it');return false} var that=this this.moveIt(true) this.map.innerHTML='' var grid_data=this.bomb_array() function getIndex(x,y){if(x>that.cols||x<=0)return-1 if(y>that.cols||y<=0)return-1 return that.cols*(y-1)+x-1} var row=document.createElement('div') row.setAttribute('role','row') grid_data.forEach(function(isBomb,i){var cell=document.createElement('span') cell.setAttribute('role','gridcell') var mine=that.mine(isBomb) var x=Math.floor((i+1)%that.cols)||that.cols var y=Math.ceil((i+1)/that.cols) var neighbors_cords=[[x,y-1],[x,y+1],[x-1,y-1],[x-1,y],[x-1,y+1],[x+1,y-1],[x+1,y],[x+1,y+1]] if(!isBomb){var neighbors=neighbors_cords.map(function(xy){return grid_data[getIndex(xy[0],xy[1])]}) mine.mine_count=neighbors.filter(function(neighbor_bomb){return neighbor_bomb}).length} mine.classList.add('x'+x,'y'+y) mine.neighbors=neighbors_cords.map(function(xy){return `.x${xy[0]}.y${xy[1]}`}) cell.appendChild(mine) row.appendChild(cell) if(x===that.cols){that.map.appendChild(row) row=document.createElement('div') row.setAttribute('role','row')}}) this.resetMetadata() this.bindEvents() this.updateBombsLeft()} Game.prototype.bindEvents=function(){var that=this var cells=document.getElementsByClassName('cell') Array.prototype.forEach.call(cells,function(target){target.addEventListener('click',function(evt){if(!target.isMasked||target.isFlagged)return if(document.getElementsByClassName('unmasked').length===0){that.startTimer() if(target.isBomb){that.restart(that.usetwemoji) var targetClasses=target.className.replace('unmasked','') document.getElementsByClassName(targetClasses)[0].click() return}} if(evt.view)that.moveIt() target.reveal() that.updateFeedback(target.getAttribute('aria-label')) if(target.mine_count===0&&!target.isBomb){that.revealNeighbors(target)} that.game()}) target.addEventListener('dblclick',function(){if(target.isFlagged)return that.moveIt() target.reveal() that.revealNeighbors(target) that.game()}) target.addEventListener('contextmenu',function(evt){var emoji evt.preventDefault() if(!target.isMasked){return} if(target.isFlagged){target.setAttribute('aria-label','Field') that.updateFeedback('Unflagged as potential bomb') emoji=that.emojiset[3].cloneNode() target.isFlagged=false}else{target.setAttribute('aria-label','Flagged as potential bomb') that.updateFeedback('Flagged as potential bomb') emoji=that.emojiset[2].cloneNode() target.isFlagged=true} target.childNodes[0].remove() target.appendChild(emoji) that.updateBombsLeft()}) if(iDevise){target.addEventListener('touchstart',function(evt){that.holding=setTimeout(function(){target.dispatchEvent(new Event('contextmenu'))},500)}) target.addEventListener('touchend',function(evt){clearTimeout(that.holding)})}}) window.addEventListener('keydown',function(evt){if(evt.key=='r'||evt.which=='R'.charCodeAt()){that.restart(that.usetwemoji)}})} Game.prototype.game=function(){if(this.result)return var cells=document.getElementsByClassName('cell') var masked=Array.prototype.filter.call(cells,function(cell){return cell.isMasked}) var bombs=Array.prototype.filter.call(cells,function(cell){return cell.isBomb&&!cell.isMasked}) if(bombs.length>0){Array.prototype.forEach.call(masked,function(cell){cell.reveal()}) this.result='lost' this.showMessage()}else if(masked.length===this.number_of_bombs){Array.prototype.forEach.call(masked,function(cell){cell.reveal(true)}) this.result='won' this.showMessage()}} Game.prototype.restart=function(usetwemoji){clearInterval(this.timer) this.result=false this.timer=false this.usetwemoji=usetwemoji this.init()} Game.prototype.resetMetadata=function(){document.getElementById('timer').textContent='0.00' document.querySelector('.wrapper').classList.remove('won','lost') document.querySelector('.result-emoji').textContent='' document.querySelector('.default-emoji').innerHTML=this.usetwemoji?twemoji.parse('😀'):'😀' document.querySelector('.js-settings').innerHTML=this.usetwemoji?twemoji.parse('🔧'):'🔧'} Game.prototype.startTimer=function(){if(this.timer)return this.startTime=new Date() this.timer=setInterval(function(){document.getElementById('timer').textContent=((new Date()-game.startTime)/1000).toFixed(2)},100)} Game.prototype.mine=function(bomb){var that=this var base=document.createElement('button') base.type='button' base.setAttribute('aria-label','Field') base.className='cell' base.appendChild(this.emojiset[3].cloneNode()) base.isMasked=true if(bomb)base.isBomb=true base.reveal=function(won){var emoji=base.isBomb?(won?that.emojiset[2]:that.emojiset[1]):that.numbermoji[base.mine_count] var text=base.isBomb?(won?"Bomb discovered":"Boom!"):(base.mine_count===0?"Empty field":base.mine_count+" bombs nearby") this.childNodes[0].remove() this.setAttribute('aria-label',text) this.appendChild(emoji.cloneNode()) this.isMasked=false this.classList.add('unmasked')} return base} Game.prototype.revealNeighbors=function(mine){var neighbors=document.querySelectorAll(mine.neighbors) for(var i=0;i/)[1]}}else{ele=document.createTextNode(emoji.alt||emoji.data||emoji)} return ele} this.emojiset=this.emojiset.map(makeEmojiElement) this.numbermoji=this.numbermoji.map(makeEmojiElement)} Game.prototype.bomb_array=function(){var chance=Math.floor(this.rate*this.number_of_cells) var arr=[] for(var i=0;i