Classic Minesweeper Game - Logic Bug Clearing Empty Squares

Hi all.

I figured I would build the classic Minesweeper game.
I’ve a tiny logic challenge in my external module whose job it is to find adjacent empty cells and to clear their button.

Currently for testing, my buttons are semi-transparent so I can see underlaying bomb locations and numbers, so I can also see the empty cells. But once fixed I will remove the alpha and you wouldn’t see both buttons and bombs/number at the same time.

board is a matrix that populates an outter table or rows, before the inner table manages each column/cell in a row.
EG
board[1][1] would reference the cell in the top left and board[10][10] the cell in the bottom right

The challenge is some adhoc cells above, and some adhoc cells below (north & south) are missed.
See screenshots.

The first cell listed in the cell the user clicked on.


Here is the code:

function mod.clearEmptySquares(board, rowX, colY)
    local rowMax = #board 
    local colMax = #board[1]
    board[rowX][colY].info.btn = false -- receive in the box to show/hide
    print("BOX["..rowX.."]["..colY.."]")

	if (board[rowX][colY].info.number == 0) then

        if rowX > 1 then -- ABOVE
            if (colY > 1) then --LEFT
                if (board[rowX-1][colY-1].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX-1, colY-1)
                end       
            end
            if (colY == 1) then --CENTER
                if (board[rowX-1][colY].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX-1, colY)
                end       
            end
            if (colY < colMax) then --RIGHT
                if (board[rowX-1][colY+1].info.btn == true)then
                    board = mod.clearEmptySquares(board, rowX-1, colY+1)
                end
            end
        end

        if rowX < rowMax then -- BELOW
            if (colY > 1) then --LEFT
                if (board[rowX+1][colY-1].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX+1, colY-1)
                end       
            end
            if (colY == 1) then --CENTER
                if (board[rowX+1][colY].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX+1, colY)
                end       
            end
            if (colY < colMax) then -- RIGHT
                if (board[rowX+1][colY+1].info.btn == true)then
                    board = mod.clearEmptySquares(board, rowX+1, colY+1)
                end
            end
        end

        if (colY > 1) then -- LEFT ADJACENT
            if (board[rowX][colY-1].info.btn == true) then
                board = mod.clearEmptySquares(board, rowX, colY-1)
            end       
        end

        if (colY < colMax) then -- RIGHT ADJACENT
            if (board[rowX][colY+1].info.btn == true)then
                board = mod.clearEmptySquares(board, rowX, colY+1)
            end
        end
    end
    return board
end

Is that intended? Seems like maybe some copy-paste from the > 1 tests?

