Snakoban


THIS IS STILL A WORK IN PROGRESS

[release]
[tab]What?[/tab]Snakoban is a little java applet made by a guy called Kevan. He describes it as an “idle experiment”
[tab]Why?[/tab] I lost an hour playing it, and I thought “Oh people might like this in gmod to waste time.”, so I made it.
[tab]How?[/tab]Well the first thing I did (just now) was a faithful nearly 1:1 conversion from Java to Lua, just as a starting base. However, I can see a lot of potential for new ways of playing this, with distinct levels and such, so I’ll do that later.
[tab]Won’t he mind?[/tab] Nah, he says the code is free game on his website. (See below)
[tab]Pics[/tab]

[tab]Original?[/tab]Original Game - Original Source
[tab]How do I use this?[/tab] Put the code below in a clientside autorun file, then put play_snakoban in console and hit enter.

[tab]Instructions?[/tab]

[tab]Improvements?[/tab] This code could do with tons, as it’s a fairly direct java2lua conversion. Please don’t comment on the code, I’m going to make a cleaned up version later. // comments are his direct from the source, all others are mine.
[tab]Code[/tab][lua]–[[
~ Snakoban (Original) ~
~ Original code/concept by Kevan Davis, 26/8/03 ~
~ http://kevan.org/proce55ing/snakoban/ ~
~ Lua-ified by Lexi, 19/10/09 ~
~ This version is a faithful convertion
using the original variable names,
comments, general structure and gameplay. ~
–]]
– Lua quirk. Define the functions here first
– to make sure they can all see each other.
– Also,make them local to prevent leakage.
local setup,loop,addBlock,keyPressed,moveSnake

local square = {}
local tailage = {}

local gamemode = 1
local speed = 200
local level, step

local head = 1
local tail = 2
local block = 3
local hole = 4
local fill = 5

local boardsize = 10
local squaresize = 20
local snakelength

local offx, offy = 0,0

// Setup function, automatically called once at the start.
function setup()
// Fill the board with blank squares, except for the edges, which
// start as filled holes.
for i = 1,boardsize do
square* = {}
for j = 1,boardsize do
if i == 1 or j == 1 or i == boardsize or j == boardsize then
square*[j] = fill
else
square*[j] = 0
end
end
tailage* = {}
end
– The colours that were defined here are now in the draw function.

// Call the "add a block-and-hole at random" function, five times.
addBlock()
addBlock()
addBlock()
addBlock()
addBlock()

// Set the starting snake length.
snakelength = 5

// Place the snake.
for i = 1, snakelength do
	square[i+1][2] = tail
	tailage[i+1][2] = snakelength - (i-1)
end
square[snakelength+1][2] = head

// Set the level and step. (Level is how many blocks you need to
// clear to finish that level; step is how many have been cleared.)
level,step = 1,0

end

// The “loop” function is automatically called again and again,
// forever, by Proce55ing. It’s the main game loop.
–[[
In this case, it’s a think used to detect the keypresses
and move the snake.
–]]
local nextthink = 0
function loop()
–[[
This stuff has been moved to the derma draw.
~ Lexi
–]]
if nextthink > CurTime() then return end
nextthink = CurTime() + (speed / 1000)
– Check for keys.
keyPressed()
– Move the snake.
moveSnake()
end

// Function to add a block at random.
function addBlock()
for i = 0,200 do
// Pick a random x and y, and see if that square is empty.
// If it is, change it to a block and set “i” to one, to break
// this loop.
local rx = math.random(boardsize-2)+1
local ry = math.random(boardsize-2)+1
if square[rx][ry] == 0 then
– I don’t know why he does this, it still puts blocks where it shouldn’t.
– Copied directly from the java.
if ((square[rx-1][ry-1] != fill && square[rx][ry-1] != fill &&
square[rx+1][ry-1] != fill && square[rx-1][ry] != fill &&
square[rx+1][ry] != fill && square[rx-1][ry+1] != fill &&
square[rx][ry+1] != fill && square[rx+1][ry+1] != fill &&
square[rx-1][ry-1] != block && square[rx][ry-1] != block &&
square[rx+1][ry-1] != block && square[rx-1][ry] != block &&
square[rx+1][ry] != block && square[rx-1][ry+1] != block &&
square[rx][ry+1] != block && square[rx+1][ry+1] != block)
|| i>198) then
square[rx][ry] = block
break
end
end
end
for i = 0, 500 do
// Same again, to place a hole.
local rx = math.random(boardsize-2)+1
local ry = math.random(boardsize-2)+1
if square[rx][ry] == 0 then
if ((square[rx-1][ry-1] != fill && square[rx][ry-1] != fill &&
square[rx+1][ry-1] != fill && square[rx-1][ry] != fill &&
square[rx+1][ry] != fill && square[rx-1][ry+1] != fill &&
square[rx][ry+1] != fill && square[rx+1][ry+1] != fill &&
square[rx-1][ry-1] != block && square[rx][ry-1] != block &&
square[rx+1][ry-1] != block && square[rx-1][ry] != block &&
square[rx+1][ry] != block && square[rx-1][ry+1] != block &&
square[rx][ry+1] != block && square[rx+1][ry+1] != block)
|| i>200) then
square[rx][ry] = hole
break
end
end
end
end

// This functiong gets called by Proce55ing whenever a key is
// pressed…
–[[
In fact, it’s called every ‘speed’ miliseconds, so we need
to make sure a key has actually been pressed.
–]]
function keyPressed()

// If playing as Sokoban, reset the offsets each keypress.
if gamemode == 1 then
	offy,offx = 0,0
end

// See what grid offset the key provokes.
if input.IsKeyDown(KEY_W) then
	offy,offx = -1,0
end
if input.IsKeyDown(KEY_S) then
	offy,offx = 1,0
end
if input.IsKeyDown(KEY_A) then
	offx,offy = -1,0
end
if input.IsKeyDown(KEY_D) then
	offx,offy = 1,0
end
// Toggle mode (1=Sokoban, 2=Snake)
if input.IsKeyDown(KEY_M) then
	gamemode = 3 - gamemode
end

// Restart
if input.IsKeyDown(KEY_Q) then
	setup()
