Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
18. Board game
--# Main
-- Flood
function setup()
if backup then backup("Flood 101") end
parameter.integer("Size",10,30,15) --size of board
parameter.action("Start",Start) --starts a new game
parameter.integer("Moves_so_far",0,100,0)
parameter.action("Undo",UndoMove)
--set up palette of six colors
colrs={color(255,0,0,255), color(0,255,0,255), color(0,0,255,255),
color(255,255,0,255),color(255,0,255,255),color(0,255,255,255)}
ss=readProjectData("Size") --read in user's last size selection
if ss~=nil then Size=ss end --set size, if a saved value exists
Start()
end
function Start()
--create random 2D table of colors
board={}--create 1D table
for i=1,Size do --loop through columns
--this is how you create a 2D table
board[i]={} --as we start each new column, create an array of rows
for j=1,Size do --choose random color for each square
board[i][j]=tostring(math.random(1,6))
end
end
state="RUNNING" --to tell draw the game has started
Moves_so_far=0
output.clear()
lastMove=board[1][1]
back=Backup()
saveProjectData("Size",Size) --save user size selection
currSize=Size
print("Press a color at the bottom to change the bottom left hand cell to that color, along with all adjoining cells. Try to flood the whole table with one color in as few moves as possible.")
end
function draw()
background(200)
--calculate the size of square we can fit on screen
local w=(HEIGHT-100)/currSize
pushStyle()
--draw table of colors
local finished=true --see use below
for x=1,currSize do
for y=1,currSize do
fill(colrs[tonumber(board[x][y])]) --set colour based on value in table 1-6
rect(50+x*w-w,75+y*w-w,w,w) --draw square
--if any of the squares are a different color, we haven't finished
if board[x][y]~=board[1][1] then finished=false end
end
end
--draw buttons for user to press, store button locations for touch detection
button={}
for i=1,6 do
fill(colrs[i])
--position button at bottom of screen
local x,y,w,h=100*i,10,75,50
rect(x,y,w,h)
--save details of button position so we can trap touches
button[i]={x=x,y=y,w=w,h=h}
end
popStyle()
--if we've just finished, print a message
if state~="FINISHED" then --we weren't finished last move
if finished then --...but we are now
state="FINISHED"
print("Finished in",Moves_so_far,"moves")
end
end
end
function touched(touch)
--state has three values, BEGAN, MOVING and ENDED
--we only want ENDED, ie when the finger lifts up
if touch.state~=ENDED then return end
--check touch postiion against each button
--that's why we stored all that info about button position
for i=1,6 do
if touch.x>=button[i].x and touch.x<=button[i].x+button[i].w
and touch.y>=button[i].y and touch.y<=button[i].y+button[i].h then
--only react if the user has chosen a different color
if tonumber(board[1][1])~=i then
back:store(board)
fillBoard(tostring(i))
Moves_so_far = Moves_so_far + 1
end
break
end
end
end
function UndoMove()
board=back:restore() or board
Moves_so_far = math.max(0,Moves_so_far - 1)
end
--this is a recursive function that calls itself
function fillBoard(c,r,x,y)
--the first time it's called, from touched, it just passes the new color
--so we'll make a note of the current color of bottom left
--square, and our starting x,y, which is 1,1
if r==nil then
r=board[1][1]
x=1 y=1
end
--check if the square we are in, needs to be changed
--if it is the first one, 1,1, the answer is always yes
if board[x][y]==r then
board[x][y]=c
--now check the neighbours around, excl diagonals
if x>1 then fillBoard(c,r,x-1,y) end
if x<Size then fillBoard(c,r,x+1,y) end
if y>1 then fillBoard(c,r,x,y-1) end
if y<Size then fillBoard(c,r,x,y+1) end
end
end
--# Backup
Backup = class()
function Backup:init()
self.backups={}
end
--backs up a 1D or 2D table to a delimited string
--one parameter is the table to be backed up, stores the string in an array backup
--this provides unlimited backups
function Backup:store(t)
if t==nil then return end --do nothing if given nothing
if self.backups==nil then self.backups={} end --if first time, create array
local s={} --we use a 1D array for the backup because it's faster than strings, we convert later
if type(t[1])~="table" then --1D table, we can use built in concat command
self.backups[#self.backups+1]="1,"..table.concat(t,",")
else --2D table, loop through
for i=1,#t do
for j=1,#t[i] do
s[#s+1]=t[i][j]
end
end
self.backups[#self.backups+1]=#t[1]..","..table.concat(s,",")
end
end
--restore last backup from a string to a table (1 or 2D)
--parameter is the number of cols - if missing,table is assumed to be 1D
--if cols is provided, it is used to calculate rows (since # total items backed up = cols x rows)
--returns the table, deletes restored backup
function Backup:restore()
if #self.backups==0 then return end --no backups
local t={} --the table we will return
local b=self:split(self.backups[#self.backups]) --split backup string into a 1D table
local cols=tonumber(b[1])
if cols==1 then
t=b --if a 1D table is needed, we're done
table.remove(t,1)
else
local ii,jj=cols,(#b-1)/cols --otherwise we need a 2D table, calc number of rows (jj)
local n=1
for i=1,ii do --loop through cols
t[i]={} --create array for this row
for j=1,jj do --loop through row
n = n + 1 --counter to help us find the place in b
t[i][j]=b[n]
end
end
end
table.remove(self.backups,#self.backups) --remove latest backup
return t
end
function Backup:clear()
self.backups=nil
end
--splits a delimited string into a table, delimiter defaults to comma
function Backup:split(str, delim)
if delim==nil then delim="," end
local result = {}
local pat = "(.-)" .. delim .. "()"
local lastPos = 1
for part, pos in string.gfind(str, pat) do
table.insert(result, part)
lastPos = pos
end
-- Handle the last field
table.insert(result, string.sub(str, lastPos))
return result
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment