Skip to content

Instantly share code, notes, and snippets.

@dave1707
Created June 8, 2012 17:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dave1707/2896942 to your computer and use it in GitHub Desktop.
Save dave1707/2896942 to your computer and use it in GitHub Desktop.
sudoku
-- sudoku solver
-- written for the iPad using Codea
--# board
board = class()
function board:squares()
    -- draw 9x9 board with red or green squares
    local x, y, z
    rectMode(CENTER)
    strokeWidth(0)
    z = 0
    for y=9,1,-1 do
        for x=1,9 do
            z = z + 1
            fill(255,0,0,255)
            if btabs[z] == 1 then
                fill(0,128,0,256)
            end
            rect(50+x*60,250+y*60,58,58)
        end
    end
    rectMode(STANDARD)
end
    
function board:values()
    -- draw the selection row with red or green squares
    local x
    rectMode(CENTER)
    for x=1,9 do
        fill(255,0,0,255)
        if selected == x then
            fill(0,128,0,256)
        end
        rect(50+x*60,200,58,58)
    end
    for x=1,9 do
        fill(255,255,255)
        text(x,50+x*60,200) 
    end
    rectMode(STANDARD)    
end
function board:border()
    -- draw border arount 9x9 board
    strokeWidth(10)
    noFill()
    rect(70,270,560,560)
    
    strokeWidth(1)
    fill(255,255,255,255)
end
function board:lines()
    -- draw the lines to define the 3x3 squares
    strokeWidth(2)
    line(260,280,260,820)
    line(440,280,440,820)
    line(80,640,630,640)
    line(80,460,630,460)
end
function board:numbers()
    -- display the numbers in the 9x9 board squares
    local x, y
    local v = 0
    for y=9,1,-1 do
        for x=1,9 do
            v = v + 1
            if btabv[v] == 0 then
                text("",50+x*60,250+y*60)
            else
                text(btabv[v],50+x*60,250+y*60)  
            end     
        end
    end
end
function board:touch(touch)
    -- check if the select, board, or buttons were touched
    if count == 0 then
        board:selectindex(touch.x,touch.y)
        board:boardindex(touch.x,touch.y)
    end
    button:pressed(touch.x,touch.y)   
end
function board:selectindex(x,y)
    -- get the value that was selected in the select row
    local z
    for z = 1,9 do
        if x > stabl[z] and x < stabr[z] then
            if y > stabb[z] and y < stabt[z] then
                selected = z
                return
            end
        end
    end      
end
function board:boardindex(x,y)
    -- put the number selected into the 9x9 board square selected
    local z
    for z = 1,81 do
        if x > btabl[z] and x < btabr[z] then
            if y > btabb[z] and y < btabt[z] then
                if btabv[z] ~= 0 then
                    btabv[z] = 0
                    btabs[z] = 0
                    return
                end
                if check:duplicates(selected,z) == 0 then   
                    btabv[z] = selected
                    btabs[z] = 1
                    return
                end
            end
        end
    end     
end
--# button
button = class()
function button:show()
    -- show title and buttons with one call
    button:title()
    button:start(btstart)
    button:exit(btexit)
    button:pause(btpause)
end
function button:set(s,e,p)
    -- set the different colors for the buttons
    btstart = s
    btexit = e
    btpause = p
end
function button:title()
    -- show title
    fill(50,50,50,255)
    rectMode(CENTER)
    rect(350,975,200,50)
    fill(255)
    text("Sudoku Solver",350,975)
    rectMode(STANDARD)        
end
function button:solution()
    -- show solution value
    str = string.format("Solution %d",solution)
    rectMode(CENTER)
    fill(255,0,0,255)
    rect(200,100,150,50)
    fill(255)
    text(str,200,100)
    rectMode(STANDARD)        
end
function button:count()
    -- show count value
    local str
    str = string.format("Count %d",count)
    rectMode(CENTER)
    fill(255,0,0,255)
    rect(500,100,150,50)
    fill(255)
    text(str,500,100)
    rectMode(STANDARD)        
end
function button:start(x)
    -- show start, next, or done button
    local str
    if x == 0 then
        fill(0,0,128,255)
    else
        fill(0,128,0,255)
    end
    rectMode(CENTER)
    rect(150,900,100,50)
    fill(255)
    if count > 0 then
        str = "NEXT"
    else
        str = "START"
    end
    if offset < 1 then
        str = "DONE"
    end    
    text(str,150,900)
    rectMode(STANDARD)    
end
function button:exit()
    -- show exit button
    fill(0,0,128,255)
    rectMode(CENTER)
    rect(350,900,100,50)
    fill(255)
    text("EXIT",350,900)
    rectMode(STANDARD)    
end
function button:pause(x)
    -- show pause button
    if x == 0 then
        fill(0,0,128,255)
    else
        fill(0,128,0,255)
    end
    rectMode(CENTER)
    rect(550,900,100,50)
    fill(255)
    text("PAUSE",550,900)
    rectMode(STANDARD)     
end
function button:pressed(x,y)
    -- check if start button was selected
    if x > bstart[1] and x < bstart[2] then
        if y > bstart[3] and y < bstart[4] then
            if offset < 1 then
                return
            end
            selected = 0
            board:values()
            button:set(1,0,0)
            button:show()
            return
        end
    end  
    
    -- check if exit button was selected
    if x > bexit[1] and x < bexit[2] then
        if y > bexit[3] and y < bexit[4] then
            close()
        end
    end
        
    -- check if pause button was selected
    if x > bpause[1] and x < bpause[2] then
        if y > bpause[3] and y < bpause[4] then
            button:set(0,0,1)
            button:show()       
            return
        end
    end          
