KATA, memory martial art in 1024 bytes


Just released KATA, my entry for the Pico 1K Jam 2023, which the games whould be created within 1024 bytes, and now I'm going to share a bit about this experience.

The idea

For the first sight I thought the size limitation would be OK for the idea of creating a "Genius" inspired game as the logic at all is pretty simple. 

Actually, I did something like this on my first semester of programming classes, as an exercise to study Pascal programming language. On that season it had the 4 colours (green, blue, red and yellow) really like the original Genius. Also it had sounds for each color and on game over. Unfortunently I lost it after format my computer.

Anyway...

This time I wanted to bring a bit of martial arts to the game because, a couple of years ago, I practiced Capoeira, a Brazilian martial art, and on the classes it's common to have a sequence of moves to repeat. I learned this is an influence of oriental martial arts, like Karate, and the term for this kind of exercise is Kata. You can learn a bit more on Wikipedia.

Challenges

As a jam requirement, we can not use pre-created sprites, sounds, etc, what means that all the content of the game needs to be generated by code.

Therefore, if someone pastes the game code into Pico-8, the game should run perfectly.
However, it makes harder to generate sprites by code having a code size limitation.

So, after create a prototype using numbers and created sprites to see the game with the images.

There were 7 sprites, one for the idle player and the others for each moves:

  1. Idle
  2. Block
  3. Punch
  4. High kick
  5. Duck
  6. Jumping kick
  7. Eagle (like Karate-Kid)

With that I thought: "Okay, now I can figure out a way to make them by code."

Generating sprites

The first attempt was to use simple built-in functions to draw the figther, like RECTFILL, CIRCLE, and PSET.

Even still being worried with the number of code I managed to create one draw with this strategy.

However I felt that would be hard if I wanted resize the sprites and  I figured out that we can do it with SSPR function that expects a sprite index.

The second attempt was to handle the memory and create sprites as I had drawed them.

For that I had to understand how the memory is organized in Pico-8 and how manipulate it. 
It took a couple of hours to understand. No problem, I was curious to understand this topic of Pico-8!

After that I had to write a function because functions like RECTFILL doesn't exist to set memory.
Keep in mind we need to to set a memory slot for each two pixels of the draw.

So the idea is send a table as argument with all the pixels should be set in a sprite.

For example:

draw(1,{11,12,19,20,25,26,27,28,29,33,37,41,42,43,44,45,50,52,58,60},0,7)
  • The first argument is the sprite I am drawing: 1.
  • The second represents all the pixels I want to set.
  • The third is the foreground color: black
  • And, the last is the background color: white.

And the belt? That has another color!

For the belt I called draw again after the previous one, but without the background  argument. 

draw(1,{34,35,36},9)

Now I can referer the sprits using the SPR function and stretch them using the SSPR.

spr(1,75,73,1,1,true)
sspr(16,0,8,8,20,50,32,32)


The 1024 bytes restriction

Having all the sprites created and the game running like I wanted, it's time to see how many bytes I should cut off to fit in 1024 bytes.

I had 224 bytes to delete!

Wasn't easy to figure out ways to reduce such bytes. It last some hours.

 Following goes the actions I took.

1) Change logical expressions

1.1) Instead of use the OR option in the begin, like:

if (s==0 or s==3) and btnp(❎)

... leave the OR operator after the AND evaluation:

if btnp(❎) and (s==0 or s==3)

1.2) Instead od use IF with multiple lines, like:

if btnp(5) then
  b=5
end

... use the one line if:

if (btnp(5)) b=5

1.3) Instead of use ELSEIF, like:

if s==1 then
 ...
elsif s==2 then
 ...
end

... avoid ELSEIF when is possible:

if s==1 then
 ... 
end
if s==2 then 
  ...
end

2) Instead of use local variables:

function draw(s,l,f,b)
  local z=0
  ...
end

... use global variables. 

function draw(s,l,f,b)
 z=0
 ...
end

Ok, that's danger and we need to have attention as different locations can have local variables with same name.

3) Instead of have unnecessary text:

pc(20,"orange belt")

.. only remove them:

pc(20) 

4) Instead of use the zero key of the table:

ctr={[0]="⬅️","➡️","⬆️","⬇️","🅾️","❎"}
...
print(ctr[sp],32,90)

... prefer remove the zero and deal with it:

ctr={"⬅️","➡️","⬆️","⬇️","🅾️","❎"}
...
print(ctr[sp+1],32,90)

5) Instead use the _init and _draw functions:

function _init()
 init_game()
end
function _update()
 ...
end
function _draw()
 ...
end

Use only the _update function:

init_game()
function _update()
  ...
  -- draw code goes here
  ...
end

Honestly, I still don't know the side effects of this, but just worked!

6) Minify the source code.


Even after all the previous changes (and the more obvious ones like reduce naming, spaces, etc) I still had 97 bytes to reduce 😭

Confess I tought of reducing the features, like the "start game" message and the number of sprites. But I was pretty satisfied with the scope I didn't want to do that.

The solutions cames when I minify the source code using the fantastic Shrinko8 tool.


Yeah, 1024 bytes!!!

Probably, it's possible reduce even more the size, like improving the drawing algorithm, but already is good enough to submit the game.

Conclusion

This experience was fun and  for me!

  1. I learned about how to manipulate memory on Pico-8 (even feel I need to study more, in special the bit operations);
  2. I learned techniques to reduce the code size;  and
  3. I still have created a game to exercise my memory, having to reduce memory and dealing with memory 🤔!

Hope you have liked!

Let me know how was your experience with those challenges I mentioned?

Leave a comment

Log in with itch.io to leave a comment.