
local blockSize = 8           -- the block width/height in pixels
local gameRows  = 36          -- the game height in number of blocks 
local gameCols  = 28          -- the game width in number of blocks
local gameCells = {}          -- hash with game arena
local FOODCYCLE = 600         -- num. cycles food lasts on screen

local BG_RED = 255            -- playfield background color        
local BG_GRE = 255
local BG_BLU = 255

-- init cells hash table
for x=0, gameCols-1 do
        gameCells[x]={}
        for y=0, gameRows-1 do
                gameCells[x][y]={}
                gameCells[x][y]['snake']=0      -- if >0 is the #cycle the snake seg lasts on screen
                gameCells[x][y]['snake_dir']=0  -- if >0 is the dir of the snake seg  
                gameCells[x][y]['food']=0       -- if >0 identify food and #cycles it lasts on screen
        end
end


local snake_x          -- snake head x position in game arena 
local snake_y          -- snake head y position in game arena
local snake_direction=1      -- snake direction of movement 
local snake_xvel = {0,1,0,-1}-- velocity x in the 4 direction       
local snake_yvel = {-1,0,1,0}
local snake_len        -- current snake length
game_score=0           -- current score during game

local food_x           -- where is the food
local food_y
local food_time         

gameHiScores={}  
game_score_rank = 0
game_score_record = 0

local game_rec = "10,1|30,2|60,3|45,4|30,1|60,2|"
local game_rec_pointer = 1
local game_rec_cycle = 0

local game_turn_trig    -- flag to avoid too often change direction
local game_speed_cycle = 3

------------------------------------------------------
-- Execute moves recorded in game_rec variable
-- my goal is to replay the best score run, but i'm too lazy ;D
function doReplay()


        local s,e,line
        
        if game_rec_pointer==1 then
                initGame()
        end 
        
        if game_rec_cycle<=0 then       -- every game_rec_cycle get a new move from recorded play
                
                s = string.find (game_rec, "|",game_rec_pointer)        
                if s==null then
                        game_rec_pointer= 1
                        s = string.find (game_rec, "|",game_rec_pointer) 
                end
                line=string.sub (game_rec, game_rec_pointer, s-1)
                game_rec_pointer = s+1          
                s = string.find (line, ",")
                game_rec_cycle = tonumber(string.sub (line, 1, s-1))
                s = tonumber(string.sub (line, s+1))
                turn(s)                 -- execute turn in s direction
        end
        
        game_rec_cycle = game_rec_cycle-1
end

------------------------------------------------------
-- Draw the hiscore screen with about and score table
-- A dummy run goes in background 
function doHighscores()

        doReplay()      -- during hiscore play a dummy run
        doGame()      
          
        local LINESPACE=12
        local SCOREHEAD_Y=35
        local SCORESTART_Y=SCOREHEAD_Y+15 
        local HELPSTART_Y=180
        local CREDITSSTART_Y=235

        setfont(font, fontSize1, 0, 0, 0)
         
        drawtext(font, 80,SCOREHEAD_Y,   " HighScores")
        drawtext(font, 80,HELPSTART_Y,   "How to play") 
        drawtext(font, 80,CREDITSSTART_Y,"   Credits")
        
        setfont(font, fontSize2, 0, 0, 0)
        drawtext(font, 20,HELPSTART_Y + LINESPACE*1,"Use Left/Right/Up/Down key to turn")
        drawtext(font, 20,HELPSTART_Y + LINESPACE*2,"Use Fire button to pause/unpause")
        drawtext(font, 20,HELPSTART_Y + LINESPACE*3,"Tap screen to start") 
              
        drawtext(font, 30,CREDITSSTART_Y + LINESPACE*1,"yaSnake (yet another!) snake clone")
        drawtext(font, 60,CREDITSSTART_Y + LINESPACE*2,"written by umbiomar for")
        drawtext(font, 70,CREDITSSTART_Y + LINESPACE*3,"www.fbacreator.com")
        drawtext(font, 85,CREDITSSTART_Y + LINESPACE*4,"www.feejo.com")
        
        -- score table
        local i, rank, plyr, score
        
        for i=1, 10 do
                rank= i
                plyr= gameHiScores[i]['player']
                score= gameHiScores[i]['score']
                
                drawtext(font, 20,SCORESTART_Y+(i-1)*LINESPACE, rank)
                drawtext(font, 40,SCORESTART_Y+(i-1)*LINESPACE, plyr)
                drawtext(font, 190,SCORESTART_Y+(i-1)*LINESPACE, score)
        end

end -- doHighscores

