[Solved] Cannot figure out this bug...

This game is basically a picture slide puzzle, but just with numbers at the moment. Slide the numbers around the board to get it to show 1-24 in order. However, the check-for-win “Grid:checkWin()” method has a bug that causes the game to be solved early and I cannot figure out why/how.


--# Main
function setup()
    
    grid = Grid()
    win = Win()
    
    for i = 1, 5 do
        grid:randomMove()
    end
    
    parameter.watch("grid.usingCell")
    parameter.watch("grid.value[grid.usingCell.x][grid.usingCell.y]")
    parameter.watch("grid:getInitialValue(grid.usingCell.x, grid.usingCell.y)")
    
end

function update()
end

function draw()
    
    update()
    
    background(66, 66, 106, 255)

    grid:draw()
    
    if win.win == true then
        win:draw()
    end
    
end

function touched(touch)
    
    --if win.win == false then
        grid:touched(touch)
    --end
    
end

--# Grid
Grid = class()

function Grid:init()
    
    self.cells = vec2(5, 5)
    self.cellSize = nil
    self.padding = nil
    
    self.usingCell = vec2(nil, nil)
    self.startTouch = vec2(nil, nil)
    
    self.value = {}
    for i = 1, self.cells.x do
        self.value[i] = {}
        for k = 1, self.cells.y do
            self.value[i][k] = self:getInitialValue(i, k)
        end
    end
    self.value[self.cells.x][1] = 0
    
end

function Grid:getInitialValue(i, k)
    return (((self.cells.y - k) + 1) * self.cells.x) - self.cells.x + i
end

function Grid:draw()
    
    -- rezise grid
    local size = vec2(WIDTH / self.cells.x, HEIGHT / self.cells.y)
    if size.x < size.y then
        self.cellSize = size.x
    else
        self.cellSize = size.y
    end
    
    strokeWidth(self.cellSize * 0.05)
    
    -- draw grid
    for i = 1, self.cells.x do
        for k = 1, self.cells.y do
            
            if self.usingCell.x == i and self.usingCell.y == k then
                stroke(255, 255, 0, 255)
            else
                stroke(255, 255, 255, 255)
            end
            
            local x = ((i - 1) * self.cellSize) + self:getPadding().x
            local y = ((k - 1) * self.cellSize) + self:getPadding().y
            
            if self.value[i][k] ~= 0 then
                fill(66, 66, 66, 255)
                rect(x, y, self.cellSize, self.cellSize)
                fill(255, 255, 255, 255)
                text(self.value[i][k], x + (self.cellSize / 2), y + (self.cellSize / 2))
            end
            
        end
    end
    
end

function Grid:touched(touch)
    
    if touch.state == BEGAN then
        
        self.startTouch = touch
    
        local i = math.floor((touch.x - self:getPadding().x) / self.cellSize) + 1
        local k = math.floor((touch.y - self:getPadding().y) / self.cellSize) + 1
    
        self.usingCell = vec2(i, k)
    
        -- out of bounds
        if i < 1 or i > self.cells.x or k < 1 or k > self.cells.y then
            self.usingCell = vec2(nil, nil)
            return
        end
    
        -- empty cell
        if self.value[i][k] == 0 then
            self.usingCell = vec2(nil, nil)
        end
        
    end
    
    -- no or end of touch
    if touch.state == ENDED then
        self:moveCell(touch)
        self.usingCell = vec2(nil, nil)
    end
    
end

function Grid:getPadding()
    
    local padding = vec2(0, 0)
    
    if WIDTH ~= self.cells.x * self.cellSize then
        padding.x = (WIDTH - (self.cellSize * self.cells.x)) / 2
    else
        padding.y = (HEIGHT - (self.cellSize * self.cells.y)) / 2
    end
    
    return padding
    
end

function Grid:moveCell(touch, move)
    
    -- move by touch
    local delta = vec2(0, 0)
    if touch ~= nil then
        delta = vec2(touch.x - self.startTouch.x, touch.y - self.startTouch.y)
    end
    
    local i = self.usingCell.x
    local k = self.usingCell.y
    
    if math.abs(delta.x) > math.abs(delta.y) or move == "right" or move == "left" then
        
        -- right
        if delta.x > 0 or move == "right" then
            if self:validMove(i + 1, k) == false then return end
            self.value[i + 1][k] = self.value[i][k]
            self.value[i][k] = 0
        --end
        
        -- left
        elseif delta.x < 0 or move == "left" then
            if self:validMove(i - 1, k) == false then return true end
            self.value[i - 1][k] = self.value[i][k]
            self.value[i][k] = 0
        end
        
    else
        
        -- up
        if delta.y > 0 or move == "up" then
            if self:validMove(i, k + 1) == false then return end
            self.value[i][k + 1] = self.value[i][k]
            self.value[i][k] = 0
        --end
        
        -- down
        elseif delta.y < 0 or move == "down" then
            if self:validMove(i, k - 1) == false then return end
            self.value[i][k - 1] = self.value[i][k]
            self.value[i][k] = 0
        end
        
    end
    
    -- check for win
    if self:checkWin() == true then win.win = true end
    
