Code: Select all
local SCREEN_WIDTH = 800
local SCREEN_HEIGHT = 600
local BLOCKSIZE = 20
local HEIGHT = SCREEN_HEIGHT / BLOCKSIZE
local WIDTH = SCREEN_WIDTH / BLOCKSIZE
local DIRECTION_RIGHT = 1
local DIRECTION_LEFT = 2
local DIRECTION_UP = 3
local DIRECTION_DOWN = 4
local BLOCK_FOOD = 1
local BLOCK_WALL = 2
local LEVEL_LENGTH_MULT = 1.05
local function xy(x, y) return x * WIDTH + y end
local state
local function newfood()
-- list of possible positions for the food
local possible = {}
for y=1, HEIGHT do
for x=1, WIDTH do
if not state.board[xy(x, y)] then
local snakeFound = false
for k, v in ipairs(state.snake) do
if v.x == x and v.y == y then
snakeFound = true
break
end
end
if not snakeFound then
possible[#possible + 1] = {x = x, y = y}
end
end
end
end
local newPos = possible[math.random(1, #possible)]
state.board[xy(newPos.x, newPos.y)] = {type = BLOCK_FOOD, ttl = 10}
end
local function reset()
state = {}
state.snake = {{x = math.random(1, WIDTH), y = math.random(1, HEIGHT)}}
state.board = {}
state.lastMove = 0
state.direction = DIRECTION_RIGHT
state.speed = 0.1
state.length = 8
state.level = 1
state.gameOver = false
newfood()
end
local function drawsnake()
local n = #state.snake
for k, v in ipairs(state.snake) do
local c = k / n * 100
love.graphics.setColor(255 - c, 255 - c, 255 - c)
love.graphics.rectangle('fill', (v.x - 1) * BLOCKSIZE, (v.y - 1) * BLOCKSIZE, BLOCKSIZE, BLOCKSIZE)
end
end
local font = {}
setmetatable(font, {__index = function(t, k)
if rawget(t, k) == nil then
t[k] = love.graphics.newFont(k)
end
return rawget(t, k)
end})
function love.load()
-- preload fonts
do
local f = font
local _ = f[10], f[16], f[24], f[48]
end
math.randomseed(os.time())
math.random()
reset()
end
function love.draw()
for y=1, HEIGHT do
for x=1, WIDTH do
if (x + y) % 2 == 0 then
love.graphics.setColor(30, 30, 30)
else
love.graphics.setColor(20, 20, 20)
end
local block = state.board[xy(x, y)]
if block and block.type == BLOCK_FOOD then
love.graphics.setColor(255, 0, 0)
end
love.graphics.rectangle('fill', (x - 1) * BLOCKSIZE, (y - 1) * BLOCKSIZE, BLOCKSIZE, BLOCKSIZE)
if block and block.ttl then
love.graphics.setColor(255, 255, 255)
local s = ('%d'):format(block.ttl)
love.graphics.print(s, 6 + (x - 1) * BLOCKSIZE, 3 + (y - 1) * BLOCKSIZE)
end
end
end
drawsnake()
love.graphics.setFont(font[10])
love.graphics.setColor(255, 255, 255)
love.graphics.print('Level '..state.level, 10, 10)
if state.gameOver then
love.graphics.setFont(font[48])
love.graphics.setColor(255, 200, 0)
local s = 'GAME OVER'
love.graphics.print(s, ((SCREEN_WIDTH / 2) - (font[48]:getWidth(s) / 2)), 200)
love.graphics.setFont(font[16])
s = 'Press R to restart'
love.graphics.print(s, ((SCREEN_WIDTH / 2) - (font[16]:getWidth(s) / 2)), 400)
end
end
local function collision()
state.gameOver = true
end
function love.update(dt)
if state.gameOver then return end
-- fade blocks
local toRemove = {}
for k, v in pairs(state.board) do
if v.ttl then
v.ttl = v.ttl - dt
if v.ttl < 0 then
toRemove[#toRemove+1] = k
end
end
end
for k, v in ipairs(toRemove) do
if state.board[v].type == BLOCK_FOOD then
newfood()
end
state.board[v] = nil
end
-- only move every state.speed seconds
state.lastMove = state.lastMove + dt
if state.lastMove >= state.speed then
state.lastMove = 0
else
return
end
local x, y
local last = state.snake[#state.snake]
-- calculate new position
if state.direction == DIRECTION_RIGHT then
x = last.x + 1
y = last.y
elseif state.direction == DIRECTION_LEFT then
x = last.x - 1
y = last.y
elseif state.direction == DIRECTION_UP then
x = last.x
y = last.y - 1
elseif state.direction == DIRECTION_DOWN then
x = last.x
y = last.y + 1
end
-- change these if you want to collide with walls
if x > WIDTH then
x = 1
elseif x < 1 then
x = WIDTH
end
if y > HEIGHT then
y = 1
elseif y < 1 then
y = HEIGHT
end
-- limit length of snake
while #state.snake > state.length do
table.remove(state.snake, 1)
end
-- check self collision
for k, v in ipairs(state.snake) do
if x == v.x and y == v.y then
collision()
return
end
end
-- check food collision
local block = state.board[xy(x, y)]
if block and block.type == BLOCK_FOOD then
state.length = state.length * LEVEL_LENGTH_MULT
state.board[xy(x, y)] = nil
newfood()
state.level = state.level + 1
elseif block and block.type == BLOCK_WALL then
collision()
return
end
-- add new snake bit
state.snake[#state.snake+1] = {x = x, y = y}
state.lastDirection = state.direction
end
function love.keypressed(key, code)
if state.gameOver then
if key == 'r' then
reset()
end
else
local d = state.lastDirection or state.direction
if key == 'up' and d ~= DIRECTION_DOWN then
state.direction = DIRECTION_UP
elseif key == 'down' and d ~= DIRECTION_UP then
state.direction = DIRECTION_DOWN
elseif key == 'left' and d ~= DIRECTION_RIGHT then
state.direction = DIRECTION_LEFT
elseif key == 'right' and d ~= DIRECTION_LEFT then
state.direction = DIRECTION_RIGHT
end
end
end