end

// Restart
if input.IsKeyDown(KEY_MINUS) and speed < 200 then
	speed = speed + 10
end
if input.IsKeyDown(KEY_EQUAL) and speed > 0 then
	speed = speed - 10
end

// If in Sokoban mode, move the snake after each keypress.
-- Actually this will happen anyway.

end

function moveSnake()
local headx,heady = 1,1
local oldest,oldestx,oldesty = snakelength,1,1
local finished = 1

// Run through the board to see where the snake's head is, and
// store its coordinates in headx and heady.
for x = 1, boardsize do
	for y = 1, boardsize do
		if square[x][y] == head then
			headx,heady = x,y
		end
	end
end
// See whether "current head position plus offset" is a block.
if square[headx+offx] and square[headx+offx][heady+offy] == block then
	// See whether "current head position plus offset *twice*" is
	// empty; if it is, the block can be pushed. Clever!
	if square[headx+(offx*2)][heady+(offy*2)] == 0 then
		// Move the block.
		square[headx+(offx*2)][heady+(offy*2)] = block
		square[headx+offx][heady+offy] = 0
	end
	if square[headx+(offx*2)] and square[headx+(offx*2)][heady+(offy*2)] == hole then
		// Or fill a hole, for that matter.

		// Add a new block first, so that it doesn't land where
		// the old one was.
		addBlock()

		// If they've finished a level, add another block.
		if step == level then
			step,level = 0,level + 1
			if level == 21 then level = 20 end
			snakelength = snakelength + 1
			addBlock()
		end
		// Fill the hole, kill the block.
		square[headx+(offx*2)][heady+(offy*2)] = 0
		square[headx+offx][heady+offy] = 0
		step = step + 1
	end
end
// See whether "current position plus offset" is empty (or if
// the player has pressed X).
if square[headx+offx] and square[headx+offx][heady+offy]==0 or input.IsKeyDown(KEY_X) then
	// Set the current head to a tail (with "tail age" zero).
	square[headx][heady] = tail
	tailage[headx][heady] = 0

	// Move the head.
	headx = headx + offx
	heady = heady + offy
	square[headx][heady] = head

	// Run through the board aging all the tail bits...
	for x = 1, boardsize do
		for y = 1, boardsize do
			if square[x][y] == block then
				// Oh, and check to see if any blocks are still around.
				// If they are, then the player hasn't finished the level.
				finished=0
				-- I'm not sure why this is in here, it has no effect and is never not called.
			end
			if square[x][y] == tail then
				// Age the tail, see if it's the oldest bit we've found
				// so far.
				tailage[x][y] = tailage[x][y] + 1
				if tailage[x][y] > oldest then
					oldest = tailage[x][y]
					oldestx,oldesty = x,y
				end
			end
		end
	end
	// If the oldest bit is longer than the snake, remove that bit.
	if oldest > snakelength then
		square[oldestx][oldesty] = 0
	end
end

end
// And that’s it! Gosh.
– Apart from the gmod specific code, which comes up now. :3
function surface.SetDrawColorC(color,alpha)
surface.SetDrawColor(color.r,color.g,color.b,alpha or color.a)
end
concommand.Add(“play_snakoban”,function()
setup()
local frame = vgui.Create(“DFrame”)
frame:SetDraggable(true)
frame:SetSizable(false)
frame:SetTitle(“Snakoban”)
frame:SetSize(boardsize * squaresize, boardsize * squaresize + 21)
frame:MakePopup()
frame:Center()
frame:SetDeleteOnClose(true)
local panel = vgui.Create(“Panel”,frame)
panel:SetPos(0,21)
panel:SetSize(boardsize * squaresize, boardsize * squaresize)
function panel:Paint() – This is mostly Kevan’s work again.
local blockr,blockg,blockb = level * 10, 0, 200 - (level * 10)
surface.SetDrawColor(51,51,51,255)
surface.DrawRect(0,0,self:GetSize())
// Loop through and draw each square.
for x = 1, boardsize do
for y = 1, boardsize do
local sq = square[y]
local draw = false
if sq == head then
surface.SetDrawColor(0,200,0,255)
draw = true
elseif sq == tail then
surface.SetDrawColor(0,100,0,255)
draw = true
elseif sq == block then
surface.SetDrawColor(blockr,blockg,blockb,255)
draw = true
elseif sq == hole then
surface.SetDrawColor(0,0,0,255)
draw = true
elseif sq == fill then
surface.SetDrawColor(100,0,0,255)
draw = true
end
if draw then
surface.DrawRect((x-1)*squaresize,(y-1)*squaresize,squaresize,squaresize)
–surface.DrawOutlinedRect(x-1)squaresize + 1,ysquaresize + 1,squaresize - 2,squaresize - 2)
end
end
end
end
panel.Think = loop
end)[/lua]
[/release]
[release]
[tab]What now?[/tab]My version. It has levels, better graphics and some weird shit.
[tab]Is it better?[/tab]Well subjectively. It has more info, less cheats and looks a bit better, but it’s no way near perfect. The modes seem to get progressively easier and I haven’t even managed to get to the final level which is supposed to be quite hard.
[tab]Pix plz[/tab]

[tab]Right. How do I use this one?[/tab] Same way, the command is play_snakoban2.

[tab]Hmm. Code?[/tab]
[lua]–[[
~ Snakoban (Lexi Varient) ~
~ Original code/concept by Kevan Davis, 26/8/03 ~
~ http://kevan.org/proce55ing/snakoban/ ~
~ Made by Lexi, 19/10/09 ~
~ This version is an overhaul, with different code,
game style and play, keeping the concept. ~
–]]
–[[ Functions ]]–
local restart,setup,think,addBox

–[[ Global Storage ]]–
local board = {}
local tail = {}
local ox,oy = 0,0
local hx,hy = 2,2
local frame

–[[ Predefined Config ]]-- -=[ Important note: Editing these will probably break things ]=-
local squaresize = 20 – How many px each board is on the screen
local initialBoard = 10 – How large the board is to start with
local initialSnake = 5 – How long the snake is to start with
local enlargeevery = 5 – Enlarge the board size every x levels

