Commented Source Code


Note: thanks to writing this devlog article, I noticed lots of opportunity to save characters and have published a new version that has better visuals and is even smaller. Now only 484 characters. I have posted annotated source code for that version here: https://dredds.itch.io/billabong/devlog/729963/annotated-source-code-for-the-latest-version

Here is the annotated source code. I have added white space and comments to make the source more readable, but otherwise the code is exactly the same as the TweetTweetJam submission.

-- single-character names for frequently used functions
a=add
k=poke
t=stat
g=sget


-- Put Pico8 into "devkit" mode, which allows mouse control. 
-- 
-- The address is in decimal, not hex, because a hex value must
-- be prefixed with "0x", and so decimal format is one 
-- character shorter.
--
k(24365,3) 

---------------------------------------------------------------
-- Draw the maze into the sprite sheet before the game starts.
-- 
-- The game runs the flood fill algorithm in the sprite sheet,
-- not the screen buffer, so that it can draw moving sprites
-- over the maze without interfering with the flood fill.
--
-- I originally intended the two players to each have a game
-- controller, and each control their own cursor.  I couldn't
-- fit that in to the 500 character limit, so used mouse/touch
-- control and a single cursor. The players have to take turns.
--
-- Make the Pico8 graphics functions draw to the sprite
-- sheet, not screen memory
k(24405)

-- When Pico8 starts up, the sprite sheet has a default 
-- graphic in sprite 0. This interferes with the flood fill. 
-- Clearing the entire sprite sheet is the shortest way to 
-- remove it.  As a result, the background of the sheet is
-- colour 0, which is transparent.
cls()

-- Iterate over the rows and columns of the sprite sheet, 
-- printing random Truchet tiles (characters 254 and 255 
-- in the Pico8 character set.  
--
-- It takes fewer characters to iterate in pixel coordinates, 
-- stepping multiple pixels at a time, than to iterate in 
-- character coordinates and then multiply by cell width/height
-- to calculate pixel coordinates.
--
-- The y step is 5 and the x step is 6 to ensure that the 
-- tiles join up with no gaps between them.  As a result, the 
-- maze is a bit squashed along the y axis.  But it ends up 
-- looking like an isometric 3D rendering. A happy accident!
--
for y=1, 120, 5 do
 for x=1, 121, 6 do
   ?rnd{"◜","◝"}, x, y, 2   -- `?` is a shortcut for `print`
 end
end

-- Draw a border around the maze, so that the we only need to 
-- check pixel values, and do not need to check x and y 
-- coordinates in the flood fill.
rect(0,0,127,120,6)

-- Switch Pico8's graphics functions back to target the screen, 
-- not the sprite sheet.
k(24405,96)

-- End of creating the maze
---------------------------------------------------------------


-- Set up the game state, again using single-character variable
-- names to save on characters.

-- "f" = "floodfill". A list of pixels to be checked next frame
-- by the flood fill algorithm.  Each element is a list of:
--   {<player-number>, <pixel-x>, <pixel-y>}.
f={}

-- "s" = "scores". The scores for players 1 and 2
s={0,0}

-- "p" = "player". The current player, either 1 or 2
p=1

---------------------------------------------------------------
-- The main game loop.

-- Start the game loop.  The end of the game loop `goto`s back 
-- here.
::_:: 

-- Read the mouse coordinates
px=t(32)
py=t(33)

-- If the mouse is clicked on a background pixel within 
-- the play area, add the point to the flood fill for the
-- current player and switch to the next player's turn.
-- 
-- p=3-p calculates the next player: 
--   when p is 1, 3-p is 2; when p is 2, 3-p is 1.
-- This is a lot shorter than using a conditional.
--
if (btnp(5) and py < 120 and g(px,py) == 0) a(f,{p,px,py}) p=3-p


-- The flood fill algorithm.

-- "n" == "next": the pixels to be checked *next* frame,
-- calculated by running the flood fill for *this* frame
n={}
while #f > 0 do
 -- treat `f` as a stack: pop the top element, then 
 -- unpack the elements into local variables.  The
 -- elements of the `f` list are themselves lists so
 -- they can be unpacked like this.
 q, x, y = unpack(deli(f))
 
 -- Clamp the x and y to the play area.  We do this
 -- here instead of before adding pixels to the list to 
 -- save space.  We only need to check once, not four times.
 --
 -- We also save space by passing two arguments to the `mid`
 -- function.  Missing arguments default to zero.
 --
 -- (In retrospect, I think the clamping is unnecessary.)
 --
 x = mid(x,127)
 y = mid(y,120)

 -- The clamped coordinates might be on the border. That's
 -- not a problem, because we only do the next step of the
 -- flood fill if the pixel at x,y is the background colour
 --
 if g(x,y) == 0 then
  -- Set the pixel to the colour of the player who launched this flood
  sset(x,y,4+4*q)
  
  -- Increase that player's score
  s[q]+=1
  
  -- Add pixels above/below/left/right of this pixel to be
  -- checked next frame
  a(n,{q,x,y+1})
  a(n,{q,x,y-1})
  a(n,{q,x+1,y})
  a(n,{q,x-1,y})
 end
end

-- Switch the `f` variable to the pixels we calculated during
-- this frame, so that they are checked in the next frame.
f=n


-- Draw the current frame

-- Clear the screen to a light purplish-grey background colour.
-- I intended to run a groovy background effect behind the 
-- transparent maze, but didn't have enough space.
cls(13) 

-- Draw the maze by blitting the entire sprite sheet onto 
-- the screen.
spr(0,0,0,16,16)

-- Print the scores for player 1 and player 2 in their colour
?s[1],1,122,8
?s[2],80,122,12

-- Draw the mouse pointer. Pico8 renders a circle of radius 1
-- as a small cross with a one-pixel space in the middle, which
-- works as the mouse pointer for this game, and takes fewer
-- characters than printing an appropriate glyph from the
-- Pico8 character set.
circ(px,py,1,7)

-- Indicate who's turn it is by printing a glyph that points
-- at the score of the current player.  Character 22 is a
-- left-pointing triangle, and character 23 a right-pointing
-- triangle.
?chr(21+p),62,122

flip() -- show the current frame

-- jump to the start of the game loop to run the next frame
goto _ 

-- End of the game loop
---------------------------------------------------------------


-- Note: no room for checking for the end game condition or
-- deciding who is the winner.  The players have to do
-- that themselves when they have filled every space in the
-- maze, and then restart the game by restarting the app
-- from the pause menu.

Get Billabong

Leave a comment

Log in with itch.io to leave a comment.