-----------------------------------------------------
-- Core loop. Manage snake movement, collision and food.
-- Finally draw the new scene
function doGame()

        local xnew,ynew         -- new snake position
        
        game_speed_cycle= game_speed_cycle-1
        if (game_speed_cycle<0) then
                -- reset game_speed_cycle based on game_score 
                if  game_score>=0    then game_speed_cycle= 3 end
                if  game_score>=100  then game_speed_cycle= 2 end
                if  game_score>=500  then game_speed_cycle= 1 end
                if  game_score>=1000 then game_speed_cycle= 0 end       
                
                -- evaluate new snake position based on current direction of movement
                xnew= snake_x + snake_xvel[snake_direction]     
                ynew= snake_y + snake_yvel[snake_direction]
                              
                -- check wall collision
                if xnew<0 then xnew=gameCols-1 end      
                if ynew<0 then ynew=gameRows-1 end
                if xnew>gameCols-1 then xnew=0 end
                if ynew>gameRows-1 then ynew=0 end
                
                -- check snake tail collision
                if gameCells[xnew][ynew]['snake']~= 0 then
                        playsound(SNDGAMEOVER)
                        gameOver()              -- loose you eated yourself
                        return        
                end 
                
                -- check food eat
                if gameCells[xnew][ynew]['food']> 0 then
                        playsound(SNDGROW)
                        snake_len=snake_len+2           -- increase lenght
                        game_score=game_score+10        -- increase score
                        
                        gameCells[xnew][ynew]['food']= 0        -- place new food
                        placeFood()  
                else
                        doUpdateCells(xnew,ynew)        -- update the game arena
                end
                
                snake_x= xnew                   
                snake_y= ynew
                gameCells[snake_x][snake_y]['snake']= snake_len         -- place there the snake head  
                gameCells[snake_x][snake_y]['snake_dir']= snake_direction       -- save seg direction       
        
        end
        
        doUpdateFood()    -- update food time
        
        drawGameArena()   -- draw the player
        drawGUI()         -- draw score text and other GUI related stuff
         
        game_turn_trig= true    -- after a movement we'r available for a direction change         
end -- doGame

-----------------------------------------------------
--  When game is over verify score for hall of fame
function gameOver()
        local i=0
        game_rec_pointer = 1
        
        -- if score is eligible for HallOfFame get player name
        i=scoreGoesToHallOfFame()

        if i>0 then
                game_score_rank= i
                game_score_record= game_score
                CURRENT_STATE = STATE_INSERTNAME
        else    -- else go to hiscore screen
                CURRENT_STATE = STATE_HIGHSCORES
        end
end

-----------------------------------------------------
-- Return true if score goes to hall of fame 
function scoreGoesToHallOfFame()

        local i,j
        for i=1, 10 do  -- cycle hiscore table
                -- if score enters the top ten...
                if gameHiScores[i]['score'] <= game_score then
                        -- shift down the scores
                        for j=10, i+1,-1 do
                                gameHiScores[j]['player']=gameHiScores[j-1]['player']
                                gameHiScores[j]['score']=gameHiScores[j-1]['score']
                        end
                        return i
                end
        end
        return 0
end


-----------------------------------------------------
-- Decrease food timer. If zero place new food
function doUpdateFood()

        local cell=gameCells[food_x][food_y]    -- get food cell
        
        cell['food']=cell['food']-1             -- decrease its timer
        if cell['food']<=0 then                 -- if <zero place new food
                placeFood()
        end
end

-----------------------------------------------------
-- Cycle all the playfield decreasing "snake" value
function doUpdateCells(xnew,ynew)
        
        local i, j
        for i=0, gameCols-1 do          -- cycle cols
                for j=0, gameRows-1 do  -- cycle rows
                        if ((i ~= xnew) or (j ~= ynew)) then  -- exept the "growing" snake head 
                                 
                                gameCells[i][j]['snake']= gameCells[i][j]['snake']-1
                                if gameCells[i][j]['snake']<0 then
                                        gameCells[i][j]['snake']=0        
                                end
                        end
                end
        end
end

-----------------------------------------------------
-- Init structure and variables for a new game
function initGame()

        -- init cells hash table
        local i, j
        for i=0, gameCols-1 do
                for j=0, gameRows-1 do
                        gameCells[i][j]['snake']= 0     -- no snake
                        gameCells[i][j]['snake_dir']= 0 -- no dir 
                        gameCells[i][j]['food']= 0      -- no food
                end
        end
        
        -- reset variables
        game_score= 0
        snake_x= math.floor(gameCols/2)         -- center snake in the arena             
        snake_y= math.floor(gameRows/2)
        snake_direction= UP                     -- start moving up
        snake_len= 1
        gameCells[snake_x][snake_y]['snake']= snake_len
        food_x= 0                               
        food_y= 0
        food_time= FOODCYCLE/20
        placeFood()                             
end

-----------------------------------------------------
-- Start a new game function
function startGame()

        initGame()  -- init structures and functions     
                
        -- finally set current state to playing
        CURRENT_STATE = STATE_PLAYING
end -- startGame