–[[ Constants ]]–
local NONE = 0
local HEAD = 1
local TAIL = 2
local BOX = 3
local HOLE = 4
local FILL = 5

–[[ Runtime Variables ]]–
local step = 0
local score = 0 – Not used until level 25
local speed = 160
local level = 1
local gamemode = 1
local boardsize = initialBoard
local snakelength = initialSnake

–[[ debug ]]–
local boxes = 0

– Restarts the game, setting vars to inital and reloading the board.
function restart()
boardsize = initialBoard
snakelength = initialSnake
level = 1
score = 0
step = 1
ox,oy = 0,0
frame:SetTitle(“Snakoban - Level 1 - 0 / 1”)
setup()
end

–[[
Sets up a new board with the right board size,
spawns the right number of blocks for the level
and adds a snake.
–]]
function setup()
–[[
Fill the board with blank boards, except for the edges, which
start as filled holes.
–]]
boxes = 0
board,tail = {},{}
for x = 1,boardsize do
board = {}
tail = {}
for y = 1,boardsize do
if x == 1 or y == 1 or x == boardsize or y == boardsize then
board[y] = FILL
else
board[y] = NONE
end
end
end
– Add the required number of boxes to pass the level
for i = 1,level do
addBox()
end
– Add some extras to make it easier
addBox()
addBox()

-- Place the snake.
for i = 1, snakelength do
	board[i+1][2] = TAIL
	tail [i+1][2] = snakelength - i
end
board[snakelength+1][2] = HEAD
hx,hy = snakelength+1,2
frame:InvalidateLayout()

end

– A think used to detect the keypresses and move the snake.
local nextthink = 0
function think()
if nextthink > CurTime() then return end
nextthink = CurTime() + (speed / 1000)
// If playing as Sokoban, reset the offsets each think.
if gamemode == 1 then
oy,ox = 0,0
end

// See what grid offset the key provokes.
if 	   input.IsKeyDown(KEY_W) then
	oy,ox = -1,0
elseif input.IsKeyDown(KEY_S) then
	oy,ox =  1,0
elseif input.IsKeyDown(KEY_A) then
	ox,oy = -1,0
elseif input.IsKeyDown(KEY_D) then
	ox,oy =  1,0
end
// Toggle mode (1=Sokoban style, 2=Snake style)
if input.IsKeyDown(KEY_M) then
	gamemode = 3 - gamemode
end

// Restart
if input.IsKeyDown(KEY_Q) then
	restart()
end

// Adjust Speed
if     input.IsKeyDown(KEY_MINUS) and speed < 200 then
	speed = speed + 10
elseif input.IsKeyDown(KEY_EQUAL) and speed > 0 then
	speed = speed - 10
end

-- Move the snake.
// See whether "current head position plus offset" is a box
if board[hx+ox] and board[hx+ox][hy+oy] == BOX then
	// See whether "current head position plus offset *twice*" is
	// empty; if it is, the box can be pushed. Clever!
	if board[hx+ox*2][hy+oy*2] == NONE then
		// Move the block.
		board[hx+ox*2][hy+oy*2] = BOX
		board[hx+ox][hy+oy] = NONE
	elseif board[hx+ox*2] and board[hx+ox*2][hy+oy*2] == HOLE then
		// Or fill a hole, for that matter.
		
		if level == 25 then -- If we're on level 25, we do stuff differently
			score = score + 1 -- Add one to the score
			frame:SetTitle("Snakoban - Level "..level.." - "..score.." boxes cleared.")
			if score % enlargeevery == 0 then -- Every enlargeevery (5) scores we
				snakelength = snakelength + 1 -- Make the snake larger
				if score % (enlargeevery*2) == 0 then -- Every enlargevery*2 (10) scores we
					addBox()	-- Throw in another block/hole pair for fun.
				end
			end
		else
			if step == level then -- If we've pushed enough blocks in holes to advance a level
				step,level = 0, level + 1
				snakelength = snakelength + 1
				frame:SetTitle("Snakoban - Level "..level.." - 0 / "..level)
				if level % enlargeevery == 0 then -- Every enlargevery(5) levels we enlarge the board
					boardsize = boardsize + enlargeevery -- Make the board big enough to fit the new snake
					step = step + 1 -- Add a step because it won't get added otherwise
					setup() -- Make a new board
					return -- Stop the snake moving excessively.
				end
				for i = 1,level do
					addBox() -- Replace the missing boxes
				end
			end
			frame:SetTitle("Snakoban - Level "..level.." - "..step.." / "..level)
		end
		board[hx+ox*2][hy+oy*2] = NONE -- Remove the hole.
		board[hx+ox][hy+oy] = NONE -- Remove the box
		step = step + 1
		boxes = boxes - 1
	end
end
// See whether "current position plus offset" is empty. There was a cheat here, but I removed it.
if board[hx+ox] and board[hx+ox][hy+oy] == NONE then
	// Set the current head to a tail (with "tail age" zero).
	board[hx][hy] = TAIL
	tail [hx][hy] = 0

	// Move the head.
	hx = hx + ox
	hy = hy + oy
	board[hx][hy] = HEAD

	// Run through the board aging all the tail bits...
	for x = 1, boardsize do
		for y = 1, boardsize do
			if board[x][y] == TAIL then
				// Age the tail, see if it's too old.
				tail[x][y] = tail[x][y] + 1
				if tail[x][y] >= snakelength then
					board[x][y] = NONE
				end
			end
		end
	end
end

end

// Function to add a box and a hole at random.
function addBox()
for i = 0,500 do
local rx = math.random(boardsize-4)+2 – This ensures boxes are not put in the gutters.
local ry = math.random(boardsize-4)+2
if board[rx][ry] == NONE then
board[rx][ry] = BOX
boxes = boxes + 1
break
end
end
for i = 0,500 do
local rx = math.random(boardsize-4)+2
local ry = math.random(boardsize-4)+2
if board[rx][ry] == NONE then
board[rx][ry] = HOLE
break
end
end
end

