Log In  
Follow
gonengazit
[ :: Read More :: ]

Over the past few weeks I've been working on Celia, a TAS tool for PICO-8 based on picolove, and it's finally ready for its initial release

Celia comes with 2 tas tools, one that should work for any PICO-8 cart (with some caveats, in the repository readme), and one that's more specialized, built for Celeste and mods. The tas tool code is layed out in a way where you can extend it to create tas tools for specific games, enhancing the normal functionality, so it also doubles as a tasing "framework" (api documentation coming soon).

Celia is based on my personal fork of picolove, Which has much better compatibility with PICO-8 carts than other forks, from what I've seen.

I'd love to hear any feedback, bug reports, and feature requests/suggestions. In addition, if you make a tas using Celia, feel free to post it here, I'd love to see it!

As a demonstration of the tas tool, I made a tas of Get Out of this Dungeon by @Insanus, which you can check out here

Happy TASing!

P#121017 2022-11-19 22:01 ( Edited 2022-11-19 22:16)

[ :: Read More :: ]

recently, while investigating the pico-8 preproccesor, i found some really weird behaviour, which culminated in some very strange token optimizations, and an infinite token exploit

Arithmetic assignment save

often, you want to perform multiple arithmetic operations on a varible, then assign it to itself. for example, a=2*a+1

the 2 ways you'd do this normally in pico-8
are

a=2*a+1 -- 7 tokens

a*=2
a+=1 -- 6 tokens

however, using preproccesor trickery, we can reduce it even more:

a*=2 
+1 -- 5 tokens

the reason this works is because the preprocessor patching for += works line-wise, so this would be patched to

a= a*(2)
+1

Infinite token exploit #1

this exploit allows you to run any code that is on 1 line, and doesn't use any pico-8 preproccesor based syntax extensions (i.e. +=, shorthand if, ?), while only costing 8 tokens
it works as follows:

a={}
a["[t"]+=" < your code here > t(

this looks extremely weird, because it is. note that our code is in an (unclosed) string thus, it only counts as 1 token

the preproccessor patches this code to

a={} 
a["[t"] = t"] + (" < your code here > t( ) 

we see that because of how the preproccesor parsed our expression, we don't have any unclosed strings anymore, but this still looks weird, so let's simplify it

a={} 
a["[t"] = t("] + (") 
< your code here > 
t( ) 

so, the code contains 4 parts

  1. creating an empty table
  2. assigning some value to some key in the table (not that t is the function time())
  3. actually running our code
  4. calling t()

parts 1 2 and 4 don't actually do anything, which means we ran our code while only costing 8 tokens!

Infinite token exploit #2

the previous exploit is already nice, but being limited to 1 line is a bit annoying. so let's improve it:

a={}
a['[t']+=[['
< your code here >
t(a[a[1]] 

is patched to

a={}
a['[t'] = t'] + ([['
< your code here >
t(a[a[1]] ) 

similarly to exploit #1, before patching, our code is in a multiline string, and thus only costs 1 token. after patching, it is not in a string anymore, so pico8 just runs it as regular code. so now we can run any code (with the same caveat as before of not using pico-8's preproccesor based syntax extensions), using only 8 tokens

An argument against the preproccesor

all of these exploits are caused by the preproccesor being kind of weird and finnicky. while i'm sure these specific ones can be fixed by changing it, i'm pretty convinced you could find things like these in every non-syntax-aware preprocessor. while @zep has been against adding compound operators (+=) to the syntax in the past, I think these examples (and all the other weird preproccesor behaviour) provide a decent argument for why it should be

Demo

to show the viability of this method, I made a version of celeste that only uses 5 tokens, using exploit #1 (the 3 token save comes from using _ENV instead of defining a)

Cart #fivetokenleste-0 | 2022-10-28 | Code ▽ | Embed ▽ | No License | Edit

P#119789 2022-10-28 19:51