Hiya, funny you say that I originally had it as ```
(colY == 1)

Code tweeked to be > =
problem persists.

function mod.clearEmptySquares(board, rowX, colY)
    local rowMax = #board 
    local colMax = #board[1]
    board[rowX][colY].info.btn = false -- receive in the box to show/hide
    -- if board[rowX-1][colY-1].imgBomb ~= nil then
    --     board[rowX-1][colY-1].imgBomb.isVisible = true
    -- end
    -- if board[rowX-1][colY-1].imgText ~= nil then
    --     board[rowX-1][colY-1].imgText.isVisible = true
    -- end

    print("BOX["..rowX.."]["..colY.."]")

	if (board[rowX][colY].info.number == 0) then

        if rowX > 1 then -- ABOVE
            if (colY > 1) then --LEFT
                if (board[rowX-1][colY-1].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX-1, colY-1)
                end       
            end
            if (colY > 1) then --CENTER
                if (board[rowX-1][colY].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX-1, colY)
                end       
            end
            if (colY < colMax) then --RIGHT
                if (board[rowX-1][colY+1].info.btn == true)then
                    board = mod.clearEmptySquares(board, rowX-1, colY+1)
                end
            end
        end

        if rowX < rowMax then -- BELOW
            if (colY > 1) then --LEFT
                if (board[rowX+1][colY-1].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX+1, colY-1)
                end       
            end
            if (colY > 1) then --CENTER
                if (board[rowX+1][colY].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX+1, colY)
                end       
            end
            if (colY < colMax) then -- RIGHT
                if (board[rowX+1][colY+1].info.btn == true)then
                    board = mod.clearEmptySquares(board, rowX+1, colY+1)
                end
            end
        end

        if (colY > 1) then -- LEFT ADJACENT
            if (board[rowX][colY-1].info.btn == true) then
                board = mod.clearEmptySquares(board, rowX, colY-1)
            end       
        end

        if (colY < colMax) then -- RIGHT ADJACENT
            if (board[rowX][colY+1].info.btn == true)then
                board = mod.clearEmptySquares(board, rowX, colY+1)
            end
        end
    end
    return board
end

Oh, well, I mean the two CENTER cases wouldn’t seem to need column checks, at all. As is, column 1 will be botched (I’m guessing that’s what we see in the last picture).

No… my code is design to search the row above the triggering cell. For the cells above, left, centre, right.
Then the code is designed to search the row below the triggering cell. For the cells below left, centre, right.
This just leave the two cells directly left and right of the trigger cell.
There is no column search.

This is what I meant by CENTER. In those two cases the column check is pointless (you don’t need to look left or right, so there’s no reason to guard) and I assume when colY is 1, it’s skipping the logic.

Oh ok - let me comment out and test your theory - thanks for helping by the way… if easier we can chat on discord.

so I tweaked my code to this - still problem persists with north and south cells reveals.

function mod.clearEmptySquares(board, rowX, colY)
    local rowMax = #board 
    local colMax = #board[1]
    board[rowX][colY].info.btn = false -- receive in the box to show/hide
    print("BOX["..rowX.."]["..colY.."]")

	if (board[rowX][colY].info.number == 0) then

        if rowX > 1 then -- ABOVE
            if (colY > 1) then --LEFT
                if (board[rowX-1][colY-1].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX-1, colY-1)
                end 
                if (board[rowX-1][colY].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX-1, colY)
                end      
            end
            -- if (colY > 1) then --CENTER
            --     if (board[rowX-1][colY].info.btn == true) then
            --         board = mod.clearEmptySquares(board, rowX-1, colY)
            --     end       
            -- end
            if (colY < colMax) then --RIGHT
                if (board[rowX-1][colY+1].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX-1, colY+1)
                end
            end
        end

        if rowX < rowMax then -- BELOW
            if (colY > 1) then --LEFT
                if (board[rowX+1][colY-1].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX+1, colY-1)
                end  
                if (board[rowX+1][colY].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX+1, colY)
                end      
            end
            -- if (colY > 1) then --CENTER
            --     if (board[rowX+1][colY].info.btn == true) then
            --         board = mod.clearEmptySquares(board, rowX+1, colY)
            --     end       
            -- end
            if (colY < colMax) then -- RIGHT
                if (board[rowX+1][colY+1].info.btn == true)then
                    board = mod.clearEmptySquares(board, rowX+1, colY+1)
                end
            end
        end

        if (colY > 1) then -- LEFT ADJACENT
            if (board[rowX][colY-1].info.btn == true) then
                board = mod.clearEmptySquares(board, rowX, colY-1)
            end       
        end

        if (colY < colMax) then -- RIGHT ADJACENT
            if (board[rowX][colY+1].info.btn == true)then
                board = mod.clearEmptySquares(board, rowX, colY+1)
            end
        end
    end
    return board
end


wouldn’t you loop from x-1, x+1 and y-1, y+1?

Change that to something like

if rowX > 1 then -- ABOVE
            if (colY > 1) then --LEFT
                if (board[rowX-1][colY-1].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX-1, colY-1)
                end
           end
                -- NOT inside the if statement
                if (board[rowX-1][colY].info.btn == true) then
                    board = mod.clearEmptySquares(board, rowX-1, colY)
                end      
         --   end
            -- if (colY > 1) then --CENTER
            --     if (board[rowX-1][colY].info.btn == true) then
            --         board = mod.clearEmptySquares(board, rowX-1, colY)
            --     end       
            -- end

and likewise for the below case, and see what you get.

here is some pseudo code

for i = x-1, x+1 do
  for j = y-1, y+1 do
    if <in bounds i, j> then <do something> end
  end
end

you can then easily extend to support looping out 2 or 3 loops easily, like this

local n = 2
for i = x-n, x+n do
  for j = y-n, y+n do
    if <in bounds i, j> then <do something> end
  end
end
1 Like

THANK YOU SO MUCH SGS :smile:

I think with my initial code, i over thought it way to much!

Having scrapped my code and adopted your advice, I have success!!

Here was the resulting function code, for anyone who reads this forum post in the future and fancies learning from this :slight_smile:

function mod.clearEmptySquares(board, rowX, colY)
    local rowMax = #board 
    local colMax = #board[1]

    local count = 1

    if (board[rowX][colY].info.number == 0) then
        for i = rowX-count, rowX+count do -- itterate matrix
            for j = colY-count, colY+count do -- itterate matrix
                if i >=1 and i <= rowMax then -- stay within board boundaries
                    if j >=1 and j <= colMax then -- stay within board boundaries

                        if (board[i][j].info.number >=1) then -- found clearing edge
                            board[i][j].info.btn = false
                        elseif ( i == rowX) and (j == colY) then -- found myself/trigger box
                            board[i][j].info.btn = false
                        elseif (board[i][j].info.number == 0 ) and ( board[i][j].info.btn == true ) then -- investigate box whose btn still shows
                            mod.clearEmptySquares(board, i, j)
                        end

                    end
                end
            end
        end

    end

    return board
end

FYI, you can also optimize some more here (makes it more readable too)…

local tile = board[i][j].info
if (tile.number >=1) then -- found clearing edge
    tile.btn = false
elseif ( i == rowX) and (j == colY) then -- found myself/trigger box
    tile.btn = false
elseif (tile.number == 0 ) and ( tile.btn == true ) then -- investigate box whose btn still shows
    mod.clearEmptySquares(board, i, j)
end


2 Likes

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.