concommand.Add(“play_snakoban2”,function()
if IsValid(frame) then frame:Remove() end
frame = vgui.Create(“DFrame”)
frame.pl = frame.PerformLayout
function frame:PerformLayout()
self:SetSize(boardsize * squaresize, boardsize * squaresize + 21)
self.panel:InvalidateLayout()
return self:pl()
end
frame:SetDeleteOnClose(true)
frame:SetDraggable(true)
frame:SetSizable(false)
frame:SetTitle(“Snakoban”)
local panel = vgui.Create(“Panel”,frame)
function panel:PerformLayout()
self:SetPos(0,21)
self:SetSize(boardsize * squaresize, boardsize * squaresize)
end
function panel:Paint() – This is mostly Kevan’s work again.
local blockr,blockg,blockb = level * 10, 0, 250 - level * 10
surface.SetDrawColor(51,51,51,255)
surface.DrawRect(0,0,self:GetSize())
–[[
surface.SetTextColor(255,255,255,255)
surface.SetFont"DefaultLarge"
surface.SetTextPos(25,20)
surface.DrawText(boxes)
–]]
// Loop through and draw each board.
for x = 1, boardsize do
for y = 1, boardsize do
local sq = board[y]
local draw
if sq == HEAD then
surface.SetDrawColor(0,200,0,255)
draw = true
elseif sq == TAIL then
surface.SetDrawColor(0,50+100*(1-tail[y]/snakelength),0,255)
draw = true
elseif sq == BOX then
surface.SetDrawColor(blockr,blockg,blockb,255)
draw = true
elseif sq == HOLE then
surface.SetDrawColor(0,0,0,255)
draw = true
elseif sq == FILL then
surface.SetDrawColor(100,0,0,255)
draw = true
end
if draw then
surface.DrawRect((x-1)*squaresize,(y-1)*squaresize,squaresize,squaresize)
surface.SetDrawColor(0,0,0,255)
surface.DrawOutlinedRect((x-1)*squaresize,(y-1)*squaresize,squaresize,squaresize)
end
end
end
end
panel.Think = think
frame.panel = panel
restart()
frame:MakePopup()
frame:Center()
end)
[/lua][/release]

I’v played this before, it’s a good idea to release it here in my opinion. Helps pass the time on those slower RP servers, etc. Great job on the conversion.

I first looked at the picture and thought of my art gamemode :V

But then I read a bit, looks and is damn good. Well done.

Seems like “language X” to Lua conversions are catching on. :wink: Fantastic port Lexic.

Ok here we go, as promised an improved version. I haven’t had the chance to test all the way up to level 25, so if someone can do that for me, I’d appreciate it. I think I’ve fixed the slugish controls but I’m not sure.
Please, have a go and this time you can comment on the code/visuals. :v:
[lua]–[[
~ Snakoban (Lexi Varient) ~
~ Original code/concept by Kevan Davis, 26/8/03 ~
~ http://kevan.org/proce55ing/snakoban/ ~
~ Made by Lexi, 19/10/09 ~
~ This version is an overhaul, with different code,
game style and play, keeping the concept. ~
–]]
–[[ Functions ]]–
local restart,setup,think,addBox

–[[ Global Storage ]]–
local board = {}
local tail = {}
local ox,oy = 0,0
local hx,hy = 2,2
local frame

–[[ Predefined Config ]]-- -=[ Important note: Editing these will probably break things ]=-
local squaresize = 20 – How many px each board is on the screen
local initialBoard = 10 – How large the board is to start with
local initialSnake = 5 – How long the snake is to start with
local enlargeevery = 5 – Enlarge the board size every x levels

–[[ Constants ]]–
local NONE = 0
local HEAD = 1
local TAIL = 2
local BOX = 3
local HOLE = 4
local FILL = 5

–[[ Runtime Variables ]]–
local step = 0
local score = 0 – Not used until level 25
local speed = 160
local level = 1
local gamemode = 1
local boardsize = initialBoard
local snakelength = initialSnake

– Restarts the game, setting vars to inital and reloading the board.
function restart()
boardsize = initialBoard
snakelength = initialSnake
level = 1
score = 0
step = 1
ox,oy = 0,0
setup()
end

–[[
Sets up a new board with the right board size,
spawns the right number of blocks for the level
and adds a snake.
–]]
function setup()
–[[
Fill the board with blank boards, except for the edges, which
start as filled holes.
–]]
board,tail = {},{}
for x = 1,boardsize do
board = {}
tail = {}
for y = 1,boardsize do
if x == 1 or y == 1 or x == boardsize or y == boardsize then
board[y] = FILL
else
board[y] = NONE
end
end
end
– Add the required number of boxes to pass the level
for i = 1,level do
addBox()
end
– Add some extras to make it easier
addBox()
addBox()

-- Place the snake.
for i = 1, snakelength do
	board[i+1][2] = TAIL
	tail [i+1][2] = snakelength - i
end
board[snakelength+1][2] = HEAD
hx,hy = snakelength+1,2
frame:InvalidateLayout()

end

– A think used to detect the keypresses and move the snake.
local nextthink = 0
function think()
if nextthink > CurTime() then return end
nextthink = CurTime() + (speed / 1000)
// If playing as Sokoban, reset the offsets each think.
if gamemode == 1 then
oy,ox = 0,0
end

// See what grid offset the key provokes.
if 	   input.IsKeyDown(KEY_W) then
	oy,ox = -1,0
elseif input.IsKeyDown(KEY_S) then
	oy,ox =  1,0
elseif input.IsKeyDown(KEY_A) then
	ox,oy = -1,0
elseif input.IsKeyDown(KEY_D) then
	ox,oy =  1,0
end
// Toggle mode (1=Sokoban style, 2=Snake style)
if input.IsKeyDown(KEY_M) then
	gamemode = 3 - gamemode
end

// Restart
if input.IsKeyDown(KEY_Q) then
	restart()
end

// Adjust Speed
if     input.IsKeyDown(KEY_MINUS) and speed < 200 then
	speed = speed + 10
elseif input.IsKeyDown(KEY_EQUAL) and speed > 0 then
	speed = speed - 10
end

