Advent of Code (briefly,
“AoC”) is a series of 25 festive programming puzzles1
released daily December 1–25. Each puzzle has two parts, which use the
same text input and are related; to solve a part, you submit the right
output corresponding to the input on the website.
If you’re reading this, I suspect there’s a good chance you knew that
already, but in case you’re new to Advent of Code, let me try to briefly
explain why I like Advent of Code, from the perspective of somebody
who’s spent a lot of their life so far doing programming competitions.2
The event has a fantastic community surrounding it. I’m the most
familiar with the
subreddit, which is full of helpful people, interesting discussions,
non-programming community games, and the occasional wonderfully,
spectacularlyoverengineeredsolution
to a puzzle; but I know there are also many smaller chatrooms and
subcommunities focused on, say, specific timezones or programming
languages.
Another aspect is the unique two-part format of each puzzle. Even
though they use the same input, you don’t get to see the second part
until after you’ve solved the first one, a feature that Eric Wastl
(AoC’s creator) has taken full advantage of in designing puzzles. The
second part is often a surprising twist on the first part, which keeps
you on your toes and challenges you to keep your code moderately general
or refactorable in a way that I think almost no other programming
challenges do. This sometimes even happens between days in a calendar,
when a puzzle turns out to be about some model of computation you
implemented two or five or ten days ago — hope you kept your code and
remember how it works!
Finally, AoC also has some non-rigorous puzzles that force you to
use your intuition and “human intelligence”, either by interpreting the
problem statement heuristically or writing code to let you explore the
input. There are quite a few puzzles where it’s infeasible to write code
that handles every step of obtaining the output from the input. The
result is that Advent of Code can feature quite a few challenges that
I’ve found particularly compelling because I think they simply could not
be posed on any other contest platform.3
These are the things that make AoC stand out to me, but it also does
a lot of other things well — the challenges are fun, approachable, and
varied even aside from their interrelations; there is a long, dramatic
story tying everything together (although it’s an Excuse
Plot if there ever was such a thing); and, although this is
obviously subjective, I find the website’s minimalist-adjacent,
terminal-esque aesthetic really charming (there is a lot of
detail in 2019’s calendar…
after you solve everything). I’ve only done the last two years of Advent
of Code, but it really seems like a one-of-a-kind event to me.
Anyway, one particular feature Advent of Code has is a leaderboard,
which you can get on by being one of the first 100 people worldwide to
solve each puzzle. The competition is fierce — every year, thousands of
people compete to get on the leaderboard. Near the start of AoC 2019, mcpower
(reddit
discussion) and Kevin
Yap (reddit
discussion) wrote some articles about how to do this, both of which
are worth reading. I also thought about writing such an article and
started a draft, but I didn’t get it anywhere close to publishable
before AoC had concluded, at which point I assumed few people would be
interested. But here it is now.
By a strange quirk of fate, I have started writing C++ for a
living.
Learning C++ was about as complicated as I think I expected it to be.
By line count, I’ve written a lot of C++ for programming competitions,
but I knew that I had only ever used a small cross-section of the
language: basic control flow and variables, STL containers and
algorithms, structs on which you mechanically define
bool operator<(const T& other) const so STL
algorithms can order them, and the very occasional macro or templated
helper function. There were many features I wasn’t even aware
existed.
In the process of learning C++ professionally, one rabbit hole I fell
into quickly was C++11’s defining feature, the rvalue
reference, and how it can be used to implement move
semantics and perfect forwarding. By poring over a copy of
the widely recommended book Effective
Modern C++, by Scott Meyers, and a few dozen StackOverflow
answers and blog posts, I roughly understood it after a few days, but
still had a sort of blind-men-feeling-the-elephant feeling. I was
confused about what lay under some of the abstractions I had been using,
unsure of the full shape of the pitfalls that some of the guides had
pointed out to me, and generally uncomfortable that there were still
many small variations of the code I had seen that I couldn’t predict the
behavior of. It took many more days to work myself out of there, and I
wished I had had a guide that explained rvalue references and their
applications to a bit more depth than what might be necessary for
day-to-day use. So here’s my attempt to explain rvalue references in my
own fundamental I-want-to-know-how-things-work-no-really
style.
(If this vision doesn’t resonate with you, there are many other posts
explaining rvalue references out there that you might prefer. Feel free
to just skim the executive summary and/or check out some of the linked
articles in the Background section.)
Executive Summary
I got… pretty carried away when writing this post, and a lot of it is
just for my own understanding, which may or may not be useful to
readers. Here’s a much more concise rundown (assuming you know basic C++
already):
Not a very completionist run. I graded myself pretty strictly though
— both sides of every “and” need to count; “all” means literally all;
fuzzy actions and phrases require full psychological commitment to
qualify.
One thing many mathematically-inclined programmers run into when
implementing mathematical algorithms, particularly number-theoretic
ones, is that the modulo
operation doesn’t behave how they expect or prefer.
In many languages, this operator is denoted %.
Concretely, one might prefer that, if the second argument is positive,
then the modulo operation would always give a nonnegative result. Under
this behavior, the expression (-5) % 3 would evaluate to
1 rather than -2. This is a lot more useful
for number theory because then for positive integers n, the
% n operation actually maps integers to exactly the
n canonical representatives for the residue classes. As a
result, \(a \equiv b \mod n\) if and
only if a % n == b % n. You can also do things like index
into a length-n array with a % n and know that
the index will be in-bounds. Finally, there are also optimization
opportunities: modding by a power of 2 becomes equivalent to a simple
bitwise AND, which is really fast on modern computers.
A few programming languages, notably Python, do implement
% this way. However, the majority of languages today,
including pretty much everything remotely descended from C, do not;
instead, (-5) % 3 is -2. This post attempts to
track down why.
The first thing to note is that there is a more important
number-theoretic identity we’d like to have:
In words, the integer division and modulo operators should give a
breakdown of a into a sum of some copies of b
plus a remainder. Note that this equation also implies that specifying
the rounding behavior of division is equivalent to specifying the sign
behavior of the modulo operation, which will come up repeatedly
later.
It’s also very uncontroversial that that remainder should have no
copies of b, positive or negative, left over, which gives
the constraint:
\[|\texttt{a \% b}| < |\texttt{b}|.
\tag{2}\]
Every programming language I can think of satisfies these two
constraints.1 So far so good. However, these two
constraints don’t uniquely determine the values of a % b
when a isn’t divisible by b; there are two
possible values for a % b, one positive and one negative.
Concretely, we could express \(-5\) as
either \((-1) \cdot 3 + (-2)\) or \((-2) \cdot 3 + 1\), so
(-5) % 3 could be -2 or 1.
It’s still mostly uncontroversial that, if a and
b are both positive, then a % b should be
nonnegative as well; we could call this constraint (3).2
However, if a is negative and b is positive,
programming languages start to diverge in their behavior. Why?
Fifth year with ✈✈✈ Galactic Trendsetters ✈✈✈ (previously: 2019, 2018, 2017, 2016; writing with Random in 2015).
We won!
But apparently I’m still really busy, so I’ll probably just focus on
a few highlights of things I personally experienced and get this post
out the door.
The theme in a few sentences: As suggested by the
invitation sent out to teams, there was a wedding. It turned out to
actually be a real wedding between two prolific puzzle writers on Left
Out, the organizing team. To the surprise of everybody who expected
either one, three, or maybe five subversions of this announcement, the
wedding actually succeeded (a COIN that would prevent all love in the
universe was briefly reported missing, but then immediately found by the
bride). Instead, we joined the newlyweds on a trip to an amusement park
called Penny Park, which we learned was struggling and closing this
weekend. It was up to help its regain its popularity and stay open by
solving puzzles. The Mystery Hunt subreddit
has lots of other cool links and discussions, including this animated
bar chart, so I won’t spend more time here.
I’m pretty sure the best story I personally experienced was how we
solved the metameta, the biggest puzzle that stood between us and
winning hunt, and the moment at which we went, holy smokes we might have
won Mystery Hunt! We got all the pennies required for the final metameta
at Sunday 5:48:09am, and it took us until 12:47:30pm, just under
seven hours later, to solve it…
This is a weird song choice — I have not even watched the movie. But
there is a story, and there is a thematic correspondence.
The story is that I was interning remotely at a coworking space over
the summer. One night, I attended a karaoke event hosted there, the kind
where adult human beings socialize and where I didn’t know anybody else,
and I sang this song. Afterwards, another attendee told me that her kid
(yeah, you know, people in my reference class have children) loved Moana
and was really excited about my performance.
The thematic correspondence is less obvious and harder for me to
describe. I’m going much less further this year than I could be, and am
less sure about next year than I expected to be at this point for
reasons I’m not ready to share yet (this seems to be happening more and
more on this blog, but there’s not much I can do about it — so it goes).
But it really is the case that there are some things I can’t deny about
myself, some attractor states that my values and way of thinking keep
dragging me towards.
Frivolous examples: I went through another online Dominion phase and
at least two Protobowl phases, the highlight of which is learning a good
deal about Émile
Durkheim and then buzzing on him the next day. I did Advent of Code
again, with the same golfing setup as last year, a foray into making an
auxiliary over-the-top
leaderboard in Svelte, and (surprisingly to myself) getting first. I
have a shiny Charizard with Blast Burn now.
It’s December, so it’s time for a lot of things, including Advent of Code. I will not be able
to be as competitive as I was last year, and already lost a lot of
points to a really silly mistake on day 1, but I’ll be playing when I
can and golfing
the problems when I have time (so far: 7 + 14 bytes).
As one might expect, Day 1 is not too complex,
but the second part can be analyzed to some mathematical depth and was
discussed a bit on Reddit; plus, it occurred to me recently that I set
up KaTeX on my blog but never used it, so I was looking for an excuse to
write some equations anyway.
The problem statement for part 2, in brief: We are tasked with
calculating the total mass of fuel required to launch a rocket module of
a given mass. For something of mass \(m\), one can compute the directly required
mass of fuel by dividing \(m\) by 3,
rounding down, and subtracting 2; if the result is negative, it is taken
to be 0 instead. However, the directly required fuel also requires fuel
itself, calculated from its own mass by the same procedure, and that
required fuel requires fuel based on its own mass, and so on until you
reach fuel with 0 requirement.
This was a web challenge with a few pages. The “User” page displayed
some user information:
Name: Alice
Email: [email protected]
Group: CSAW2019
Intro: Alice is cool
Name: Bob
Email: [email protected]
Group: CSAW2019
Intro: Bob is cool too
The “About” page simply told us, “Flag is located at /flag.txt, come
get it”. The most interesting page was “Upload”, where we could view an
example users XML file:
Ahhh, CSAW CTF. Amidst all the other CTFs where we’re competing with
security professionals who probably have decades of experience and who
follow security developments for a living or whatever, there remains a
competition where scrubs like me can apply our extremely basic CTF
skills and still feel kinda smart by earning points. Now that I’ve
graduated and am no longer eligible, our team was pretty small and I
didn’t dedicate the full weekend to the CTF, but it means I got to do
the really easy challenges in the categories that I was the worst at, by
which I mean pwn.
baby_boi is pretty much the simplest possible modern ROP
(the modern security protections NX and ASLR are not artificially
disabled, but you get everything you need to work around them). We even
get source code.
So there’s nothing novel here for experienced pwners, but I feel like
there is a shortage of tutorials that walk you through how to solve a
textbook ROP the way you’d want to solve it in a CTF, so here is a
writeup.