Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
Minesweeping-learning materials: Basically all the minesweeping-learning videos are native JS. A small bug is solved with jquery. If the bug is solved and then changed, jQ will be marked with JS writing
Github-yuriafan/Mine-Clearance
HTML part, because it is a study + practice only wrote a very common style
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta HTTP-equiv =" x-UA-compatible "content=" IE =edge"> <link rel="style" href=" CSS /index.css"> <script src="js/jquery.js"></script> <title>Document</title> </head> <body> <div id="content"> <! <div id="level"> <button class="active"> Beginner </button> </button> <button> Advanced </button> <button </button> </div> <! <div id="gameBox"> <! </div> <! <div id="bottomText"> <div class="text"> <span class="mineNum"></sapn></div> </div> </div> <script src="js/index.js"></script> </body> </html>Copy the code
Three parts, the upper button, the middle minesweeper screen (‘ board ‘), the lower part of the remaining number of mines text, the middle ‘board’ is added with
In the CSS section, the first part is the style modification of the base interface, and the second part is the TD.one-TD.eight part is used to change the color of the numbers that appear after minesweeper click
Note that td border-color is used to create a stereoscopic effect like bricks on the ‘checkerboard’. Set the top and left sides to white, and the rest to whatever color you want.
#content{ height: 900px; margin: 0 auto; width: 800px; } #level{ height: 25px; width: 280px; margin: 0 auto; } #level button{ height: 25px; line-height: 25px; width: 60px; text-align: center; background: rgb(73, 139, 141);; color: white; padding: 0px; margin: 0px; border: 0px; border-radius: 5px; outline: none; cursor: pointer; } #level button.active{ background: #0bb9b6; } #level button.big{ width: 80px; } #gameBox{ padding-top: 20px; } #bottomText{ height: 20px; width: 200px; margin: 0 auto; line-height: 20px; } /* .mineNum{ height: 20px; width: 50px; } */ table{ border-spacing: 1px; background-color: #929196; margin: 0 auto; } td{ height: 20px; width: 20px; background-color: #ccc; padding: 0; border: 1px solid; border-color: #fff #a1a1a1 #a1a1a1 #fff; text-align: center; line-height: 20px; font-weight: bold; } .mine{ background:#c9c9c9 url(.. /images/mine.png) no-repeat center; background-size: cover; } .flag{ background:#ccc url(.. /images/flag.png) no-repeat center; background-size: cover; } td.zero{ background-color: #d9d9d9; border-color: #d9d9d9; } td.one{ color: red; background-color: #d9d9d9; border-color: #d9d9d9; } td.two{ color: green; background-color: #d9d9d9; border-color: #d9d9d9; } td.three{ color: yellow; background-color: #d9d9d9; border-color: #d9d9d9; } td.four{ color: blue; background-color: #d9d9d9; border-color: #d9d9d9; } td.five{ color: blueviolet; background-color: #d9d9d9; border-color: #d9d9d9; } td.six{ color: brown; background-color: #d9d9d9; border-color: #d9d9d9; } td.seven{ color: cadetblue; background-color: #d9d9d9; border-color: #d9d9d9; } td.eight{ color: coral; background-color: #d9d9d9; border-color: #d9d9d9; }Copy the code
JS part!
The first is object-oriented thinking, where there are partial calls to written functions that are written on a prototype.
The first step is to write the constructors, write down the variables that will be used in the later generation of the ‘checkerboard’.
Function Mine(tr,td,mineNum){this.tr=tr; / / the number of rows this. Td = td; / / the number of columns enclosing mineNum = mineNum; // this.squares=[]; This.tds =[]; this.tds=[]; // This. SurplusMine =mineNum; // The number of remaining mines this.allRight=false; This.parent = document.getelementById ('gameBox'); }Copy the code
Then createDom() generates the ‘checkerboard’ using table, TD, tr, but ul and LI can also do this
After the base ‘checkerboard’ is generated, the click event that writes the grid calls the play function, which we’ll talk about later
Mine.prototype.createDom=function(){ var This=this; var table=document.createElement('table'); for(var i=0; i<this.tr; i++){ var domTr=document.createElement('tr'); // Generate tr this.tds[I]=[]; // Generate tr this.tds[I]=[]; for(var j=0; j<this.td; J++) {/ / column var domTd = document. The createElement method (" td "); / / same as above domTd. Pos = [I, j]; Domtd. onmouseDown =function(){this. play(event, This); Td} this.tds[I][j]=domTd; domTr.appendChild(domTd); } table.appendChild(domTr); } this.parent.innerHTML=""; this.parent.appendChild(table); }Copy the code
And then the init function
A random function is used to randomly generate the location of lightning. Td * TR is used to obtain the ordered number greater than the number of required lightning; sorting +random is used to obtain the random number; mineNum is used to determine the number of random numbers; finally, an array is returned.
Mine.prototype.randomNum=function(){ var square=new Array(this.td*this.tr); for(var i=0; i<square.length; i++){ square[i]=i; } square. Sort (function () {return 0.5 - Math. The random ()}); return square.slice(0,this.mineNum); } Mine.prototype.init=function(){ var rn=this.randomNum(); Var n=0; For (var I =0; var I =0; i<this.tr; i++){ this.squares[i]=[]; for(var j=0; j<this.td; j++){ n++; If (rn.indexof (n)!); if(rn.indexof (n)! =-1){ this.squares[i][j]={type:'mine',x:j,y:i}; }else{ this.squares[i][j]={type:'number',x:j,y:i,value:0}; } } } this.update(); this.createDom(); this.parent.oncontextmenu=function(){ return false; } // this.mineNumDom=document.getElementsByClassName('mineNum'); // this.mineNumDom.innerHTML=this.surplusMine; $('.mineNum').html(this.surplusMine); // There is a bug that cannot be displayed in the native implementation, but it is not fixed.Copy the code
Top init function
Define an n for later comparison. The two for loops use Rn.indexof (n) to determine what rn is. If it is in a random number array, the unsquares coordinate will be assigned type:’mine’, otherwise number.
Update () later, more or less.
Enclosing the parent oncontextmenu is used to cancel the right mouse button click event, for a moment to right click on the add flags 🚩 preparation.Copy the code
The last line, using jQuery, puts the thunder number in the bottom span.
The function getAround() is called to get the error value of the four corners of the checkerboard near a ray + itself + 9 squares. The error value of the four corners of the checkerboard near a ray + itself + 9 squares.
Prototype. GetAround =function(square){var x=square. var y=square.y; var result=[]; For (var I =x-1; var I =x-1; i<=x+1; i++){ for(var j=y-1; j<=y+1; J++) {/ / exclude the whole board four Angle error value + + in the eight grid ray if (I < 0 | | j < 0 | | I > this. Td - 1 | | j > this. Tr - 1 | | (I = = x & j = = y) | | this. Squares [j] [I] type = = 'mine') { continue; } result.push([j,i]); }} return result; }Copy the code
Then getAround is called in the update
Prototype. Update =function(){for(var I =0; i<this.tr; i++){ for(var j=0; j<this.td; If (this.squares[I][j].type=='number'){continue; } var num=this.getAround(this.squares[i][j]); For (var k=0; k<num.length; k++){ this.squares[num[k][0]][num[k][1]].value+=1; }}}}Copy the code
Use two for loops to exclude positions that are not mines, and then call getAround to get the corresponding array of coordinates next to the mines. Since this is a two-dimensional array, use num[k][0] to get the x coordinates, and the same for y coordinates, and then increment the value of the corresponding position by 1
And then the play function that was called in createDom at the beginning. This is a long function, so we split it into two parts.
Play () the first part
Mine.prototype.play=function(ev,obj){ var This=this; if(ev.which==1 && obj.className! Var curSquare=this.squares[obj. Pos [0]][obj. Pos [1]]; var cl=['zero','one','two','three','four','five','six','seven','eight']; if(curSquare.type=='number'){ obj.innerHTML=curSquare.value; obj.className=cl[curSquare.value]; If (cursquare. value==0){obj.innerHTML=''; function getAllZero(square){ var around=This.getAround(square); for(var i=0; i<around.length; i++){ var x=around[i][0]; / / var y = around [I] [1]; / / column enclosing TDS [x] [y] className = cl [enclosing squares [x] [y] value]; If (this.squares [x][y].value==0){// Add attributes to the squares to reduce resource waste if(! This.tds[x][y].check){ This.tds[x][y].check=true; getAllZero(This.squares[x][y]); This.tds[x][y].innerhtml = this.squares [x][y].value; this.tds [x][y].value; } } } getAllZero(curSquare); } }else{ this.gameOver(obj); }}Copy the code
I’m going to start by taking this out of the function’s scope
The first if, two judgments: 1. The left button of the mouse is clicked; 2. There is no red flag in the clicked grid (this attribute is optional and can be added according to personal needs.
Write cl for adding CSS properties
If it is zero, use a function to process it. In getAllZero(), obtain the eight adjacent grids with the value of zero, add the corresponding CSS attributes, and determine whether the value is zero. If recursion is used, Reason: Look around for patterns as follows
If it finds it, it shows itself, and then continues to look around. If it finds it, it shows itself, and then continues to look. Repeat until you find a cell with a value that is not zero, and then display its innerHTML value
One small problem is that, for the grid X, it exists in the surrounding grid X-1, and also in the grid X +1, which will lead to repeated query and resource waste. Therefore, attributes are added to the queried grid to reduce resource waste.
Then call getAllZero()
And finally else if it is not a number then gameOver is called.
\
Play () Part 2
If (ev.which==3){// If the right click is a number, ignore if(obj.className&&obj.className! ='flag'){ return; } obj.className=obj.className=='flag'? '':'flag'; If (this.squares[obj.pos[0]][obj.pos[1]].type=='mine'){this.allRight=true; }else{ this.allRight=false; } / / flag add and reduce the if (obj. ClassName = = 'flag') {$(' mineNum '). The HTML (-- this. SurplusMine); }else{ $('.mineNum').html(++this.surplusMine); } if(this.surplusMine==0){ if(this.allRight){ alert('win'); }else{ alert('lose'); this.gameOver; }}}}Copy the code
This part deals with the case when the right mouse button, if the right click has left click out of the number, it is ignored.
Then, using the ternary operator, the grid that is not a number is switched with or without flags.
Define an allRight attribute at the beginning of the constructor, then right-click the processing area, and set allRight to true if flags are inserted and all places with flags of type mine within the specified number, otherwise false, Used at the end of the game to determine whether the flag has inferred all the thunder place is accurate.
Then if-else controls the display of flags in span and the surplusMine property
Finally, use surplusMine to determine whether the game is about to end, and all pairs will pop Win, otherwise call gameOver function.
Prototype. GameOver =function(clickTd){//1. For (var I =0; var I =0; i<this.tr; i++){ for(var j=0; j<this.td; j++){ if(this.squares[i][j].type=='mine'){ this.tds[i][j].className='mine'; } this.tds[i][j].onmousedown=null; } } if(clickTd){ clickTd.style.backgroundColor="white"; } alert('lose'); }Copy the code
To determine if a game is over, do the three things in the code comment above.
\
The last is to deal with adjusting the difficulty of the game, dynamically generating different ‘boards’
/ / button function realization of var BTNS = document. QuerySelectorAll (' # level button "); var mine=null; // Store the generated instance var ln=0; / / deal with the currently selected state var arr = [,9,10 [9], [16,16,40], [28,28,99]]. For (let I =0; i<btns.length-1; i++){ btns[i].onclick=function(){ btns[ln].className=""; this.className="active"; mine=new Mine(... arr[i]); // Extend operator... You can't get arr[I] out. Arr [I][0],arr[I][1] mine.init(); ln=i; } } btns[0].onclick(); // Initialize BTNS [3]. Onclick =function(){mine.init(); }Copy the code
Write the data required by the constructor as a two-dimensional array, generate the ‘checkerboard’ when clicking the corresponding button, and undisplay the previous’ checkerboard ‘(className)
Two notes: 1…. Is an extended operator
2. Let is used in the for loop instead of var, so ln= I is used in the onclick loop
Make it not empty when you first open it, write the code and click the first button
And then the final onclick handles the restart button
That’s basically it, there are still many details need to be modified, if there are any problems, please kindly advise