A few notes to self, in case I'm looking for previous stuff to copypaste in some future december or something...
Maybe this mostly just "Lua stuff I think is fun" and not necessarily always that AoC related? Anyway I like Lua, it's smol. When I AoC with Lua I usually make pretty self-contained programs that only depend on the plain and regular Lua implementation and its standard library.
Here's some example code. It does the file stuff that usually needs to be done and the "works in the browser as well" thing.
If not line by line: Possibly f:read("*all")
instead of f:lines()
.
Here's foldl
because because:
Cataphatically matching on and grabbing what I want is often preferable to apophatically splitting on what I don't want.
If I e.g. have a line of input like "123: 54 34 12"
and I need to do one thing with the number before the :
and one thing with the rest of the numbers. I typically ignore the :
and just gmatch
to get the numbers:
If there had been a varying number of numbers before the :
I'd probably match and grab the before and after pieces before doing work on those:
(The match
function and the iterator function you get from gmatch
return multiple values when patterns with multiple captures match something. It's nice.)
string.find
, it returns start position and stop index if it finds. Sometimes I wanna keep finding from the stop index + 1. The values returned for empty capture groups are also positions, so I guess I don't really need find
? Certainly not if I'm mostly interested in the stop + 1 thing:
This area of the Lua manual is sometimes handy.
If I want to e.g. use x,y-positions as keys in a table, for each unique x,y-value there should be only one table. "Constructing" the same position twice gives me two references to the same table, and not two tables with equal x and y values:
See also: Programming in Lua: 17.1 – Memoize Functions.
The "weak" stuff (__mode = "v"
) is not usually important particularly important for AoC stuff.
I typically use vectors for positions and also for directions. Som stuf:
And then things like these might happen:
{}
. That's it, that's the data structures.
Sets are tables where we only care about the keys (and just set the values to true or something).
You can use the same table as a list and a dictionary and a set if you'd like. Unless you can't because the keys are clashing or something. Or maybe don't want. But like. Some times.
Helper function if I need to do the cachy memoization thing with more than one type of data structure. I typically want to pass in a "key" function and a constructor function:
Metatables and Metamethods in the manual`.
Most of the metamethods enables syntax like the +
/__add
above:
Note that the first argument of a binary operation like __add
might not be the one with the metatable with that __add
function. While the first operand of the +
expression takes precedence, the _add
of the second operand can get called:
Related: Greater than (or equal to) expressions are rewritten to less than (or equal to) expressions before things get to __lt
(or __le
).
Also the metatable is generally only reached through the metamethods and not more directly. If I want something kind of like "methods defined by a class" then I probably wanna go through __index
. This works:
This doesn't:
For functionmethodstuff, some things are pretty equivalent:
__index
is also sometimes nice for stuff like initializing default values in a table when needed:
(__newindex(t, k, v)
is also a thing. Haven't used it much. I'm a bit uncertain about it and the "key
must not already be present in table" condition.)
I usually use fairly pure and functional data structures for smol things like vectors, but not for larger things like 2D maps. The immutable stuff is easiest to deal with when trying different alternatives and backtracking and stuff like that: I have a value. I try one thing. I try another thing. The value didn't change in-between. (Also, if I'm doing the cachy memoization stuff where I'm always using the same table for the same x,y-value, things will very break if I start changing the x,y-values of vectors after constructing them.)
With mutable stuff things are more awkward and I might need to "undo" changes instead. For small changes I can probably just do that kind of ad hoc. But if I wanna do a bunch of changes and then be able to throw those changes away, __index
might be useful: I can keep an original table unchanged while "inheriting" its values through __index
.
When I'm using the over
table below, writes modify over
and reads read from original
if the key is not present in over
:
if-then-else is statement stuff. For expression stuff, and
and or
is nice.
nil
and false
is false, all else is truthtrue and 4
evaluates to 4
, false or 2
evaluates to 2
Oh kay.