Tripped myself up by being too clever… My Advent library doesn’t support parsing integers like it did in previous years, so I wasted a few minutes implementing that. Could’ve just used Integer_IO if I weren’t so stubborn
Changed my mind about using a Vector halfway through and switched to an indefinite array. Made some things a little easier, but part 2 would’ve been simpler had I just stuck to Vectors. Oh well.
I tried to do part 2 by adjusting my comparison counts in Is_Safe, but that got messy and I started having off-by-one bugs… A clear sign that I was on the wrong track. Ended up going with the brute force solution and called Is_Safe with each missing level. It works, I guess.
I also tried to be clever and fast, but didn’t manage both, so went for fast using brute force, to finish before leaving for work.
I might try a more clever version later today, though; would be a good opportunity to also check out spark_unbound. So, yeah, at least I learned that vectors are not allowed in SPARK. Not surprising, but I guess every little insight counts…
Done, but I spent almost more time just parsing the report lines to get the levels than on the logic for finding if they’re safe. Is there a simple way to split up a string into separate components using a character separator?
There’s indeed a lot of work parsing the file, but … now I realized one could also do it like I do it for the JavaScript that I use to verify my solution: just paste the contents in a large matrix, and forget about text io.
BTW, today I got to use one of the Ada constructs I liked from the moment I saw them: qualified expressions. For part 2, I’m using
if (for some Excl in 0 .. N - 1 => Report_Minus_1_Is_Safe (Report, N, Excl))
There might be better ways, but I leverage the Index function from Ada.Strings.Fixed:
with Ada.Strings.Fixed;
with Ada.Containers.Indefinite_Vectors;
with Ada.Text_IO;
procedure Example is
package String_Vectors is new Ada.Containers.Indefinite_Vectors
(Index_Type => Positive,
Element_Type => String);
function Split(Item : String; Separator : String) return String_Vectors.Vector;
function Split(Item : String; Separator : String) return String_Vectors.Vector is
First : Natural := Item'First;
Last : Natural := Item'Last;
begin
return Result : String_Vectors.Vector do
loop
Last := Ada.Strings.Fixed.Index(Item(First..Item'Last), Separator);
exit when Last = 0;
Result.Append(Item(First .. Last-1));
First := Last + Separator'Length;
end loop;
Result.Append(Item(First .. Item'Last));
end return;
end Split;
v1 : String_Vectors.Vector := Split("a b c"," ");
begin
for Item of v1 loop
Ada.Text_IO.Put_Line("Substring: " & Item);
end loop;
end Example;
That’s a very over simplified logic with very little error checking, so it generates a vector element even if the string is empty or there are two back to back separators (but the added element is a null string, so that is checkable), but that can be adjusted by the programmer.
For splitting your string, you can use the csv package
and use ’ ’ as a separator.
However, another, IMHO much simpler, solution is to use Get (file, i) from Ada.Text_IO.Integer_IO.
You know that you are at the end of a line with a call to End_Of_Line (file).
No need to absorb the end-of-line character(s), nor the spaces, it’s done for you.
You can see my AoC solution for the details (link below).
Done with HAC, but HAC’s toolbox just calls Ada.Text_IO.* .
Basically the parser looks like this:
while not End_Of_File (f) loop
last := last + 1;
Get (f, data (last));
if End_Of_Line (f) or End_Of_File (f) then
-- ... Do something with the complete line
last := 0;
end if;
end loop;
The or End_Of_File (f) is there for the case the last line doesn’t end with an end-of-line.
So far, so good
Clearly not the best solution out of there but it works and it’s finally quite simple for my point of view.
I’ll check the other solutions later.
if
(for some N in Numbers'Range =>
Is_Safe -- Pass a dynamically-created array into is_safe
((if N = Numbers'First then
Numbers (N + 1 .. Numbers'Last)
elsif N = Numbers'Last then
Numbers (Numbers'First .. N - 1)
else
Numbers (Numbers'First .. N - 1) &
Numbers (N + 1 .. Numbers'Last))))
then
Result_P2 := @ + 1;
end if;
I did, but I usually prefer to hand roll the ascii-to-integer conversion. I’ve noticed on embedded platforms that GNAT’s ‘Value implementations are often extremely slow and don’t get vectorized/inlined properly.