-- Move the snake.
// See whether "current head position plus offset" is a box
if board[hx+ox] and board[hx+ox][hy+oy] == BOX then
	// See whether "current head position plus offset *twice*" is
	// empty; if it is, the box can be pushed. Clever!
	if board[hx+ox*2][hy+oy*2] == NONE then
		// Move the block.
		board[hx+ox*2][hy+oy*2] = BOX
		board[hx+ox][hy+oy] = NONE
	elseif board[hx+ox*2] and board[hx+ox*2][hy+oy*2] == HOLE then
		// Or fill a hole, for that matter.
		
		if level == 25 then -- If we're on level 25, we do stuff differently
			score = score + 1 -- Add one to the score
			if score % enlargeevery == 0 then -- Every enlargeevery (5) scores we
				snakelength = snakelength + 1 -- Make the snake larger
				if score % (enlargeevery*2) == 0 then -- Every enlargevery*2 (10) scores we
					addBox()	-- Throw in another block/hole pair for fun.
				end
			end
		else
			if step == level then -- If we've pushed enough blocks in holes to advance a level
				step,level = 0, level + 1
				snakelength = snakelength + 1
				frame:SetTitle("Snakoban - Level "..level.." - 0 / "..level)
				if level % enlargeevery == 0 then -- Every enlargevery(5) levels we enlarge the board
					boardsize = boardsize + enlargeevery -- Make the board big enough to fit the new snake
					setup() -- Make a new board
					return -- Stop the snake moving excessively.
				end
				for i = 1,level do
					addBox() -- Replace the missing boxes
				end
			end
			board[hx+ox*2][hy+oy*2] = NONE -- Remove the hole.
			-- This would be "fill in the hole" if this had individual levels, but it doesn't. D:
			board[hx+ox][hy+oy] = NONE -- Remove the box
			frame:SetTitle("Snakoban - Level "..level.." - "..step.." / "..level)
			step = step + 1
		end
	end
end
// See whether "current position plus offset" is empty. There was a cheat here, but I removed it.
if board[hx+ox] and board[hx+ox][hy+oy] == NONE then
	// Set the current head to a tail (with "tail age" zero).
	board[hx][hy] = TAIL
	tail [hx][hy] = 0

	// Move the head.
	hx = hx + ox
	hy = hy + oy
	board[hx][hy] = HEAD

	// Run through the board aging all the tail bits...
	for x = 1, boardsize do
		for y = 1, boardsize do
			if board[x][y] == TAIL then
				// Age the tail, see if it's too old.
				tail[x][y] = tail[x][y] + 1
				if tail[x][y] > snakelength then
					board[x][y] = NONE
				end
			end
		end
	end
end

end

// Function to add a box and a hole at random.
function addBox()
for i = 0,500 do
local rx = math.random(boardsize-4)+2 – This ensures boxes are not put in the gutters.
local ry = math.random(boardsize-4)+2
if board[rx][ry] == NONE then
board[rx][ry] = BOX
break
end
end
for i = 0,500 do
local rx = math.random(boardsize-4)+2
local ry = math.random(boardsize-4)+2
if board[rx][ry] == NONE then
board[rx][ry] = HOLE
break
end
end
end

concommand.Add(“play_snakoban2”,function()
if IsValid(frame) then frame:Remove() end
frame = vgui.Create(“DFrame”)
frame.pl = frame.PerformLayout
function frame:PerformLayout()
self:SetSize(boardsize * squaresize, boardsize * squaresize + 21)
self.panel:InvalidateLayout()
return self:pl()
end
frame:SetDeleteOnClose(true)
frame:SetDraggable(true)
frame:SetSizable(false)
frame:SetTitle(“Snakoban - Level 1 - 0 / 1”)
local panel = vgui.Create(“Panel”,frame)
function panel:PerformLayout()
self:SetPos(0,21)
self:SetSize(boardsize * squaresize, boardsize * squaresize)
end
function panel:Paint() – This is mostly Kevan’s work again.
local blockr,blockg,blockb = level * 10, 0, 200 - (level * 10)
surface.SetDrawColor(51,51,51,255)
surface.DrawRect(0,0,self:GetSize())
// Loop through and draw each board.
for x = 1, boardsize do
for y = 1, boardsize do
local sq = board[y]
local draw
if sq == HEAD then
surface.SetDrawColor(0,200,0,255)
draw = true
elseif sq == TAIL then
surface.SetDrawColor(0,100,0,255)
draw = true
elseif sq == BOX then
surface.SetDrawColor(blockr,blockg,blockb,255)
draw = true
elseif sq == HOLE then
surface.SetDrawColor(0,0,0,255)
draw = true
elseif sq == FILL then
surface.SetDrawColor(100,0,0,255)
draw = true
end
if draw then
surface.DrawRect((x-1)*squaresize,(y-1)*squaresize,squaresize,squaresize)
surface.SetDrawColor(0,0,0,255)
surface.DrawOutlinedRect((x-1)*squaresize,(y-1)*squaresize,squaresize,squaresize)
end
end
end
end
panel.Think = think
frame.panel = panel
restart()
frame:MakePopup()
frame:Center()
end)
[/lua]

Oh my God, what are you DOING? Shouldn’t those be local functions?

[editline]01:32PM[/editline]

Ballsack. I keep sniffing my own ass in this game.

They are. :v:
Functions are first class values, so line #10, *local restart,setup,think,addBox * means that all those functions are local.
To test, run this snipit.
[lua]do
local a
function a()
print"Foo!"
end
end
a()[/lua]

One line mode for lua_run: lua_run_cl do local a function a() print[[foo]] end end a()

[editline]10:00PM[/editline]

well I’ve been playing mine but when you get to the higher levels, it just gets a bit dull.

I’ve done a bunch of bugfixing, I’m just going to make the tail fade then I’ll update my post. Someone needs to come in and make my version challenging. :saddowns:

Hahaha, ya got me. I saw those, and apparently missed the connection.

Also, I give you MAAAD props for doing a proper language conversion. :v: Included commented code and everything.

Fading snake.
[lua]–[[
~ Snakoban (Lexi Varient) ~
~ Original code/concept by Kevan Davis, 26/8/03 ~
~ http://kevan.org/proce55ing/snakoban/ ~
~ Made by Lexi, 19/10/09 ~
~ This version is an overhaul, with different code,
game style and play, keeping the concept. ~
–]]
–[[ Functions ]]–
local restart,setup,think,addBox

