SHOW:
|
|
- or go back to the newest paste.
1 | ||
2 | --# Main | |
3 | -- Flood | |
4 | ||
5 | function setup() | |
6 | if backup then backup("Flood 101") end | |
7 | parameter.integer("Size",10,30,15) --size of board | |
8 | parameter.action("Start",Start) --starts a new game | |
9 | parameter.integer("Moves_so_far",0,100,0) | |
10 | parameter.action("Undo",UndoMove) | |
11 | --set up palette of six colors | |
12 | colrs={color(255,0,0,255), color(0,255,0,255), color(0,0,255,255), | |
13 | color(255,255,0,255),color(255,0,255,255),color(0,255,255,255)} | |
14 | ss=readProjectData("Size") --read in user's last size selection | |
15 | if ss~=nil then Size=ss end --set size, if a saved value exists | |
16 | Start() | |
17 | end | |
18 | ||
19 | function Start() | |
20 | --create random 2D table of colors | |
21 | board={}--create 1D table | |
22 | for i=1,Size do --loop through columns | |
23 | --this is how you create a 2D table | |
24 | board[i]={} --as we start each new column, create an array of rows | |
25 | for j=1,Size do --choose random color for each square | |
26 | board[i][j]=tostring(math.random(1,6)) | |
27 | end | |
28 | end | |
29 | state="RUNNING" --to tell draw the game has started | |
30 | Moves_so_far=0 | |
31 | output.clear() | |
32 | lastMove=board[1][1] | |
33 | back=Backup() | |
34 | saveProjectData("Size",Size) --save user size selection | |
35 | currSize=Size | |
36 | 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.") | |
37 | end | |
38 | ||
39 | function draw() | |
40 | background(200) | |
41 | --calculate the size of square we can fit on screen | |
42 | local w=(HEIGHT-100)/currSize | |
43 | pushStyle() | |
44 | --draw table of colors | |
45 | local finished=true --see use below | |
46 | for x=1,currSize do | |
47 | for y=1,currSize do | |
48 | fill(colrs[tonumber(board[x][y])]) --set colour based on value in table 1-6 | |
49 | rect(50+x*w-w,75+y*w-w,w,w) --draw square | |
50 | --if any of the squares are a different color, we haven't finished | |
51 | if board[x][y]~=board[1][1] then finished=false end | |
52 | end | |
53 | end | |
54 | --draw buttons for user to press, store button locations for touch detection | |
55 | button={} | |
56 | for i=1,6 do | |
57 | fill(colrs[i]) | |
58 | --position button at bottom of screen | |
59 | local x,y,w,h=100*i,10,75,50 | |
60 | rect(x,y,w,h) | |
61 | --save details of button position so we can trap touches | |
62 | button[i]={x=x,y=y,w=w,h=h} | |
63 | end | |
64 | popStyle() | |
65 | --if we've just finished, print a message | |
66 | if state~="FINISHED" then --we weren't finished last move | |
67 | if finished then --...but we are now | |
68 | state="FINISHED" | |
69 | print("Finished in",Moves_so_far,"moves") | |
70 | end | |
71 | end | |
72 | end | |
73 | ||
74 | function touched(touch) | |
75 | --state has three values, BEGAN, MOVING and ENDED | |
76 | --we only want ENDED, ie when the finger lifts up | |
77 | if touch.state~=ENDED then return end | |
78 | --check touch postiion against each button | |
79 | --that's why we stored all that info about button position | |
80 | for i=1,6 do | |
81 | if touch.x>=button[i].x and touch.x<=button[i].x+button[i].w | |
82 | and touch.y>=button[i].y and touch.y<=button[i].y+button[i].h then | |
83 | --only react if the user has chosen a different color | |
84 | if tonumber(board[1][1])~=i then | |
85 | back:store(board) | |
86 | fillBoard(tostring(i)) | |
87 | Moves_so_far = Moves_so_far + 1 | |
88 | end | |
89 | break | |
90 | end | |
91 | end | |
92 | end | |
93 | ||
94 | function UndoMove() | |
95 | board=back:restore() or board | |
96 | Moves_so_far = math.max(0,Moves_so_far - 1) | |
97 | end | |
98 | ||
99 | --this is a recursive function that calls itself | |
100 | function fillBoard(c,r,x,y) | |
101 | --the first time it's called, from touched, it just passes the new color | |
102 | --so we'll make a note of the current color of bottom left | |
103 | --square, and our starting x,y, which is 1,1 | |
104 | if r==nil then | |
105 | r=board[1][1] | |
106 | x=1 y=1 | |
107 | end | |
108 | --check if the square we are in, needs to be changed | |
109 | --if it is the first one, 1,1, the answer is always yes | |
110 | if board[x][y]==r then | |
111 | board[x][y]=c | |
112 | --now check the neighbours around, excl diagonals | |
113 | if x>1 then fillBoard(c,r,x-1,y) end | |
114 | if x<Size then fillBoard(c,r,x+1,y) end | |
115 | if y>1 then fillBoard(c,r,x,y-1) end | |
116 | if y<Size then fillBoard(c,r,x,y+1) end | |
117 | end | |
118 | end | |
119 | ||
120 | --# Backup | |
121 | Backup = class() | |
122 | ||
123 | function Backup:init() | |
124 | self.backups={} | |
125 | end | |
126 | ||
127 | --backs up a 1D or 2D table to a delimited string | |
128 | --one parameter is the table to be backed up, stores the string in an array backup | |
129 | --this provides unlimited backups | |
130 | function Backup:store(t) | |
131 | if t==nil then return end --do nothing if given nothing | |
132 | if self.backups==nil then self.backups={} end --if first time, create array | |
133 | local s={} --we use a 1D array for the backup because it's faster than strings, we convert later | |
134 | if type(t[1])~="table" then --1D table, we can use built in concat command | |
135 | self.backups[#self.backups+1]="1,"..table.concat(t,",") | |
136 | else --2D table, loop through | |
137 | for i=1,#t do | |
138 | for j=1,#t[i] do | |
139 | s[#s+1]=t[i][j] | |
140 | end | |
141 | end | |
142 | self.backups[#self.backups+1]=#t[1]..","..table.concat(s,",") | |
143 | end | |
144 | end | |
145 | ||
146 | --restore last backup from a string to a table (1 or 2D) | |
147 | --parameter is the number of cols - if missing,table is assumed to be 1D | |
148 | --if cols is provided, it is used to calculate rows (since # total items backed up = cols x rows) | |
149 | --returns the table, deletes restored backup | |
150 | function Backup:restore() | |
151 | if #self.backups==0 then return end --no backups | |
152 | local t={} --the table we will return | |
153 | local b=self:split(self.backups[#self.backups]) --split backup string into a 1D table | |
154 | local cols=tonumber(b[1]) | |
155 | if cols==1 then | |
156 | t=b --if a 1D table is needed, we're done | |
157 | table.remove(t,1) | |
158 | else | |
159 | local ii,jj=cols,(#b-1)/cols --otherwise we need a 2D table, calc number of rows (jj) | |
160 | local n=1 | |
161 | for i=1,ii do --loop through cols | |
162 | t[i]={} --create array for this row | |
163 | for j=1,jj do --loop through row | |
164 | n = n + 1 --counter to help us find the place in b | |
165 | t[i][j]=b[n] | |
166 | end | |
167 | end | |
168 | end | |
169 | table.remove(self.backups,#self.backups) --remove latest backup | |
170 | return t | |
171 | end | |
172 | ||
173 | function Backup:clear() | |
174 | self.backups=nil | |
175 | end | |
176 | ||
177 | --splits a delimited string into a table, delimiter defaults to comma | |
178 | function Backup:split(str, delim) | |
179 | if delim==nil then delim="," end | |
180 | local result = {} | |
181 | local pat = "(.-)" .. delim .. "()" | |
182 | local lastPos = 1 | |
183 | for part, pos in string.gfind(str, pat) do | |
184 | table.insert(result, part) | |
185 | lastPos = pos | |
186 | end | |
187 | -- Handle the last field | |
188 | table.insert(result, string.sub(str, lastPos)) | |
189 | return result | |
190 | end |