end
--# check
check = class()
function check:horizontal(s,i)
    -- check if there are duplicate horizontal numbers
    local val, z
    val = math.ceil(i/9)-1
    val = val * 9 + 1
    for z=val,val+8 do
        if btabv[z] == s then
            return(1)    -- dups
        end
    end   
    return(0)    -- no dups
end
function check:vertical(s,i)
    -- check if there are duplicate vertical numbers
    local val, z
    val = math.fmod(i,9)
    for z=val,val+72,9 do
        if btabv[z] == s then
            return(1)    -- dups
        end
    end
    return(0)    -- no dups 
end
function check:square(s,i)
    -- check if there are duplicate numbers in the 3x3 square
    local w1, w2, w3, x, z
    w1 = i - 1
    w2 = math.floor(w1 / 9) * 9
    w3 = math.fmod(w1,3) + w2 - math.floor(w2 / 27) * 27
    x = i - w3
    for z = 1,9 do
        if btabv[x] == s then
            return(1)    -- dup
        end
        x = x + 1
        if z == 3 or z == 6 then
            x = x + 6
        end
    end  
    return(0)    -- no dups   
end
function check:duplicates(s,i)
    -- check for duplicates
    if check:horizontal(s,i) == 1 then
        return(1)    -- dup
    end
    if check:vertical(s,i) == 1 then
        return(1)    -- dup
    end
    if check:square(s,i) == 1 then
        return(1)    -- dup
    end
    return(0)    -- no dups
end
--# Main
function setup()
    
    displayMode(FULLSCREEN)
    background(40, 40, 50)
    backingMode(RETAINED)   
    
    -- define variables
    add = 1
    btstart = 0
    btexit = 0
    btpause = 1
    selected = 0
    solution = 0
    count = 0    
    offset = 1
    
    -- tables used for the 9x9 board
    btabv={}
    btabs={}
    btabl={}
    btabr={}
    btabt={}
    btabb={}
    
    -- tables used for the selectio numbers
    stabl={}
    stabr={}
    stabt={}
    stabb={}
    
    -- tables used for the buttons
    bstart={}
    bexit={}
    bpause={}    
    
    -- initialize the tables   
    tables:board()
    tables:select()
    tables:set()
    tables:buttons()
end
-- This function gets called once every frame
function draw()
    -- draw everything once per cycle
    board:squares()
    board:border()
    board:values()
    board:lines()
    board:numbers()
    button:show()
    search:answer()
    button:count()
    button:solution()
end
function touched(touch)
    -- something was touched
    if touch.state == ENDED then
        board:touch(touch)
    end      
end
--# search
search = class()
function search:answer()
    -- search for a solution
    if btstart == 0 then
        return
    end
    
    local loop = 0 
    -- do a maximun of 100 checks per draw cycle
    while loop < 100 do
        count = count + 1
        if btabs[offset] == 1 then
            if add == 1 then
                search:plus()
            else
                search:minus()
            end
        end
        
        -- no more solutions found
        if offset < 1 then
            button:set(0,0,1)
            button:count()
            return
        end
        
        -- a solution was found
        if offset > 81 then
            offset = 81
            add = 0
            
            -- comment out next line to not stop at a solution
            button:set(0,0,1)
            
            button:show()
            solution = solution + 1
            button:count()
            return
        end
        
        -- increment square values and inc or dec offset
        if btabv[offset] < 9 then
            btabv[offset] = btabv[offset] + 1
            local h
            h = btabv[offset]
            btabv[offset] = 0
            if check:duplicates(h,offset) == 0 then
                btabv[offset] = h                  
                search:plus()
                add = 1
            else
                btabv[offset] = h
            end             
        else
            btabv[offset] = 0
            add = 0
            search:minus()
        end
        loop = loop + 1
    end        
end
function search:minus()
    -- decrement the offset
    local x = 1
    while x == 1 do
        offset = offset - 1
        if offset == 0 then
            return
        end
        if btabs[offset] ~= 1 then
            x = 0
        end
    end            
end
function search:plus()
    -- increment the offset
    local x = 1
    while x == 1 do
        offset = offset + 1
        if offset > 81 then
            return
        end
        if btabs[offset] ~= 1 then
            x=0
        end
    end
end
--# tables
tables = class()
function tables:set()
    -- set values in the 9x9 table
    local x
    for x=1,81 do
        btabs[x] = 0
        btabv[x] = 0
    end
end
function tables:select()
    -- set left, right, bottom, top values for the select table
    local x
    for x=1,9 do
        table.insert(stabl,x,50+x*60-29)
        table.insert(stabr,x,50+x*60+29)        
        table.insert(stabb,x,200-29) 
        table.insert(stabt,x,200+29)
    end                  
end
function tables:board()
    -- set left, right, bottom, top button values for 9x9 board
    local x, y
    local v = 0
    for y=9,1,-1 do
        for x=1,9 do
            v = v + 1
            btabv[v] = ""    
            table.insert(btabl,v,50+x*60-29)
            table.insert(btabr,v,50+x*60+29)        
            table.insert(btabb,v,250+y*60-29) 
            table.insert(btabt,v,250+y*60+29)            
        end
    end
end
function tables:buttons()
    -- set left, right, bottom, top values for the buttons
    bstart[1] = 100    -- left
    bstart[2] = 200    -- right
    bstart[3] = 875    -- bottom
    bstart[4] = 925    -- top
   
    bexit[1] = 300
    bexit[2] = 400
    bexit[3] = 875
    bexit[4] = 925
   
    bpause[1] = 500
    bpause[2] = 600
    bpause[3] = 875
    bpause[4] = 925               
end
-- end of program
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment