Prime Time Commented Source Code


Commented, uncompressed code that describes the game logic and the techniques I used to reduce the size of the code. I used the shrinko8 tool to do the shrinking.

--ttj10 math puzzle
--by @dredds

-- define single-character 
-- names for functions  that 
-- are used multiple times
g=mget
s=mset
m=mid
r=rnd

-- the number 15 is used in so
-- many expressions that it is
-- worth defining a single
-- character variable for it 
f=15

-- the game state...

l={} -- positions of numbers
     -- keyed by value
     
o=1  -- current objective.
     -- this will be incremented
     -- to 2 at the start of
     -- the game loop
     
c=0  -- count of factors of the
     -- current objective still
     -- to be collected.
     -- defined as zero so that
     -- the first level, with
     -- objective 2, is
     -- initialised in the
     -- iteration of the game 
     -- loop
     
a=1  -- is the player alive?
     -- use 1/0 instead of
     -- true/false so a can
     -- be used in arithmetic
     -- expressions without
     -- conditional statements
     
x=4  -- player x coordinate
y=4  -- player y coordinate


-- the game loop repeats until
-- the player dies or reaches
-- the objective of 100
repeat
 -- if the number of factors
 -- to collect is zero, then
 -- initialise the level. the
 -- expression c<1 is one
 -- character less than c==0,
 -- and has the same effect
 -- because the game sets c
 -- to a positive integer and
 -- decrements it by one every
 -- time the player collects a
 -- factor of the objective
 if c<1 then
  -- increase the objective.
  -- o is initialised to 1 so
  -- the first level has
  -- objective 2 (the first
  -- prime number)
  o+=1
  
  -- clear the map. the game
  -- uses the map as a 2d array
  -- to track what is in each 
  -- grid cell, so the map
  -- must be cleared before
  -- creating each level to
  -- remove data about the
  -- previous level
  reload()
  -- 1 in a map cell indicates
  -- that it is the position of
  -- the player 
  s(x,y,1)
  
  -- for all numbers from 2
  -- (the first prime) up to
  -- the objective...
  for n=2,o do
   -- spawn a number only if n
   -- is a prime. the string
   -- of glyphs is a bitset
   -- indicating which numbers
   -- below 100 are prime.
   -- the division and bit-
   -- twiddling extracts the
   -- bit corresponding to n
   if ord(
    "て(⌂き ⌂ (☉🐱⁸²²",
      n\8+1
    ) & (1<<(n%8)) > 0
   then
    -- generate random coords
    -- u and v for the number
    u=r(f)\1v=r(f)\1
    
    -- set the map cell u,v to
    -- the value of the number
    -- for collision detection
    s(u,v,n)
    
    -- add the coordinates to
    -- the table of numbers
    l[n]={x=u,y=v}
    
    -- increase the count by
    -- one if this is a prime
    -- factor of the objective.
    -- max defaults missing
    -- parameters to zero, so
    -- this is the same as
    -- max(0,1-o%n). if n is
    -- a factor of o, then o%n
    -- is 0, 1-o%n is 1, and so
    -- the statement increments
    -- c. if not, then 1-0%n is
    -- zero or negative, and 
    -- max clamps it to zero.
    c+=max(1-o%n)
   end
  end
 end
 
 -- draw current state
 
 -- clear the background to
 -- black or dark blue, 
 -- depending on the current
 -- level. this gives a clear
 -- indication when the player
 -- has a new objective 
 cls(o%2)
 
 -- draw the objective in orange
 -- cls resets the print state
 -- so the objective is drawn
 -- at the top-left
 ?o,9
 
 -- draw the player character.
 -- the print color persists
 -- so the player is also drawn
 -- in orange
 ?"웃",x*8,y*8
 
 -- draw the numbers. iterating
 -- with the hidden next 
 -- function is one character
 -- shorter than iterating with
 -- the pairs function.
 -- numbers are drawn in green.
 for n,e in next,l do
  ?n,e.x*8,e.y*8,3
 end
 flip()
 
 -- only move the numbers if
 -- there is any user input.
 -- interpreting the input
 -- uses some amazing code
 -- by @pancelor that turns
 -- the buttons bitset into
 -- an angle.
 b=btnp()*12\5/4
 if b>0then
  -- clear the old position of 
  -- the player in the map. mset
  -- defaults the missing value
  -- parameter to zero, saving
  -- two characters.
  s(x,y)
  -- calculate the new position
  -- of the player. the 
  -- positions are masked with
  -- 0x0f, making them wrap
  -- round the play area.
  -- masking with f& at the 
  -- start of the expression 
  -- instead of &f at the end
  -- means there does not need
  -- to be a space between the
  -- statement and the next,
  -- saving two characters.
  x=f&x+cos(b)
  y=f&y+sin(b)
  -- set the new position of
  -- the player in the map
  s(x,y,1)
  
  -- for each number, using the
  -- hidden next function again
  -- to save a character
  for n,e in next,l do
   -- if the player is now at
   -- the number's position,
   -- check if the player has
   -- collected a prime factor
   -- or is dead
   if g(e.x,e.y)==1 then
    -- use the same trick with
    -- max to calculate if the
    -- number is a factor of
    -- the objective (1) and
    -- therefore the player is
    -- still alive, or not (0)
    -- and therefore it's game
    -- over
    a=max(1-o%n)
    -- delete the number from
    -- the table if it is a
    -- prime factor, meaning
    -- the player is still alive.
    -- if the player is alive,
    -- a == 1 so n*a == n.
    -- if the player is not
    -- alive a == 0 so n*a == 0
    -- and this sets the unused
    -- entry l[0] to nil.
    -- 
    -- an undefined variable is
    -- used instead of the 
    -- keyword nil to save two
    -- characters. (the shrinko8
    -- tool replaces the name
    -- undef with a single 
    -- character).
    l[n*a]=undef
    
    -- reduce the count of
    -- prime factors to be
    -- collected if n is a
    -- prime factor
    c-=a
   else
    -- if no collision with
    -- the player, move the
    -- number randomly. again,
    -- the new coordinate is
    -- masked with 15 to wrap
    -- around the play area.
    -- masking takes only two 
    -- characters. clamping
    -- to 0..15 with the mid
    -- function would take more.
    -- again, putting f& at the
    -- start of the expression
    -- to eliminate the space 
    -- character after each
    -- statement.
    u=f&e.x+r(3)\1-1
    v=f&e.y+r(3)\1-1
    
    -- only move if the map at
    -- u,v contains zero.
    -- again, using <1 instead
    -- of ==0 to save space.
    if g(u,v)<1 then
     -- clear the map at the
     -- number's old position
     s(e.x,e.y)
     -- store the number in the
     -- map at its new position
     s(u,v,n)
     -- store the new position
     -- of the number
     e.x=u
     e.y=v
    end
   end
  end
 end
 
