I was rewriting a minesweeper tutorial when I ran into a little problem. I'm using a flood fill to uncover all the blank squares. It'll keep revealing squares until it either finds a bomb or uncovers a square with a number. The problem is, it doesn't uncover all the squares it should.
rem *****************************
rem Minesweeper Tutorial
rem Author: Zimnox Inc
rem Date: Dec 22, 2013
rem *****************************
setVirtualResolution(480,480)
// The various flag states
#CONSTANT FLAG_REVEALED 1
#CONSTANT FLAG_QUESTION 2
#CONSTANT FLAG_FLAGGED 3
// Game parameters
Type _Params
width as integer
height as integer
boxWidth as integer
boxHeight as integer
cleared as integer
bombCount as integer
EndType
// Box parameters
Type Box
bombCount as integer
flag as integer
txt_ID as integer
spr_ID as integer
isBomb as integer
EndType
Game as _Params
// Build the game board
buildMap(20, 20, 10)
// Text to display info to user
msg = createText("")
setTextSize(msg, 20)
setTextPosition(msg, getVirtualWidth()/2-120, 5)
setTextColor(msg, 255,0,0,255)
// Start Main game loop =========================================================
repeat
// Get user pointer clicks
if getPointerPressed() = 1
x = trunc(getPointerX() / Game.boxWidth)
y = trunc(getPointerY() / Game.boxHeight)
status = revealBox(x, y)
if status = -1 then setTextString(msg, "You lost after "+str(trunc(timer()))+" seconds.")
if status = 1 then setTextString(msg, "You WON in just "+str(trunc(timer()))+" seconds!")
endif
// Update timer text
if status = 0 then setTextString(msg, "Elapsed Time - "+str(trunc(timer())))
Sync()
until getRawKeyPressed(27) = 1
// End Main game loop ===========================================================
// close down the app
cleanup()
end
// Called to uncover a box on the game board
function revealBox(x, y)
// Check bounds
if x < 0 or y < 0 or x >= Game.width or y >= Game.height then exitfunction 0
// default return status
status = 0
if boxes[x,y].flag <> FLAG_REVEALED
inc thing
boxes[x,y].flag = FLAG_REVEALED
// Hide the box sprite to reveal the number or bomb
setSpriteVisible(boxes[x,y].spr_ID, 0)
// If player revealed a bomb, game over!
if boxes[x,y].isBomb = 1 then exitfunction -1
// Keep track of how many boxes have been revealed
inc Game.cleared
// If player revealed a blank box, check surrounding boxes
if boxes[x,y].bombCount = 0
if getIsBomb(x-1, y-1) = 0 then revealBox(x-1, y-1)
if getIsBomb(x, y-1) = 0 then revealBox(x, y-1)
if getIsBomb(x+1, y-1) = 0 then revealBox(x+1, y-1)
if getIsBomb(x-1, y) = 0 then revealBox(x-1, y)
if getIsBomb(x+1, y) = 0 then revealBox(x+1, y)
if getIsBomb(x-1, y+1) = 0 then revealBox(x-1, y+1)
if getIsBomb(x, y+1) = 0 then revealBox(x, y+1)
if getIsBomb(x+1, y+1) = 0 then revealBox(x+1, y+1)
endif
// All available boxes without bombs have been revealed
if Game.cleared = Game.width*Game.height - Game.bombCount then status = 1
endif
endfunction status
function buildMap(width, height, bombs)
Game.width = width
Game.height = height
Game.cleared = 0
Game.bombCount = bombs
// Determine size of boxes to fit the board to the entire screen
Game.boxWidth = trunc(GetVirtualWidth() / Game.width)
Game.boxHeight = trunc(GetVirtualHeight() / Game.height)
// Create the array that will contain the game board
dim boxes[Game.width, Game.height] as Box
// Add bombs to the board
repeat
x = random(0, Game.width-1)
y = random(0, Game.width-1)
if boxes[x,y].isBomb = 0
boxes[x,y].isBomb = 1
dec bombs
endif
until bombs = 0
// Calculate the numbers
for y = 0 to Game.height-1
for x = 0 to Game.width-1
if boxes[x,].isBomb = 0
boxes[x,y].bombCount = 0
inc boxes[x,y].bombCount, getIsBomb(x-1, y-1)
inc boxes[x,y].bombCount, getIsBomb(x, y-1)
inc boxes[x,y].bombCount, getIsBomb(x+1, y-1)
inc boxes[x,y].bombCount, getIsBomb(x-1, y)
inc boxes[x,y].bombCount, getIsBomb(x+1, y)
inc boxes[x,y].bombCount, getIsBomb(x-1, y+1)
inc boxes[x,y].bombCount, getIsBomb(x, y+1)
inc boxes[x,y].bombCount, getIsBomb(x+1, y+1)
endif
next x
next y
// Create the box elements
for y = 0 to Game.height-1
for x = 0 to Game.width-1
// Boxes with no adjacent bombs do not display a number
// Boxes with a bomb will show an X instead
t$ = str(boxes[x,y].bombCount)
if boxes[x,y].bombCount = 0 then t$ = " "
if boxes[x,y].isBomb = 1 then t$ = "X"
// Create box object IDs
boxes[x,y].txt_ID = createText(t$)
boxes[x,y].spr_ID = createSprite(0)
boxes[x,y].flag = 0
// Set box's sprite parameters
setSpriteSize(boxes[x,y].spr_ID, Game.boxWidth-1, Game.boxHeight-1)
setSpritePosition(boxes[x,y].spr_ID, x*Game.boxWidth, y*Game.boxHeight)
colorSprite(x,y)
// Set box's text parameters
setTextPosition(boxes[x,y].txt_ID, x*Game.boxWidth , y*Game.boxHeight)
setTextSize(boxes[x,y].txt_ID, Game.boxWidth)
// Make sure text shows behind the box graphic
setTextDepth(boxes[x,y].txt_ID, 11)
next x
next y
endfunction
// Safer way to retrieve value from array to prevent out
// of bounds issues when checking boxes along board edges.
function getIsBomb(x, y)
if x >= 0 and y >= 0 and x < Game.width and y < Game.height
t = boxes[x,y].isBomb
exitfunction t
endif
endfunction 0
// Free up resources
function cleanup()
for y = 0 to Game.height-1
for x = 0 to Game.width-1
deleteSprite(boxes[x,y].spr_ID)
deleteText(boxes[x,y].txt_ID)
next x
next y
undim boxes[]
endfunction
// Create the boxes with a gradient across the board
function colorSprite(x, y)
max# = sqrt(Game.width^2 + Game.height^2)
p# = sqrt(x*x + y*y) / max#
r = 141 + (65-141)*p#
g = 180 + (84-180)*p#
b = 246 + (189-246)*p#
setSpriteColor(boxes[x,y].spr_ID, r, g, b,255)
endfunction
There are 10 bombs in this example. I clicked once (on a blank square) which began the flood fill process of uncovering the rest of the map. But notice the lower right corner. If it stopped there, it should be showing a border of numbers around what's left. Turns out those squares were all blank anyway, so why are they still there?