–[[ Global Storage ]]–
local board = {}
local tail = {}
local ox,oy = 0,0
local hx,hy = 2,2
local frame

–[[ Predefined Config ]]-- -=[ Important note: Editing these will probably break things ]=-
local squaresize = 20 – How many px each board is on the screen
local initialBoard = 10 – How large the board is to start with
local initialSnake = 5 – How long the snake is to start with
local enlargeevery = 5 – Enlarge the board size every x levels

–[[ Constants ]]–
local NONE = 0
local HEAD = 1
local TAIL = 2
local BOX = 3
local HOLE = 4
local FILL = 5

–[[ Runtime Variables ]]–
local step = 0
local score = 0 – Not used until level 25
local speed = 160
local level = 1
local gamemode = 1
local boardsize = initialBoard
local snakelength = initialSnake

–[[ debug ]]–
local boxes = 0

– Restarts the game, setting vars to inital and reloading the board.
function restart()
boardsize = initialBoard
snakelength = initialSnake
level = 1
score = 0
step = 1
ox,oy = 0,0
frame:SetTitle(“Snakoban - Level 1 - 0 / 1”)
setup()
end

–[[
Sets up a new board with the right board size,
spawns the right number of blocks for the level
and adds a snake.
–]]
function setup()
–[[
Fill the board with blank boards, except for the edges, which
start as filled holes.
–]]
boxes = 0
board,tail = {},{}
for x = 1,boardsize do
board = {}
tail = {}
for y = 1,boardsize do
if x == 1 or y == 1 or x == boardsize or y == boardsize then
board[y] = FILL
else
board[y] = NONE
end
end
end
– Add the required number of boxes to pass the level
for i = 1,level do
addBox()
end
– Add some extras to make it easier
addBox()
addBox()

-- Place the snake.
for i = 1, snakelength do
	board[i+1][2] = TAIL
	tail [i+1][2] = snakelength - i
end
board[snakelength+1][2] = HEAD
hx,hy = snakelength+1,2
frame:InvalidateLayout()

end

– A think used to detect the keypresses and move the snake.
local nextthink = 0
function think()
if nextthink > CurTime() then return end
nextthink = CurTime() + (speed / 1000)
// If playing as Sokoban, reset the offsets each think.
if gamemode == 1 then
oy,ox = 0,0
end

// See what grid offset the key provokes.
if 	   input.IsKeyDown(KEY_W) then
	oy,ox = -1,0
elseif input.IsKeyDown(KEY_S) then
	oy,ox =  1,0
elseif input.IsKeyDown(KEY_A) then
	ox,oy = -1,0
elseif input.IsKeyDown(KEY_D) then
	ox,oy =  1,0
end
// Toggle mode (1=Sokoban style, 2=Snake style)
if input.IsKeyDown(KEY_M) then
	gamemode = 3 - gamemode
end

// Restart
if input.IsKeyDown(KEY_Q) then
	restart()
end

// Adjust Speed
if     input.IsKeyDown(KEY_MINUS) and speed < 200 then
	speed = speed + 10
elseif input.IsKeyDown(KEY_EQUAL) and speed > 0 then
	speed = speed - 10
end

-- Move the snake.
// See whether "current head position plus offset" is a box
if board[hx+ox] and board[hx+ox][hy+oy] == BOX then
	// See whether "current head position plus offset *twice*" is
	// empty; if it is, the box can be pushed. Clever!
	if board[hx+ox*2][hy+oy*2] == NONE then
		// Move the block.
		board[hx+ox*2][hy+oy*2] = BOX
		board[hx+ox][hy+oy] = NONE
	elseif board[hx+ox*2] and board[hx+ox*2][hy+oy*2] == HOLE then
		// Or fill a hole, for that matter.
		
		if level == 25 then -- If we're on level 25, we do stuff differently
			score = score + 1 -- Add one to the score
			frame:SetTitle("Snakoban - Level "..level.." - "..score.." boxes cleared.")
			if score % enlargeevery == 0 then -- Every enlargeevery (5) scores we
				snakelength = snakelength + 1 -- Make the snake larger
				if score % (enlargeevery*2) == 0 then -- Every enlargevery*2 (10) scores we
					addBox()	-- Throw in another block/hole pair for fun.
				end
			end
		else
			if step == level then -- If we've pushed enough blocks in holes to advance a level
				step,level = 0, level + 1
				snakelength = snakelength + 1
				frame:SetTitle("Snakoban - Level "..level.." - 0 / "..level)
				if level % enlargeevery == 0 then -- Every enlargevery(5) levels we enlarge the board
					boardsize = boardsize + enlargeevery -- Make the board big enough to fit the new snake
					step = step + 1 -- Add a step because it won't get added otherwise
					setup() -- Make a new board
					return -- Stop the snake moving excessively.
				end
				for i = 1,level do
					addBox() -- Replace the missing boxes
				end
			end
			frame:SetTitle("Snakoban - Level "..level.." - "..step.." / "..level)
		end
		board[hx+ox*2][hy+oy*2] = NONE -- Remove the hole.
		board[hx+ox][hy+oy] = NONE -- Remove the box
		step = step + 1
		boxes = boxes - 1
	end
end
// See whether "current position plus offset" is empty. There was a cheat here, but I removed it.
if board[hx+ox] and board[hx+ox][hy+oy] == NONE then
	// Set the current head to a tail (with "tail age" zero).
	board[hx][hy] = TAIL
	tail [hx][hy] = 0

	// Move the head.
	hx = hx + ox
	hy = hy + oy
	board[hx][hy] = HEAD

	// Run through the board aging all the tail bits...
	for x = 1, boardsize do
		for y = 1, boardsize do
			if board[x][y] == TAIL then
				// Age the tail, see if it's too old.
				tail[x][y] = tail[x][y] + 1
				if tail[x][y] >= snakelength then
					board[x][y] = NONE
				end
			end
		end
	end
end

end

