Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
pico-8 cartridge // http://www.pico-8.com
version 29
__lua__
--hand cram
--by @andy_makes
--playable at https://andymakes.itch.io/hand-cram
--**********************************
--original, 557 char code
--**********************************
--[[
t=0g=0r=rnd
w=128b=abs
o=circ::_::if g>0then
pal({5,2},1)print("score:"..t,9,9,7)
if(btn(5))t,g=0,0
else
if(t<1)a={9,9,88,0,40,0,0,0,0,0,40,0,88}
i=btn()pal({7,8},1)t+=1
for k=0,200do
o(r(w),r(w),5,0)end
for k=0,10do
s=2^k
l=s*2
a[k+3]+=band(i,l)/l-band(i,s)/s
end
for k=0,1do
x=a[3+k*8]y=a[5+k*8]s=a[k+1]o(x,y,s,1+k)o(x,y,s+2,1+k)
for c=0,w do
srand(c+t)if r(8)<1then
p=1+flr(r(2))z=r(w)o(c,z,4,p)
if(sqrt((x-c)^2+(y-z)^2)<s)a[k+1]+=b(1+k-p)/5-.01end
end
if(k==1 and sqrt((x-a[3])^2+(y-a[5])^2)<s+a[1])g=1
if(b(64-x)>66or 66<b(64-y))g=1end
end
flip()goto _
--]]
--**********************************
--expanded, commented code
--**********************************
--this is the exact same code as above, but with whitespace and line breaks added
--and a ton of comments to try to explain what it's doing
--variables that change and track game-state
t=0 --elapsed time
g=0 --game-over. if this is anything other than 0, the game has ended
--constants
w=128 --screen width. this value comes up enough that it saves chars to define it
--functions i use enough that it saves characters to to give them single-letter names
r=rnd
b=abs
o=circ
--this is the identifer that goto() will use.
--this marks the start of the game loop
::_::
--game over stuff
--g gets set to 1 when the game ends
if g>0 then
--using pal to change the colors of what has been drawn to "fade" the shapes and highlight the score text
pal({5,2},1)
--drawing the score. i found out later that i could have saved chars by using "?" instead of print
print("score:"..t,9,9,7)
--if x is pressed, i reset the game by setting g and time back to 0
--g is an int instead of a bool to save a few characters
if(btn(5)) t,g=0,0
--whenever possible, i shoot for single line if statements because they do not require "then" and "end"
--if g is 0, we play the game
--this is the main game loop
else
--if t is 0, that means we're on the first frame and i need to "initialize" the game
--this array stores all of the game values for the two circles
--slots 1 and 2 are used for the sizes (both start at 9)
--most of the rest of it is unused because my input loop creates garbage data
--but four of the remaining slots represent the x and y of each circle (set to 40 or 88 to give them starting positions on screen)
if(t<1) a={9,9,88,0,40,0,0,0,0,0,40,0,88}
--btw, i use "<1" instead of "==0" because it saves a char
--getting the input as a bitfield
--you can see the docs for this here: https://pico-8.fandom.com/wiki/btn
--more on this when we get to input
i=btn()
--setting the palette used during the game
--this does not need to happen every frame
--but putting it in the above if statement would require making it multi-line, which require "then" and "end"
pal({7,8},1)
--advance the time
--this acts as the score, but is also used to generate the positions of the dots that move across the screen
t+=1
--clearing the screen
--i did not use cls() because i wanted some visual noise
--this loop draws 200 black circles in random positions
--this covers most of the screen, but not all of it, creating trails
for k=0,200 do
o(r(w),r(w),5,0)
end
--munro hoberman's work introduced me to this technique
--he uses is a lot and to great effect in his tweetcarts
--exmaple: https://twitter.com/munrohoberman/status/1345134334232113157
--input
--this is probably the most confusing part of the code
--i highly recomend reading this post by eli piilonen (@2darray) to get a better understanding of how to read the btn bitfield
--https://pastebin.com/MVhr16td
--this loop runs through 11 of the 13 bits in the input value
--the last two don't matter to me so i don't bother
--there are other irrelevant values in the middle, but i need to get through them to get the ones the i care about
for k=0,10 do
s=2^k --getting a power of two to do a binary and comparison to isolate a single bit for each button that pico-8 tracks
l=s*2 --getting the value of the next bit
a[k+3]+=band(i,l)/l-band(i,s)/s --comparing these values and modifying the number stored in the array
--most of the slots in the array are garbage because we don't care about most pairs of inputs
--but a few are important like right-arrow minus left-arrow or down-arrow minus up-arrow
--the slots we care about wind up representing the x and y positions of the player circles
end
--this loop checks both player circles
--k==0 is the first player circle, k==1 is the second
for k=0,1 do
--grab the array values we care about for this circle
--and store them in names that are shorter and more readable
x=a[3+k*8]
y=a[5+k*8]
s=a[k+1] --size of the circle
--draw the circle twice
--the second one is two pixels bigger than the actual size
--i just thought this looked nice and helped differentiate the player circles from the obstacles
--i redefined circ() as o() at the top
o(x,y,s,1+k)
o(x,y,s+2,1+k)
--this loop goes through each pixel on the screen left to right
--it will create obstacles and move them across the screen
--the seed for each cycle is set by the x position plus the time
--this creates the effect of the "random" obstacles moving cleanly across the screen without needing to make objects
--shoutout to alex mckendry (fuzzydunlop) on the wonderville discord for introducing me to this
for c=0,w do --c is the x position
--reseed random using the x position plus the time
srand(c+t)
--there is a 1/8 chance of having an obstacle in this column
--up at the top i redefined rnd() as r()
if r(8)<1 then
--p represents the color. this can be 1 or 2
p=1+flr(r(2))
--z is the y position ("y" is already used for the player circle position)
z=r(w)
--draw the circle
o(c,z,4,p)
--hit detection
--getting this to work as single-line if statement was very handy, but it is a dense line!
if(sqrt((x-c)^2+(y-z)^2)<s) a[k+1] += b(1+k-p)/5-.01
--the first part is a distance function, getting the distance using the pythagorean theorem
-- sqrt((x-c)^2+(y-z)^2) < s
--the second part adjusts the circle's size
-- i have to use "a[k+1]" instead of "s" because i need to modify the array, not my copy of the value
--so i'm adding to a[k+1] but "b(1+k-p)/5-.01" is also confusing
--i previously redifed abs as b, so it's taking the absolute value of 1+k (the circle's color) minus p (the obstacle's color) and then dividing it by 5
--so if it matches it will add 0 divided by 5 (0), if it doesn't match it will add 1/5 (0.2)
--but then i subtract 0.01 to give a slight reduction in size if the colors match
--so what it actually comes out to is adding 0.2-.01 (0.19) if they don't match and subtracting 0.01 if they do
end
end
--this whole loop runs twice, once for each player circle
--this means all obstacles are drawn twice, which is unnecessary and a little wasteful, but it saves characters
--finally we check if the game has ended
--there are two ways this can happen
--the first is the two player circles touching
--i only check this in the loop for the second circle (k==1) (note: just realized i could have saved a char by using k>0)
--i do the same distance check as above using my x,y values for this player circle, and the array locations of those values for the other player circle
--if their distance is less than their combined radius, it's game over, menaing i set g to 1
if(k==1 and sqrt((x-a[3])^2+(y-a[5])^2)<s+a[1]) g=1
--the other way you can lose is by going off the screen
--i check this by taking the absolute value of the x and y position to the center of the screen
if(b(64-x)>66or 66<b(64-y)) g=1
--that is the end of the code that runs for each player circle
end
--and that's the end of the game loop as well!
end
--all that's left to do is draw everything to the screen using flip()
flip()
--and use goto to hop back to ::_::
goto _
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment