Annotated source code for the latest version


This is the annotated source code of the latest version of Billabong, with comments explaining how the code works and the tricks I used to squeeze it into 484 bytes.

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

-- Note: to save characters, variables are not declared local.
-- Therefore they all default to global variables. As a space
-- saving measure, the same global variable is sometimes used
-- to store different temporary data in different parts of 
-- the code.


-- 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.  The sprite sheet starts at
-- address 0x0000. If you don't pass a value to poke, it
-- defaults to zero. This saves us two characters of code!
k(24405)

-- Clear the entire sprite sheet with the background colour.
-- The player colours are red and blue.  The maze is drawn 
-- with dark purple walls on a light purple/grey background.
-- This gives the game a consistent colour theme.
cls(13)

-- 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 
-- glyphs 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 isometric 3D. A happy accident!
for y = 1, 122, 5 do
 for x = 1, 122, 6 do
  ?rnd{"◜","◝"},x,y,2
 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 to prevent it running beyond
-- the bounds of the maze.
rect(0,0,127,121,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 will be 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.
-- Player 1 has the first turn.
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, and
-- two characters shorter than a modulo calculation p=p%2+1.
--
if (btnp(5) and py<120 and g(px,py)==13) a(f,{p,px,py}) p=3-p


-- The flood fill algorithm.

-- "n" == "next": the pixels to be checked *next* frame,
-- generated by running the flood fill for *this* frame
n={}

while #f > 0 do
 -- treat `f` as a stack: pop the top element, and 
 -- unpack its elements into local variables.
 -- q = the player that launched the flood that this
 -- pixel is part of
 -- x, y = the coordinates of the pixel
 q,x,y = unpack(deli(f))
 
 -- We only flood fill the pixel if it is part of the 
 -- background.
 if g(x,y) == 13 then
  -- Set the pixel to the colour of the player who launched 
  -- the flood.  Again, we use a concise expression to
  -- calculate the colour, rather than a conditional, to
  -- save characters. When q is 1, 4+4*q is 8 (red); when
  -- q is 2, 4+4*q is 12 (light blue).
  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 hold 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 black.  Only the score line at the
-- bottom of the screen has a black background, but clearing
-- the entire screen uses the fewest characters.
cls()

-- Draw the maze and flood-fill by blitting the area of the
-- sprite sheet that contains the maze onto the screen, 
-- leaving room for the score line at the bottom.
-- Pico8 rounds the height down from 15.3 to 15.25, letting
-- us save one character of code.
spr(0,0,0,16,15.3)

-- Draw the mouse pointer in white (colour 7). 
-- 
-- 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)

-- Print the scores for player 1 in their colour (red).
-- The x variable is now used to hold the x coordinate
-- after the printed text, which we use to lay out the score
-- line without writing code to calculate pixel widths of 
-- the numbers.
x = ?s[1],1,123,8

-- 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.
x = ?chr(21+p),x+1,123,7

-- Print the scores for player 2 in their colour (light blue)
?s[2],x+1,123,12

-- Show the frame we just drew
flip()

-- 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 start a new game by restarting the cartridge
-- from the pause menu.

Get Billabong

Leave a comment

Log in with itch.io to leave a comment.