// Function to add a box and a hole at random.
function addBox()
for i = 0,500 do
local rx = math.random(boardsize-4)+2 – This ensures boxes are not put in the gutters.
local ry = math.random(boardsize-4)+2
if board[rx][ry] == NONE then
board[rx][ry] = BOX
boxes = boxes + 1
break
end
end
for i = 0,500 do
local rx = math.random(boardsize-4)+2
local ry = math.random(boardsize-4)+2
if board[rx][ry] == NONE then
board[rx][ry] = HOLE
break
end
end
end

concommand.Add(“play_snakoban2”,function()
if IsValid(frame) then frame:Remove() end
frame = vgui.Create(“DFrame”)
frame.pl = frame.PerformLayout
function frame:PerformLayout()
self:SetSize(boardsize * squaresize, boardsize * squaresize + 21)
self.panel:InvalidateLayout()
return self:pl()
end
frame:SetDeleteOnClose(true)
frame:SetDraggable(true)
frame:SetSizable(false)
frame:SetTitle(“Snakoban”)
local panel = vgui.Create(“Panel”,frame)
function panel:PerformLayout()
self:SetPos(0,21)
self:SetSize(boardsize * squaresize, boardsize * squaresize)
end
function panel:Paint() – This is mostly Kevan’s work again.
local blockr,blockg,blockb = level * 10, 0, 250 - level * 10
surface.SetDrawColor(51,51,51,255)
surface.DrawRect(0,0,self:GetSize())
–[[
surface.SetTextColor(255,255,255,255)
surface.SetFont"DefaultLarge"
surface.SetTextPos(25,20)
surface.DrawText(boxes)
–]]
// Loop through and draw each board.
for x = 1, boardsize do
for y = 1, boardsize do
local sq = board[y]
local draw
if sq == HEAD then
surface.SetDrawColor(0,200,0,255)
draw = true
elseif sq == TAIL then
surface.SetDrawColor(0,50+100*(1-tail[y]/snakelength),0,255)
draw = true
elseif sq == BOX then
surface.SetDrawColor(blockr,blockg,blockb,255)
draw = true
elseif sq == HOLE then
surface.SetDrawColor(0,0,0,255)
draw = true
elseif sq == FILL then
surface.SetDrawColor(100,0,0,255)
draw = true
end
if draw then
surface.DrawRect((x-1)*squaresize,(y-1)*squaresize,squaresize,squaresize)
surface.SetDrawColor(0,0,0,255)
surface.DrawOutlinedRect((x-1)*squaresize,(y-1)*squaresize,squaresize,squaresize)
end
end
end
end
panel.Think = think
frame.panel = panel
restart()
frame:MakePopup()
frame:Center()
end)
[/lua]
I can’t think of anything to do now. You’re better at invention, you have a go. :v:

http://img188.imageshack.us/img188/2884/snakoban.png

Challenging.

What the balls?

But doable. :v:

:science:

[editline]10:38PM[/editline]

Updated OP. This syntax highlighter is balls.

[editline]11:02PM[/editline]

Hmm.
I think I may have overdone the difficulty modifier a little.

:laffo:

Final testing version for tonight. This does get progressively harder.
Every box you push into a hole has a 50% chance to respawn and every box that’s on the screen has a 50% chance to still be there every size upgrade.
I can’t seem to get past level 9 though, so it might be a bit too hard. :v:
[lua]–[[
~ Snakoban (Lexi Varient) ~
~ Original code/concept by Kevan Davis, 26/8/03 ~
~ http://kevan.org/proce55ing/snakoban/ ~
~ Made by Lexi, 19/10/09 ~
~ This version is an overhaul, with different code,
game style and play, keeping the concept. ~
–]]
–[[ Functions ]]–
local restart,setup,think,addBox

–[[ Global Storage ]]–
local board = {}
local tail = {}
local ox,oy = 0,0
local hx,hy = 2,2
local frame

–[[ Predefined Config ]]-- -=[ Important note: Editing these will probably break things ]=-
local squaresize = 20 – How many px each board is on the screen
local initialBoard = 10 – How large the board is to start with
local initialSnake = 5 – How long the snake is to start with
local enlargeevery = 5 – Enlarge the board size every x levels

–[[ Constants ]]–
local NONE = 0
local HEAD = 1
local TAIL = 2
local BOX = 3
local HOLE = 4
local FILL = 5

–[[ Runtime Variables ]]–
local step = 0
local boxes = 0
local score = 0 – Not used until level 25
local speed = 160
local level = 1
local gamemode = 1
local boardsize = initialBoard
local snakelength = initialSnake

– Restarts the game, setting vars to inital and reloading the board.
function restart()
boardsize = initialBoard
snakelength = initialSnake
level = 1
score = 0
boxes = 0
step = 1
ox,oy = 0,0
frame:SetTitle(“Snakoban - Level 1 - 0 / 1”)
setup()
end

–[[
Sets up a new board with the right board size,
spawns the right number of blocks for the level
and adds a snake.
–]]
function setup()
–[[
Fill the board with blank boards, except for the edges, which
start as filled holes.
–]]
board,tail = {},{}
for x = 1,boardsize do
board = {}
tail = {}
for y = 1,boardsize do
if x == 1 or y == 1 or x == boardsize or y == boardsize then
board[y] = FILL
else
board[y] = NONE
end
end
end
local a = boxes
boxes = 0
–[
if a > 0 then
for i = 1,a do
if math.random() > 0.5 then
addBox()
end
end
end
–]]
– Add the required number of boxes to pass the level
for i = 1,level do
addBox()
end
– Add some extras for the hell of it
addBox()

-- Place the snake.
for i = 1, snakelength do
	board[i+1][2] = TAIL
	tail [i+1][2] = snakelength - i
end
board[snakelength+1][2] = HEAD
hx,hy = snakelength+1,2
frame:InvalidateLayout()

end

– A think used to detect the keypresses and move the snake.
local nextthink = 0
function think()
if nextthink > CurTime() then return end
nextthink = CurTime() + (speed / 1000)
// If playing as Sokoban, reset the offsets each think.
if gamemode == 1 then
oy,ox = 0,0
end

// See what grid offset the key provokes.
if 	   input.IsKeyDown(KEY_W) then
	oy,ox = -1,0
elseif input.IsKeyDown(KEY_S) then
	oy,ox =  1,0