-- we keep doing that until
-- the player is not alive
-- (again using the <1 trick
-- to test for zero) or the
-- player has reached the
-- objective of 100, using
-- >99 instead of ==100 to
-- save two characters.
until a<1or o>99

-- if the player has one or
-- died, the game crashes.
-- no room for any game over
-- message. if the objective
-- was drawn as 100 during
-- the game loop, then the
-- player has won.  if not,
-- the player has lost.

When shrunk, the code becomes…

u=mget i=mset b=mid h=rnd f=15l={}d=1a=0p=1e=4n=4repeat if a<1do d+=1reload()i(e,n,1)for e=2,d do if(ord("て(⌂き ⌂ (☉🐱⁸²²",e\8+1)&1<<e%8>0)o=h(f)\1r=h(f)\1i(o,r,e)l[e]={x=o,y=r}a+=max(1-d%e)
end end cls(d%2)?d,9
?"웃",e*8,n*8
for n,e in next,l do?n,e.x*8,e.y*8,3
end flip()s=btnp()*12\5/4if s>0do i(e,n)e=f&e+cos(s)n=f&n+sin(s)i(e,n,1)for n,e in next,l do if(u(e.x,e.y)==1)p=max(1-d%n)l[n*p]=c a-=p else o=f&e.x+h(3)\1-1r=f&e.y+h(3)\1-1if(u(o,r)<1)i(e.x,e.y)i(o,r,n)e.x=o e.y=r
end end until p<1or d>99

Leave a comment

Log in with itch.io to leave a comment.