end

function Grid:validMove(i, k)
    
    -- out of bounds
    if self:validCell(i, k) == false then return false end
    
    -- cell not empty
    if self.value[i][k] ~= 0 then return false end
    
    return true
    
end

function Grid:validCell(i, k)
    
    -- out of bounds
    if i < 1 then
        return false
    elseif i > self.cells.x then
        return false
    elseif k < 1 then
        return false
    elseif k > self.cells.y then
        return false
    end
        
    return true
    
end

function Grid:randomMove()
    
    local index = vec2(nil, nil)
    
    -- find empty cell
    for i = 1, self.cells.x do
        for k = 1, self.cells.y do
            if self.value[i][k] == 0 then
                index = vec2(i, k)
            end
        end
    end
    
    -- random move
    while 1 do
        
        local rand = math.random(1,4)
        
        -- right
        if rand == 1 and self:validCell(index.x - 1, index.y) == true then
            self.usingCell = vec2(index.x - 1, index.y)
            self:moveCell(nil, "right")
            break
        end
        
        -- left
        if rand == 2 and self:validCell(index.x + 1, index.y) == true then
            self.usingCell = vec2(index.x + 1, index.y)
            self:moveCell(nil, "left")
            break
        end
        
        -- up
        if rand == 3 and self:validCell(index.x, index.y - 1) == true then
            self.usingCell = vec2(index.x, index.y - 1)
            self:moveCell(nil, "up")
            break
        end
        
        -- down
        if rand == 4 and self:validCell(index.x, index.y + 1) == true then
            self.usingCell = vec2(index.x, index.y + 1)
            self:moveCell(nil, "down")
            break
        end
        
    end
    
    self.usingCell = vec2(nil, nil)
    
end

function Grid:checkWin()
    
    for i = 1, self.cells.x do
        for k = 1, self.cells.y do
            if i == self.cells.x and k == 1 then
                if self.value[i][k] ~= 0 then return false end
            else
                if self.value[i][k] ~= self:getInitialValue(i, k) then return false end
            end
        end
    end
    
    return true
    
end

--# Win
Win = class()

function Win:init()
    
    self.win = false
    
end

function Win:draw()
    
    local size = vec2(200, 100)
    local position = vec2((WIDTH / 2) - (size.x / 2), (HEIGHT / 2) - (size.y / 2))
    
    fill(66, 66, 106, 255)
    rect(position.x, position.y, size.x, size.y)
    
    fill(255, 255, 255, 255)
    text("Solved!", WIDTH / 2, HEIGHT / 2)
    
end

function Win:touched(touch)

end

Just put “Win = false” in the setup function.

The following should work. I’ve changed the check initial value to return 0 rather than 25 for the final slot which simplifies your check win statement. I’ve also explicitly set win.win to false in the check statement

--# Main
function setup()

    grid = Grid()
    win = Win()

    for i = 1, 5 do
        grid:randomMove()
    end

    parameter.watch("grid.usingCell")
    parameter.watch("grid.value[grid.usingCell.x][grid.usingCell.y]")
    parameter.watch("grid:getInitialValue(grid.usingCell.x, grid.usingCell.y)")

end

function update()
end

function draw()

    update()

    background(66, 66, 106, 255)

    grid:draw()
--print(win.win)
    if win.win == true then
        win:draw()
    end

end

function touched(touch)

    --if win.win == false then
        grid:touched(touch)
    --end

end


--# Grid
Grid = class()

function Grid:init()

    self.cells = vec2(5, 5)
    self.cellSize = nil
    self.padding = nil

    self.usingCell = vec2(nil, nil)
    self.startTouch = vec2(nil, nil)

    self.value = {}
    for i = 1, self.cells.x do
        self.value[i] = {}
        for k = 1, self.cells.y do
            self.value[i][k] = self:getInitialValue(i, k)
        end
    end
    self.value[self.cells.x][1] = 0

end

function Grid:getInitialValue(i, k)
    if i==self.cells.x and k==1 then
        return 0
    else
    return (((self.cells.y - k) + 1) * self.cells.x) - self.cells.x + i
    end
end

function Grid:draw()

    -- rezise grid
    local size = vec2(WIDTH / self.cells.x, HEIGHT / self.cells.y)
    if size.x < size.y then
        self.cellSize = size.x
    else
        self.cellSize = size.y
    end

    strokeWidth(self.cellSize * 0.05)

    -- draw grid
    for i = 1, self.cells.x do
        for k = 1, self.cells.y do

            if self.usingCell.x == i and self.usingCell.y == k then
                stroke(255, 255, 0, 255)
            else
                stroke(255, 255, 255, 255)
            end

            local x = ((i - 1) * self.cellSize) + self:getPadding().x
            local y = ((k - 1) * self.cellSize) + self:getPadding().y

            if self.value[i][k] ~= 0 then
                fill(66, 66, 66, 255)
                rect(x, y, self.cellSize, self.cellSize)
                fill(255, 255, 255, 255)
                text(self.value[i][k], x + (self.cellSize / 2), y + (self.cellSize / 2))
            end

        end
    end