elseif input.IsKeyDown(KEY_A) then
	ox,oy = -1,0
elseif input.IsKeyDown(KEY_D) then
	ox,oy =  1,0
end
// Toggle mode (1=Sokoban style, 2=Snake style)
if input.IsKeyDown(KEY_M) then
	gamemode = 3 - gamemode
end

// Restart
if input.IsKeyDown(KEY_Q) then
	restart()
end

// Adjust Speed
if     input.IsKeyDown(KEY_MINUS) and speed < 200 then
	speed = speed + 10
elseif input.IsKeyDown(KEY_EQUAL) and speed > 0 then
	speed = speed - 10
end

-- Move the snake.
// See whether "current head position plus offset" is a box
if board[hx+ox] and board[hx+ox][hy+oy] == BOX then
	// See whether "current head position plus offset *twice*" is
	// empty; if it is, the box can be pushed. Clever!
	if board[hx+ox*2][hy+oy*2] == NONE then
		// Move the block.
		board[hx+ox*2][hy+oy*2] = BOX
		board[hx+ox][hy+oy] = NONE
	elseif board[hx+ox*2] and board[hx+ox*2][hy+oy*2] == HOLE then
		// Or fill a hole, for that matter.
		
		if level == 25 then -- If we're on level 25, we do stuff differently
			score = score + 1 -- Add one to the score
			frame:SetTitle("Snakoban - Level "..level.." - "..score.." boxes cleared.")
			if score % enlargeevery == 0 then -- Every enlargeevery (5) scores we
				snakelength = snakelength + 1 -- Make the snake larger
				if score % (enlargeevery*2) == 0 then -- Every enlargevery*2 (10) scores we
					addBox()	-- Throw in another block/hole pair for fun.
				end
			end
		else
			if step == level then -- If we've pushed enough blocks in holes to advance a level
				step,level = 0, level + 1
				snakelength = snakelength + 1
				frame:SetTitle("Snakoban - Level "..level.." - 0 / "..level)
				if level % enlargeevery == 0 then -- Every enlargevery(5) levels we enlarge the board
					boardsize = boardsize + enlargeevery -- Make the board big enough to fit the new snake
					step = step + 1 -- Add a step because it won't get added otherwise
					setup() -- Make a new board
					return -- Stop the snake moving excessively.
				end
				addBox()
			end
			frame:SetTitle("Snakoban - Level "..level.." - "..step.." / "..level)
		end
		if math.random() > 0.5 then
			addBox()
		end
		board[hx+ox*2][hy+oy*2] = NONE -- Remove the hole.
		board[hx+ox][hy+oy] = NONE -- Remove the box
		step = step + 1
	end
end
// See whether "current position plus offset" is empty. There was a cheat here, but I removed it.
if board[hx+ox] and board[hx+ox][hy+oy] == NONE then
	// Set the current head to a tail (with "tail age" zero).
	board[hx][hy] = TAIL
	tail [hx][hy] = 0

	// Move the head.
	hx = hx + ox
	hy = hy + oy
	board[hx][hy] = HEAD

	// Run through the board aging all the tail bits...
	for x = 1, boardsize do
		for y = 1, boardsize do
			if board[x][y] == TAIL then
				// Age the tail, see if it's too old.
				tail[x][y] = tail[x][y] + 1
				if tail[x][y] >= snakelength then
					board[x][y] = NONE
				end
			end
		end
	end
end

end

// Function to add a box and a hole at random.
function addBox()
for i = 0,500 do
local rx = math.random(boardsize-4)+2 – This ensures boxes are not put in the gutters.
local ry = math.random(boardsize-4)+2
if board[rx][ry] == NONE then
board[rx][ry] = BOX
boxes = boxes + 1
break
end
end
for i = 0,500 do
local rx = math.random(boardsize-4)+2
local ry = math.random(boardsize-4)+2
if board[rx][ry] == NONE then
board[rx][ry] = HOLE
break
end
end
end

concommand.Add(“play_snakoban2”,function()
if IsValid(frame) then frame:Remove() end
frame = vgui.Create(“DFrame”)
frame.pl = frame.PerformLayout
function frame:PerformLayout()
self:SetSize(boardsize * squaresize, boardsize * squaresize + 21)
self.panel:InvalidateLayout()
return self:pl()
end
frame:SetDeleteOnClose(true)
frame:SetDraggable(true)
frame:SetSizable(false)
frame:SetTitle(“Snakoban”)
local panel = vgui.Create(“Panel”,frame)
function panel:PerformLayout()
self:SetPos(0,21)
self:SetSize(boardsize * squaresize, boardsize * squaresize)
end
function panel:Paint() – This is mostly Kevan’s work again.
local blockr,blockg,blockb = level * 10, 0, 250 - level * 10
surface.SetDrawColor(51,51,51,255)
surface.DrawRect(0,0,self:GetSize())
–[[
surface.SetTextColor(255,255,255,255)
surface.SetFont"DefaultLarge"
surface.SetTextPos(25,20)
surface.DrawText(boxes)
–]]
// Loop through and draw each board.
for x = 1, boardsize do
for y = 1, boardsize do
local sq = board[y]
local draw
if sq == HEAD then
surface.SetDrawColor(0,200,0,255)
draw = true
elseif sq == TAIL then
surface.SetDrawColor(0,50+100*(1-tail[y]/snakelength),0,255)
draw = true
elseif sq == BOX then
surface.SetDrawColor(blockr,blockg,blockb,255)
draw = true
elseif sq == HOLE then
surface.SetDrawColor(0,0,0,255)
draw = true
elseif sq == FILL then
surface.SetDrawColor(100,0,0,255)
draw = true
end
if draw then
surface.DrawRect((x-1)*squaresize,(y-1)*squaresize,squaresize,squaresize)
surface.SetDrawColor(0,0,0,255)
surface.DrawOutlinedRect((x-1)*squaresize,(y-1)*squaresize,squaresize,squaresize)
end
end
end
end
panel.Think = think
frame.panel = panel
restart()
frame:MakePopup()
frame:Center()
end)[/lua]

Can someone move this back to the main Lua Scripting subforum please. It’s release ready yet, it’s a WIP.