Advent of Code 2020. Gives me an excuse to work with Ada.
Expense report, completed.
Password validation, completed.
Trees a downhilling toboggan might encounter, completed.
Checking validity of passports, completed. Kind of early to have an exercise drive one mad. Reacquainted myself with Ada's enumerations.
Lost your boarding bass; try to find your seat, completed.
Help people fill out customs forms, completed.
Acquainted myself with Strings.Maps
and Containers.Hash_Sets
.
Strange regulations about bags that can contain other bags, completed.
Acquainted myself with Containers.Maps
and for all
expressions.
Also learned about initializing records with default values (use => <>
).
On Jeffrey Carter's advice I changed a constant, fixed-length string
from " "
to others => ' '
.
Debugging a child's handheld, stuck on an infinite loop, completed.
This time I Pack
'd an array
of Boolean
and used renames
to create a reference.
Find the weakness in an encryption scheme, completed.
The numbers grow larger than Natural
, so I used Long_Long_Integer
.
(Probably could have gotten away with Long_Integer
.)
After solving it, I replaced a Boolean
and three if
's in the first exercise
with labels and goto
s. I'm not convinced that either is easier to follow.
Find the number of ways to arrange some adapters, completed.
I think this is the first time we can actually apply a non-brute-force solution.
My approach is not complete, however, so I defined an exception
which,
amazingly enough, was not needed.
Essentially a variant on the game of Life, completed.
I defined a function
type, made use of labeled arguments,
and eschewed the use of Vector
s in favor of two-dimensional array
s,
which means I encountered Ada's 'Length(N)
attribute.
Later updated to use task
s, with improvements in timing!
Cool, we get to work with the sidewalk metric!
Essentially we just follow some directions to figure out where we're going,
with different answers depending on how one understands the directions.
The first is egocentric (directions are relative to me!);
the second is relative to a waypoint.
I made heavy use of enumerations, especially in the first part,
where I used an two-dimensional array indexed by an enumeration
to avoid if statements.
There were also lots of case
statements.
In fact, there's not a single if
statement in the whole program,
which is probably not impressive, but it does amuse me.
(It's completed.)
Find various intersections of shuttles whose circulation time
is a prime number of minutes, completed.
I used variant record
s (i.e., a discriminant) to distinguish between shuttles
that are in service and are not.
Unfortunately, while the answer fits in Long_Long_Integer
,
some intermediate expressions do not,
so I had ("got"?) to use Ada2020's Ada.Numerics.Big_Numbers
package.
Determine the values or memory to write to,
according to a given bit mask, completed.
Time to use the mod
types! so that we can and
and or
;
Ada only allows masking with mod
types; see Turn_On
and Turn_Off
.
Finally found a place to use renames
where I didn't feel at least a little
guilty.
Double-buffered an array using a mod 2
type, following a recommendation
on an earlier assignment.
gnat also surprised me with a (correct) warning that a String
does not always
starts at index 1.
Determine the nth number of a relatively simple sequence, completed. I didn't come up with anything interesting here. Kind of disappointed in this puzzle, truth be told.
Given specifications for train tickets,
first determine the invalid tickets.
Then figure out which specification applies to which position.
This was my first time using Ada.Strings.Fixed
and Find_Token
,
which may have been overkill in this case.
It was also my first time using the &
operator for string concatenation.
I found that I could have used bitwise operations on an array of Boolean
but settled on a little more straightforward.
I also used renames
a bit more, and increasingly respectably.
Overall though the program strikes me as a bit of a mess.
I guess yesterday's breezer of a puzzle was to give us a rest before this one, completed.
Another variant of the game of life, completed. One new "feature" of "Ada" I discovered today is that gnat crashed when I tried to initialize 3- and 4-dimensional arrays using complicated aggregates that Ada 202x SHOULD allow. So I guess I will report a bug. (Done! -- update: AdaCore acknowledged bug and says it's fixed in next version.) I also made respectable use of negative array indices. (Try doing that in C/C++.)
Evaluate arithmetic expressions in unusual order, completed. Nothing really new; I did use functions as parameters. This is essentially a very simple exercise from classes in Compiler Construction.
Verify a message against rules of a grammer, most of which are non-terminal. In part 2, two of the rules form a loop.
Completed -- eventually; my first approach was incorrect, and I couldn't shake my brain of it. I took a glance at Gautier de Montmollin's solution (uses HAC Pack, which I don't have), and that helped shake my brain a bit. Eventually I worked out a solution that I think is different from his.
I did a bit of work with types here, relying on subtypes to define ranges for rules and options. I don't think I did enough with types; there should have been an Option type. One day I may rewrite this to take that into account.
Find monsters in monochrome images that have been cut into squares, then thrown up in the air & allowed to fall to the ground. Yup: rotations and reflections are in play!
Completed, using a lot of types. It may actually have been overkill. The complexity of the code reflects some of the trouble I had with the assignment. I've actually simplified it a bit already, but I've also noted where I think it should be simplified further.
Made use of constants, rename
expressions,
a for
expression to exit
a loop
,
custom ranges, for
expressions inside if
expressions,
and some other stuff I can't recollect at the moment.
If I do, I'll update.
Identify which ingredients correspond to allergens, and which do not, completed. First part asks us to identify the ingredients that do not correspond to allergens; second part asks us to identify the correct correspondence and report them in order of allergen. Since the initial data does not narrow all the ingredients down, we have to do a little work to sort that out. (Finally! Back to a relatively easy puzzle.)
Here I relied on ordered maps instead of hash maps. I also instantiated a generic sorter. "for all" expressions came in useful again.
Play a game of Combat (essentially the card game "War") with a crab who has hitched a ride on your raft. Then modify the rules to allow recursive games, which are sort of cool.
Completed.
I tried to implement this using Ada's Queues, but that didn't work out so well; I kept getting memory errors. Not easily finding any good examples online, or even any, really, I implemented a queue using an array and a modular type. [Later update: I managed to get Ada's Queues working for Part 1, but I don't understand why they weren't working last night. I can't figure out how to copy them without writing a custom function that enqueues & dequeues everything, and that's something I need for Part 2.]
I then used sets to track previous configurations in part 2. Some minor bugs meant I didn't quite get part 2 working for quite a while; in fact I grew confused with some things Ada told me about equivalent sets, and even though I was close to the right answer for a while, I dind't realize the problem was a bug in the code that implemented the rules: through a copy-and-paste error, I added a card to the wrong player's deck.
I did discover, rather to my disappointment, that the statements
type Mod52 is mod 52;
A: Mod52 := 50;
B: Mod52 := 2;
for I in A .. B
etc.
...does not cycle around the way I thought it should.
I would have liked to use a tagged type for the Decks, to work a little with the object-oriented programming in Ada, but I decided against that for now.
Rearrange a circular list of cups, completed. Rule: three cups after the "current" cup (which I keep at the head) are removed to a position after the cup whose label is one less than the current cup's label (subtracting more if we are removing that cup, and cycling to the largest cup label if we go less than 0); then the cup that now follows the "current" cup is designated "current".
In part 1, we do this 100 times with 10 cups; in part 2, we do this 10 million times with 1 million cups.
Many solutions work, and I bungled into two, but they take hours,
probably days to complete. If you have to copy most of the list repeatedly,
or if you have to search through most of the list repeatedly,
you are going to wait a looooooong time.
For instance, main_original.adb
and main_linked_lists.adb
will work,
but I gave up on both after nearly an hour, or maybe more than an hour,
once I had the final main.adb
working in just a couple of seconds!
See its documentation for details on how it works.
For this puzzle I finally used Ada's generic
facility to create
a generic package
, complete with private
objects, subprograms,
and automatic initialization. In retrospect, I'm not sure that's necessary,
but it was fun, and it does make it easy to keep the intermediate configurations
hidden from the client.
This program uses an array of 1 million 32-bit integers,
which causes a stack overflow unless you increase the default stack size.
On the Mac I used ulimit -s 13000
after using gcc
with the -fstack-usage
option to determine how much stack it was using.
A hexagonal floor layout, completed.
The size of the floor is not given, so I guessed,
got part 2 wrong the first time, made it larger on my second guess, and got it.
Employed a tagged record
, albeit without extension / inheritance.
"Exported" an enumeration by reading & realizing that its name is actually
a function, so we simply need to rename the function.
Again used negative array indices.
It's a really good idea to look at
Maxim Reznik's solution,
immensely elegant in too many ways to list here,
in particular its use of Ada.Containers.Maps
to avoid guessing the size of the floor.
I'm kind of surprised he didn't call it "Hotel Hacking".
This turned out to be an easy problem, one loop followed by an exponentiation.
Completed, using a modular type and the **
operator for exponentiation.