Flowtris: A tiny and gooey action puzzler
My 2024 entry for Pico-1k!
A simple game in just 1018 compressed bytes of Pico-8 instructions. It combines the expanding goo mechanics from the classic Pipe Dream game with the block-dropping puzzle action of Tetris. Try to make a path for the goo to flow across the bottom before you spill!
Controls
- ⬅➡ to move the pending tile
- ⬇ to start the drop
- ⬆ for a quick-drop
- z/❎: Rotate
- x/🅾️: Restart after game-over.
Difficulty modes
- Easy: The edges of the tile also block goo expansion
- Hard: They do not. Good luck!
Source code
If pasting this into Pico-8 to check it out, make sure you enter puny font mode (ctrl-p) first.
b=112p="%,%?%,%o%,%?%,%?%,%"g="z0%,%0お%0>%3%"h="%,%?%,%か%,%"q="`"..h.."o4?"..h.."l%2%<"..h.."i%8%9"..h.."f%>%6"..h.."c%𝘥7210%,%0➡️/%0@6%"..g..g..g..g.."𝘥7210%,%0a2@/%01/%6%>%6"..p.."9%8%9"..p.."<%2%<"..p.."?4?"..p.."o"..p.."0"z=0for e=1,277do j=ord(q,e)-35for e=0,j\3do sset(z%b,z\b,j%3)z+=1end end e={{1,0,112},{2,112,108}}d={ord("`ppppppp\0",1,9)}d[0]=0y={}for e=0,128do y[e]={}end i={{8,124},{8,124}}f=""l="lost"u=true k=0a,r=0,0c=0::e::cls()?"easy",58,50 ?"hard",58,58 ?">",52,50+c*8 if btnp(2)or btnp(3)then c=c~1end if btnp(5)then flip()goto f end flip()goto e::f::cls()pal({1-c,7,11,139,3,139,11},1)::n::for e=1,999do n,o=rnd(128),rnd(128)m=pget(n,o)if m>2then pset(n,o,(m-2)%5+3)end end w,a,r=unpack(e[#e])s=a\16if u then e[#e][3]+=4if d[s+1]<=r+4then if d[s+1]<16then f=l end d[s+1]-=16u=false k+=1add(e,{rnd(10)\1+3,48,0})end end if#f>0then if btnp(4)then run()end else if btnp(0)and d[s]>=r then e[#e][2]=max(a-16)end if btnp(1)and d[s+2]>=r then e[#e][2]=min(a+16,112)end if btnp(2)then e[#e][3]=d[s+1]-4u=true end if btnp(3)then u=true end if btnp(5)then e[#e][1]=ord("$%&('*+,)./0-",w)-35end end if k>1and#i>0then n,o=unpack(deli(i,1))if(d[n\16+1]+16>o or c==1and pget(n,o)==1)and#f==0then f=l?"⁷szc2.g1.e-.ccc" end if n==120and o==120and#f==0then f="won!"l=f?"⁷sacecegcegc2..c3cc" end if pget(n,o)<1+c then pset(n,o,3)for e in all{{0,1,1},{1,0,#i},{-1,0,#i},{0,-1,#i}}do if y[n+e[1]][o+e[2]]==nil then add(i,{n+e[1],o+e[2]},e[3])y[n+e[1]][o+e[2]]=1end end end end if#i==0and#f==0then f=l end rectfill(a,r,a+16,r+16,0)if#f==0then for e in all(e)do w,v,x=ord("#%$#$$%$$'$$)$$+$$-$$+$%-%$/$$/$%/%%/%$",(e[1]-1)*3+1,3)spr(w-35,e[2],e[3],2,2,v==36,x==36)end end rect(0,0,127,127,2)if#f>0then?"²0ᶜ2you "..f.." press 🅾️ to restart",8,56 end flip()goto n |
Comments and credits
Thanks go out to Liquidream for hosting the Pico-1k jam, thisismypassword for Shrinko8, and JadeLombax for his graphics tutorial and tools! Without these people, none of this would have been possible.
Fun tricks
I used JadeLombax's run-length-encoded sprites, but my sprites were larger and simpler than some of the examples that tool is often used on, and the output sprite definition ended up using 281 characters, and had some repeated sub-strings:
z="`%,%?%,%か%,%o4?%,%?%,%か%,%l%2%<%,%?%,%か%,%i%8%9%,%?%,%か%,%f%>%6%,%?%,%か%,%c%D7210%,%0➡️/%0@6%z0%,%0お%0>%3%z0%,%0お%0>%3%z0%,%0お%0>%3%z0%,%0お%0>%3%D7210%,%0a2@/%01/%6%>%6%,%?%,%o%,%?%,%?%,%9%8%9%,%?%,%o%,%?%,%?%,%<%2%<%,%?%,%o%,%?%,%?%,%?4?%,%?%,%o%,%?%,%?%,%o%,%?%,%o%,%?%,%?%,%0" |
Pulling those out and then composing the sprite data from repeated lets me get that down to 220 characters.
o="%,%?%,%o%,%?%,%?%,%"l="z0%,%0お%0>%3%"z="%,%?%,%か%,%"i="`"..z.."o4?"..z.."l%2%<"..z.."i%8%9"..z.."f%>%6"..z.."c%𝘥7210%,%0➡️/%0@6%"..l..l..l..l.."𝘥7210%,%0a2@/%01/%6%>%6"..o.."9%8%9"..o.."<%2%<"..o.."?4?"..o.."o"..o.."0" |
The other big use of ord
was for compressing lookup tables. Each one of the tiles can be rotated and then can be rendered. Before shrinking the code down, this was done with two lookup-tables:
-- To rotate a tile, lookup in this list. States 1,2, and 3 don't rotate, 4 and 5 form a two-state loop, -- and 7,8,9,6 and 11,12,13,10 form two four-state loops rotate={1,2,3,5,4,7,8,9,6,11,12,13,10} -- Render maps from a state to its sprite number, and flipx, flipy values render={ {0,1,0}, {0,0,0}, {2,0,0}, {4,0,0}, {6,0,0}, {8,0,0}, {10,0,0}, {8,0,1}, {10,1,0}, {12,0,0}, {12,0,1}, {12,1,1}, {12,1,0}} -- Rotating code if bntp(5) tile=rotate[tile] -- Rendering code sp,fx,fy = unpack(render[tile]) spr(sp,x,y,2,2,fx,fy) |
After Shrinko8, this uses a total of 196 characters.
The ord
command can get multiple values from a string, and gets rid of all the extra {},
characters by packing the data into a string, which shinks all the above down to just:
-- Rotate if (btnp(5)) tile=ord("$%&('*+,)./0-",tile)-35 -- Render sp,fx,fy=ord("#%$#$$%$$'$$)$$+$$-$$+$%-%$/$$/$%/%%/%$",(s[1]-1)*3+1,3) spr(sp-35,s[2],s[3],2,2,fx==36,fy==36) |
Which shrinks down to 141 characters, saving 55! The token savings is even more significant, but that didn't matter as much for Pico1k.
This was really fun! A good challenge. I'm impressed by the goo spreading system being fit into 1k! I haven't won in hard mode yet, but I plan to conquer it soon.
Intuitive, visually interesting, challenging, and most importantly: fun! Great puzzle game and gold star'd
Easy mode took a few tries, but hard mode was super hard (so I guess the name fits). Very fun, and impressive given the constraints.
Very fun, impressive that fits in 1K. Tried the game without looking at the instructions and won easy and hard without knowing I could rotate the pieces...
This would be a good game in any size, and amazing in such a tiny space.
A challenge: win after filling as many squares as you can. Ideally, make it fully enclosed while minimising the number of unused pieces. I was pretty happy with this one, with just three pieces I couldn't do anything useful with.
@Cowirrie
1st image is your challenge. While fun, it's too RNG based for the last few tiles, so I'm playing "levels" :
before running the game, i do srand(1) to play level 1.
I lose the challenge, exit the game, retype srand(1), run and replay, but this time I know in advance the sequence of pieces. A few tries later, and I have won level one.
Time for srand(2). I could mod the game (but not stay under 1K) to automate this. Maybe also add a display of the pieces to come. @sizescape, do you mind ?
2nd image is "straightforward" challenge.
3rd image is snake challenge.
@RealShadowCaster I don't mind at all. I have been thinking of reworking this a bit and adding some more features outside of the 1k limit, but it'll be a while before I have time.
Not at my computer right now to verify, but if my notes are correct, the source code here is what I had before sending it though Shrinko, and is going to be easier to work with:
[Please log in to post a comment]