Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines
(2 edits)

I am butting up hard against both the token and compressed code limits right now, 99.85% and 99.79%, respectively. About 1300 tokens golfed. I don't know why I assumed this game would be simpler than my previous PiCoSteveMo entry! (I'm at like 86% for sprite/map space, despite having fleshed out the map far beyond what I'd predicted would be feasible. Apparently this game's level design compresses really well.)

Pico-8 token golfing is a deep rabbit hole. 

A lot of token golf optimizations are just good changes, taking unnecessary complexity or verbosity and turning into it something simple. For example, in the initializer, I looped over the map, with a series of if-then statements to compare each map tile to a dozen different magic numbers that indicated a certain entity is supposed to spawn there, like this:

if t==1 then e = balloon(p)
elseif t==4 then e = boulder(p)
elseif t==20 then e = phone(p)

I have a function that initializes a dictionary using a string, so I created a tile to object mapping:

objs = dict"1,balloon,4,boulder,20,phone,37,target,34,stormdrain,39,checkpoint,2,button,48,gate,52,theythem,78,button"

So most of that series of if-thens gets replaced with this code that looks up the that object's initialization function in the global namespace dictionary:

if objs[t] then e = _ENV[objs[t]](p)

And I think that's a net good change, sacrificing simplicity for size and flexibility -- though the "actually good code" version would initialize the dictionary in a more readable way.  But if you want to really get the most out of your tokens, you get up to some wild shenanigans. For example, I've got this code as part of the routine that draws the title screen, several calls to the sprite blitting routine specifying what to draw where:

cspr(256,78,0,3,2)
cspr(259,110,0,2,2)
cspr(261,4,20,8,2)
cspr(288,68,20,7,2)

By Pico-8's count, that's 28 tokens. I added a wrapper to cspr that allows you to pass parameters in a string:

csprs"256,78,0,3,2"
csprs"259,110,0,2,2"
csprs"261,4,20,8,2"
csprs"288,68,20,7,2"

Which is apparently valid function call syntax?? And a mere 8 tokens.

One of the most recent things I did was to take all the state of the dialog tree system that used to be encapsulated inside its own object, and just dump it into the global namespace. This saved like 50 tokens throughout the codebase because nar_msg is one fewer token than nar.msg.

This is by far the ugliest code base of any of my Pico-8 games. Historically I've avoided using a minifier because I like to ship a relatively readable game, but even with manual optimization, readability is totally out the window at this point. For my next game I'm probably going to figure out how to use a minifier workflow so things stay readable on my end at least.

(+1)

I haven't done enough PICO-8 to have enough of a sense of how I really feel about tokens. It's part of the whole constraint philosophy, and I do appreciate that you can name things a bit better because you're not totally constrained only on characters (although I do find myself using a lot of short and semi-cryptic variable names). But some of the things that take up tokens just go against a lot of how I personally feel about good code.

Like you pointed out, getting rid of objects and dumping things in the global namespace can save tokens, and sometimes a lot of tokens depending on how much you repeat. But ugggghhhhhh.

Yeah I'm of two minds about it as well. I think the token constraint is a crucial aspect of Pico-8's design. It discourages feature creep and encourages a shipping mentality. But when you're at the token limit and you need to fix a bug or make a tweak, there's nothing stopping you from golfing just a few more tokens, until your code is an unmaintainable mess and and you realize that if you do it wrong, Pico-8 is actually harder and less fun to use than an ordinary game engine.