Annotated source code for the latest version
Billabong » Devlog
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
Billabong
A tiny shared-screen multiplayer real-time strategy game for two players, written for #TweetTweetJam 9
Status | Released |
Author | dredds |
Genre | Strategy, Action |
Tags | Multiplayer, PICO-8, Tactical, tweettweetjam |
More posts
- Announcement: The Color of Honey63 days ago
- Update: improved visualsMay 11, 2024
- Commented Source CodeMay 11, 2024
- New version fixes annoying bugMay 10, 2024
Leave a comment
Log in with itch.io to leave a comment.