--------------------------------------------------
-- Draw the game arena
function drawGameArena()

        local r,g,b 
        local cell
        local x,y,i,j
        for i=0, gameCols-1 do                  -- cycle cols
                for j=0, gameRows-1 do          -- cycle rows
                        x=SCREEN_ORIG_X+i*blockSize
                        y=SCREEN_ORIG_Y+j*blockSize
                        cell=gameCells[i][j]    -- get a cell reference
                        r,g,b=BG_RED,BG_GRE,BG_BLU 
                        -- if snake
                        if cell['snake']>0 then 
                                r,g,b=0,0,255   -- color is blue   
                                if snake_len>3 then     -- is snake longer than 3cell
                                        -- draw the last 3 cell tail faded to white 
                                        if cell['snake']==3 then r,g,b=77,77,255 end
                                        if cell['snake']==2 then r,g,b=184,184,255 end
                                        if cell['snake']==1 then r,g,b=230,230,255 end
                                end     
                        end
                                                
                        -- if food
                        if cell['food']>0 then
                                r,g,b=0,255,0   -- color is green
                        end
                        fillrect(x, y, x+blockSize, y+blockSize, r, g, b)
                end
        end

end

-----------------------------------------------------
-- Draw border of the game arena
function drawBorder()

        local x= SCREEN_ORIG_X
        local y= SCREEN_ORIG_Y
        
        local h= blockSize* gameRows   
        local w= blockSize* gameCols 
        
        local x1,y1,x2,y2

        fillrect(x+3,y+3,x+w+6,y+h+6,192,192,192)
        
        x1=x-3
        y1=y-3
        x2=x1+w+5
        y2=y1+h+5
        fillrect(x1,y1,x2,y2,BG_RED,BG_GRE,BG_BLU)
        rect(x1,y1,x2,y2,0,0,0)
        
        x1=x-1
        y1=y-1
        x2=x1+w+1
        y2=y1+h+1
        rect(x1,y1,x2,y2,0,0,0)

end

-----------------------------------------------------
-- Draw the GUI actually score e food timer
function drawGUI()

        local y=315
        
        setfont(font, fontSize2, 0, 0, 0)
        drawtext(font, 10,y,"Score: " .. game_score)    -- draw score
        
        local modulo                                    -- draw timer  
        local a= gameCells[food_x][food_y]['food'] 
        modulo= a - math.floor(a/20)*20
        if (modulo==0) then                             -- once a second
                food_time=a/20
        end
        drawtext(font, 150,y,"Food time: " .. food_time .. " sec") 
        
        rect(215,10,215+10,10+10,0,0,0)
        line(215,10,215+10,10+10,0,0,0)
        line(215,10+10,215+10,10,0,0,0)
        drawtext(font,190,20,"quit")
end

-----------------------------------------------------
-- Place a food cell randomly
function placeFood()
 
        local x,y
        local cell 
              
        math.randomseed (clock())       -- init random seed
     
        repeat                          -- repeat until we found a "not snake" cell
                x=math.random (0, gameCols-1)
                y=math.random (0, gameRows-1)
                cell=gameCells[x][y]
        until (cell['snake']==0) and (cell['food']==0)
        
        gameCells[food_x][food_y]['food']=0
        gameCells[x][y]['food']=FOODCYCLE       -- place there food
        
        food_time= FOODCYCLE/20
        
        food_x= x                       -- save position for quick ref
        food_y= y

end

-------------------------------------------------------
-- Point snake head towards new direction
function turn(dir) 
        
        if game_turn_trig==false then           -- avoid subsequent change direction
                return
        end
        
        if snake_direction == dir then          -- if dir == current direction
                return                          
        end
        
        if dir==UP and snake_direction ~= DOWN then     -- avoid self eat
                snake_direction = dir 
                game_turn_trig=false
                return
        end
        
        if dir==DOWN and snake_direction ~= UP then     -- avoid self eat
                snake_direction = dir
                game_turn_trig=false
                return 
        end
                
        if dir==LEFT and snake_direction ~= RIGHT then  -- avoid self eat
                snake_direction = dir
                game_turn_trig=false 
                return
        end
        
        if dir==RIGHT and snake_direction ~= LEFT then  -- avoid self eat
                snake_direction = dir
                game_turn_trig=false 
                return
        end
        
end

--------------------------------------------------------
-- Load hiscore from settings into a hash table
function loadHiscores()

        local sc= ""        
        local i, s, line
        local plyr, score
        
        sc = "empty|00000<empty|00000<empty|00000<empty|00000<empty|00000<empty|00000<empty|00000<empty|00000<empty|00000<empty|00000"
        
        openconfig("hiscores.ini")
        sc= readconfigstring("general", "hiscores", sc)
        
        for i=1, 10 do  -- load the hash table with player and score
                gameHiScores[i]={}
                if string.len(sc)==0 then break end
                s= string.find (sc, "<")
                
                if s==null then s= string.len(sc) end
                
                line= string.sub (sc, 1, s-1)
                sc= string.sub (sc, s+1)
                
                s= string.find (line, "|")
                plyr= string.sub (line, 1, s-1)
                score= string.sub (line, s+1)
                                
                gameHiScores[i]['player']= plyr 
                gameHiScores[i]['score']= score*1.0
        end

end

---------------------------------------------------
-- Save hiscores table in the settings
function saveHiscores()

        local hscore=""
        local i
        
        -- table is saved with the format player1|score1<player2|score2<...
        for i=1, 10 do
                hscore = hscore .. gameHiScores[i]['player'].."|".. gameHiScores[i]['score'].."<"
        end
        openconfig("hiscores.ini")
        writeconfigstring("general", "hiscores", hscore)
end
