问题
I am creating a minesweeper game for a javascript project and have run into a problem that I can't get my head round. When you click on an empty cell (one which does not have any mines around it and so does not display a number) in the minesweeper game for those who don't know, this will reveal the whole block of empty cells that are neighbouring eachother, stopping when the "wall of numbers" containing these empty blocks is found. Example below:
[1]http://datagenetics.com/blog/june12012/hole.png
This requires a recursive function to figure out which blocks are to be revealed. My code at the moment only reveals the block that is clicked on:
function revealGridContents()
{
switch (positionClickContents)
{
case 0:
ctx.drawImage(clickedImage, (xClick*20), (yClick*20));
break;
case 1:
ctx.drawImage(num1Image, (xClick*20), (yClick*20));
break;
case 2:
ctx.drawImage(num2Image, (xClick*20), (yClick*20));
break;
case 3:
ctx.drawImage(num3Image, (xClick*20), (yClick*20));
break;
case 4:
ctx.drawImage(num4Image, (xClick*20), (yClick*20));
break;
case 5:
ctx.drawImage(num5Image, (xClick*20), (yClick*20));
break;
case 6:
ctx.drawImage(num6Image, (xClick*20), (yClick*20));
break;
case 7:
ctx.drawImage(num7Image, (xClick*20), (yClick*20));
break;
case 8:
ctx.drawImage(num8Image, (xClick*20), (yClick*20));
break;
};
};
The number passed into the switch statement is the value of the data in the array grid[xClick][yClick]; eg a 4 symbolises a block with 4 mines around it, so the image for 4 will be displayed.
Case 0 is the case that a blank block is clicked and so the code for this needs modifying but I really can't think what to do.
From what I can understand, I will need to call the revealGridContents(); function from case 0, but passing in new values for xClick and yClick (the x and y values of the array position) for each square that I want to check.
Any help shedding the light on what I need to do next would be greatly appreciated!
回答1:
Without knowing slightly more of your program it's hard to give you an exact solution. You'll probably need a separate function to do this, as just using the same function will reveal everything (which is obviously not how the game works). You'll also need some way of keeping track of revealed cells, otherwise you'll get into a loop (I'm assuming this is stored in another 2d array revealed[x][y]).
You probably want to do something like this (I haven't tested this so there may be errors in it - apologies):
function revealGridContents(){
switch (positionClickContents){
case 0:
ctx.drawImage(clickedImage, (xClick*20), (yClick*20));
checkAdjacentCells(xClick, yClick);
break;
...
}
}
function checkAdjacentCells(x,y){
var cellsToCheck = [
[x,y+1],
[x,y-1],
[x+1,y],
[x-1,y]];
var x,y;
for(var i=0; i<=cellsToCheck.length; i++){
x = cellsToCheck[i][0];
y = cellsToCheck[i][1];
if(!revealed[x][y] && grid[x][y] == 0){
ctx.drawImage(clickedImage, x*20, y*20);
checkAdjacentCells(x,y);
}
}
}
回答2:
Just as a general advice, you need a better separation between the model of your game and the UI.
Here is the begining of my interpretation of the minesweeper game:
function init() {
var i,j; // indexes
map = []; // global map, because i'm lazy
for (i=0; i<10; i++) {
var row = [];
for (j=0; j<10; j++)
row.push({
bomb : Math.round(Math.random()-0.4), // set bombs randomly, change to your correct ratio
revealed : false, // nothing is visible at start
count : 0 // counts will be computed after all the map is created
});
map.push(row);
}
// set adjacent bomb counts
for (i=0; i<10; i++)
for (j=0; j<10; j++) {
if (map[i-1] && map[i-1][j-1] && map[i-1][j-1].bomb) map[i][j].count++;
if (map[i-1] && map[i-1][j] && map[i-1][j].bomb) map[i][j].count++;
if (map[i-1] && map[i-1][j+1] && map[i-1][j+1].bomb) map[i][j].count++;
if (map[i] && map[i][j-1] && map[i][j-1].bomb) map[i][j].count++;
if (map[i] && map[i][j+1] && map[i][j+1].bomb) map[i][j].count++;
if (map[i+1] && map[i+1][j-1] && map[i+1][j-1].bomb) map[i][j].count++;
if (map[i+1] && map[i+1][j] && map[i+1][j].bomb) map[i][j].count++;
if (map[i+1] && map[i+1][j+1] && map[i+1][j+1].bomb) map[i][j].count++;
}
}
function print() { // uses console to display instead of canvas
var output = '\n';
for (var i=0; i<10; i++) {
for (var j=0; j<10; j++) {
var item = map[i][j];
output += (item.revealed ? item.count : 'x') + ' ';
}
output += '\n';
}
console.log(output);
}
function click(x,y) {
reveal(x,y);
print(map);
}
function reveal(x,y) {
// break early if click is invalid (invalid clicks are generated)
if (x < 0 || x > 9 || y < 0 || y > 9 || map[x][y].revealed) return;
// mark the square as clicked
map[x][y].revealed = true;
if (map[x][y].bomb) { // losing click
console.log('You lost');
} else if (map[x][y].count === 0) { // click on 0 adjacent bombs
reveal(x-1, y);
reveal(x, y-1);
reveal(x, y+1);
reveal(x+1, y);
}
}
init();
console.log('First print');
print();
console.log('Click 1,3');
click(1,3);
The difficult part is in the click() function.
Try this demo (click 'run with JS' several times until you don't lose and hit a 0): http://jsbin.com/iqeganU/1/edit
来源:https://stackoverflow.com/questions/19686176/recursive-function-to-check-2d-array