Log In  


Cart #flowtris-0 | 2024-09-14 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
5

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.

5


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.



[Please log in to post a comment]