Package: pakettic
PaketticPakettic is a command-line tool for minifying and compressingTIC-80fantasy console carts. The tool is written
in Python (3.9+) and used especially forsizecoding. It compresses
existing carts approximately ~1.2% better than best alternatives, and by
using itsmagic comments, pakettic might find code
that compresses even better.InstallationInstalling with pip:$pipinstallpaketticInstalling the latest main branch from GitHub:$pipinstallgit+https://github.com/vsariola/pakettic.git@mainInstalling a checked out version of the repository:$pipinstall-epath/to/paketticInstalling a checked out version of the repository usingpoetryfor a nice virtual environment with
locked dependencies (run inside the pakettic folder):$poetryinstallUsageTo compress a cart, run:$paketticpath/to/cart.ticIf your PATH is not configured to include pip installed executables, you
can use$python-mpaketticpath/to/cart.ticIf you installed using poetry into a virtual environment, you need to
prependpoetry runbefore every command e.g.$poetryrunpaketticpath/to/cart.ticPakettic supports both .tic and .lua carts. Multiple input files may be
defined. Input files are globbed, so?,*, and**work as
wildcards for a single character, multiple characters and a directory,
respectively.For a full list of command line options, see:$pakettic--helpSee alsotips for command line argumentsRunning all tests:$poetryrunpython-munittestdiscover-stestsFeaturesPakettic first parses the LUA-script to an abstract syntax tree, and
then uses a local optimization algorithm
(simulated annealing,late acceptance hill climbingor
its variant diversified late acceptance search) to randomly mutate the
syntax tree & see if it compresses better. Implemented mutations
include:shortening variable namesflipping comparisons>,<,>=,<=,~=, and==reordering arithmetic operators+,-,*and/and bit logic
operators&,~and|convertinga^2intoa*aand vice versausing either single or double quotes for all stringsconverting whole hexadecimals into decimalsconvertfor a,b,1 dointofor a,b doand vice versareordering statements: statements that can be reordered are marked
withmagic commentsalternative expressions: alternatives are marked withmagic commentsfolding constant expressionsInternally, pakettic useszopflifor
the compression.load'<code>'is parsed asfunction(...)<code>endso you can easily
recompress already compressed carts. Conversely,function()<code>endorfunction(...)<code>endis replaced withload'<code>'during
compression.Note thatfunction(...)<code>endandload'<code>'are not 100%
semantically identical: the load version cannot access locals in the outer
scope. For example:local x="hello" function f()print(x)endworks butlocal x="hello" f=load'print(x)'does not. Since locals are rarely
used in size-coding, we default to using the load-trick, but you can
disable it with the command-line parameter--no-load.However, pakettic does not convert functions with parameters. In
particular, pakettic does not automatically convertfunction SCN(x)<code>endintoSCN=load'x=...<code>', because they
are not semantically identical: in the load version,xis now global
and thus could trash a global variable, unintentionally breaking the
cart. To makeSCNcompress nicely, you have to write it asfunction SCN(...)x=...<code>end, taking responsibility forxnot
overwriting anything important.Unnecessary parentheses are removed from expressions so you do not have
to worry about those.Magic commentsReorderable statementsThe algorithm will try to reorder statements between--{and--}.
For example:--{a="hello"b="world"--}will try botha="hello"b="world"andb="world"a="hello"to see which
compresses better.Notice that only complete statements can be reordered. Thus, this will
NOT work:--{forx=0,239dofory=0,135do--}endendA good rule of thumb is that you should be able to replace--{and--}withdoandend, respectively, and still have valid code.Statements between--{!and--}are not ordered, so you can make
blocks of statements that are kept in order within a pair of--{and--}tags.Alternative expressionsThere is a special--|operator that allows alternative expressions to
be tested, to see if they compress better. For example:5--|4--|6means that the algorithm will try 4 and 6 in place of the 5. This will
naturally show up as a comment in LUA so you will have to continue the
expression on next line if this is in the middle of an expression.--|has the lowest precedence, even lower than^, so put parentheses if
you want to try more complicated expressions e.g.(x//256)--|(x>>8)Debug codePakettic treats--![and--!]as multiline comment tags, while LUA
treats these as single line comments. Useful for including debug code in
the unpacked intro: the code will not be included in the packed cart.Tips for command line argumentsIf pakettic complains about CODE_ZIP chunk size, the code is just too
big after compression. In TIC-80, CODE_ZIP chunks do not support
multiple banks (and likely never will, as the feature is already
deprecated), and thus are unfortunately limited to 65535 bytes.--uncompressedis a temporary fix, but code will be uncompressed and
thus the size much larger.The Zopfli compression level can be set with-z<level>, with level
ranging from 0 to 5. When developing, start with-z0for fast
optimization, and only increase when necessary e.g. when you are just
a few bytes over the limit. The default Zopfli-level is 0.The algorithm uses a pseudorandom generator. Sometimes using a
different seed finds a few byte better or worse solution. Use command
line argument--seedto try different seeds.Similarly, different optimization heuristics produce slightly
different results. Try different heuristics e.g. with-alahc,-adlasor-aanneal.To avoid re-optimizing all the expressions every time, do a long
optimization run, study the results and change your expressions to the
forms that pack well. Set the number of steps with-s. Use
command-line argument-pto always print a reasonably readable
version of the best solution when one is found.By default, pakettic only includes CODE and DEFAULT chunks. DEFAULT
indicates that before loading the cart, TIC-80 loads the default cart,
setting default palette, waveforms etc. If you don't need the default
values (e.g. you set the palette yourself), save one byte by only
including CODE chunk in the cart:-ccodeWorking on a tweet-cart? Use-lto output LUA carts, which are
uncompressed. The optimization algorithm then just optimizes the
uncompressed size of the code.If the packed cart is missing sprites, music, map etc., try adding-call(or something more specific) to include necessary chunks.Do you want to use the TIC-80 sprites or the tracker, but don't like
the fact that the data chunks are uncompressed? Use-dto have
pakettic automatically convert all data chunks into hexadecimal
strings in the code, along with a small stub placed at the beginning
of the code that interprets the string and loads the data at correct
address. For example,-d -cCODE,MUSIC,PATTERNS,WAVEFORM,SAMPLES,DEFAULTwould include the
necessary chunks for the music.Known issuesAt the moment, all the branches of swappable operators are assumed to
be without side effects. If they have side-effects, the swapping might
inadvertedly swap the execution order of the two branches.The parser can crash with large carts. Carts in the size coding range
(few thousand characters) do not seem to cause problems, but crashes
have been observed parsing carts with tens of thousands of code
characters. This may be related to how the pyparsing grammar is
defined, which could result in highly recursive parsing and eventually
stack overflows.CreditsCode contributors:Veikko Sariola/pestis,wojciech-graj,koorogi,dezgegTest corpus contributors:psenough,ilmenit,gigabates,gasman,pellicus,luchak.LicenseMITThe test corpus carts have their own licenses, see the license files in
the subdirectories of thecorpusdirectory.