end

function Grid:touched(touch)

    if touch.state == BEGAN then

        self.startTouch = touch

        local i = math.floor((touch.x - self:getPadding().x) / self.cellSize) + 1
        local k = math.floor((touch.y - self:getPadding().y) / self.cellSize) + 1

        self.usingCell = vec2(i, k)

        -- out of bounds
        if i < 1 or i > self.cells.x or k < 1 or k > self.cells.y then
            self.usingCell = vec2(nil, nil)
            return
        end

        -- empty cell
        if self.value[i][k] == 0 then
            self.usingCell = vec2(nil, nil)
        end

    end

    -- no or end of touch
    if touch.state == ENDED then
        self:moveCell(touch)
        self.usingCell = vec2(nil, nil)
    end

end

function Grid:getPadding()

    local padding = vec2(0, 0)

    if WIDTH ~= self.cells.x * self.cellSize then
        padding.x = (WIDTH - (self.cellSize * self.cells.x)) / 2
    else
        padding.y = (HEIGHT - (self.cellSize * self.cells.y)) / 2
    end

    return padding

end

function Grid:moveCell(touch, move)

    -- move by touch
    local delta = vec2(0, 0)
    if touch ~= nil then
        delta = vec2(touch.x - self.startTouch.x, touch.y - self.startTouch.y)
    end

    local i = self.usingCell.x
    local k = self.usingCell.y

    if math.abs(delta.x) > math.abs(delta.y) or move == "right" or move == "left" then

        -- right
        if delta.x > 0 or move == "right" then
            if self:validMove(i + 1, k) == false then return end
            self.value[i + 1][k] = self.value[i][k]
            self.value[i][k] = 0
        --end

        -- left
        elseif delta.x < 0 or move == "left" then
            if self:validMove(i - 1, k) == false then return true end
            self.value[i - 1][k] = self.value[i][k]
            self.value[i][k] = 0
        end

    else

        -- up
        if delta.y > 0 or move == "up" then
            if self:validMove(i, k + 1) == false then return end
            self.value[i][k + 1] = self.value[i][k]
            self.value[i][k] = 0
        --end

        -- down
        elseif delta.y < 0 or move == "down" then
            if self:validMove(i, k - 1) == false then return end
            self.value[i][k - 1] = self.value[i][k]
            self.value[i][k] = 0
        end

    end

    -- check for win
    if self:checkWin() == true then win.win = true else win.win=false end

end

function Grid:validMove(i, k)

    -- out of bounds
    if self:validCell(i, k) == false then return false end

    -- cell not empty
    if self.value[i][k] ~= 0 then return false end

    return true

end

function Grid:validCell(i, k)

    -- out of bounds
    if i < 1 then
        return false
    elseif i > self.cells.x then
        return false
    elseif k < 1 then
        return false
    elseif k > self.cells.y then
        return false
    end

    return true

end

function Grid:randomMove()

    local index = vec2(nil, nil)

    -- find empty cell
    for i = 1, self.cells.x do
        for k = 1, self.cells.y do
            if self.value[i][k] == 0 then
                index = vec2(i, k)
            end
        end
    end

    -- random move
    while 1 do

        local rand = math.random(1,4)

        -- right
        if rand == 1 and self:validCell(index.x - 1, index.y) == true then
            self.usingCell = vec2(index.x - 1, index.y)
            self:moveCell(nil, "right")
            break
        end

        -- left
        if rand == 2 and self:validCell(index.x + 1, index.y) == true then
            self.usingCell = vec2(index.x + 1, index.y)
            self:moveCell(nil, "left")
            break
        end

        -- up
        if rand == 3 and self:validCell(index.x, index.y - 1) == true then
            self.usingCell = vec2(index.x, index.y - 1)
            self:moveCell(nil, "up")
            break
        end

        -- down
        if rand == 4 and self:validCell(index.x, index.y + 1) == true then
            self.usingCell = vec2(index.x, index.y + 1)
            self:moveCell(nil, "down")
            break
        end

    end

    self.usingCell = vec2(nil, nil)

end

function Grid:checkWin()

    for i = 1, self.cells.x do
        for k = 1, self.cells.y do
                if self.value[i][k] ~= self:getInitialValue(i, k) then return false end
        end
    end
    return true

end


--# Win
Win = class()

function Win:init()

    self.win = false

end

function Win:draw()

    local size = vec2(200, 100)
    local position = vec2((WIDTH / 2) - (size.x / 2), (HEIGHT / 2) - (size.y / 2))

    fill(66, 66, 106, 255)
    rect(position.x, position.y, size.x, size.y)

    fill(255, 255, 255, 255)
    text("Solved!", WIDTH / 2, HEIGHT / 2)

end

function Win:touched(touch)

end

Thank you both!