No spoilers about spoiled food in this puzzle; IMHO it was surprisingly easier than yesterday’s.
Simple components for Ada
Simple components for Ada
No spoilers about spoiled food in this puzzle; IMHO it was surprisingly easier than yesterday’s.
I actually had a lot of problems again…
especially parsing strings. I still have to look at a good string parsing library but I barely fit in the time to do AoC.
At least I learned from the past days to use Long_Long_Integer!
I’ll have to look at how others implemented the range. My approach may be a bit overdone/underdone
type ID_Range is record
Begining : Long_Long_Integer;
Ending : Long_Long_Integer;
end record;
Fresh_IDs : array (1 .. 177) of ID_Range; -- magic numbers
On my side, with Claude Code Opus 4.5, part 1 and 2 worked on the 1st try.
Relatively impressive, even if the code can be a bit lighter
Assuming you’re using gnat, you may want to try something like this (taken from my code; your issues addressed in reverse order):
type ID_Number is range 0 .. 2**64 - 1;
package ID_Vectors is new
Ada.Containers.Vectors
(Index_Type => Positive,
Element_Type => ID_Number);
subtype ID_Vector is ID_Vectors.Vector;
Questionable_IDs : ID_Vector;
and for reading and parsing them, I basically did this:
with Ada.Text_IO;
-- ...
package ID_IO is new IO.Integer_IO (Num => ID_Number);
-- ...
-- the following inside a loop in the Read_Input function
ID_IO.Get (Line, Start, Position);
ID_IO.Get (Line (Position + 2 .. Line'Last), Finish, Position);
ID_Ranges.Append (ID_Range_Record'(Start, Finish));
Some real Ada programmers may weigh in with better options, but this worked fine for me.
![]()
(+16 characters)
For me this one was basically “import solution from 2023.” Not super exciting but I’m not implementing interval merging again. I hope the difficulty ramps up - there’s not much time left with only 12 puzzles.
I had a lot of trouble getting the merging logic correct for part 2. Maybe an interval library would be a good thing, but I don’t think I ever use them outside of AoA.
In this case, you can strings read line by line. Until you encounter an empty line, you’re reading ranges. So you split the string at the hyphen, and then convert both resulting strings to numbers. There isn’t much more to it, but Ada doesn’t make such tasks as easy as e.g. JavaScript. A generic parsing library seems overkill. I’ve been looking into functions that perform common use cases, but my Ada skills are too limited to make something compact, useful and elegant.
Your range record is also how I did it. Another option is two arrays: one for start and one for end of range. Or even just one array and alternate start/end (ugly, but sometimes handy).
Nonsence:
loop
declare
Line : constant String := Get_Line (File);
Pointer : Integer := Line'First;
Low, High : Positive;
ID : Positive;
begin
Get (Line, Pointer); -- Skip blanks
if Pointer <= Line'Last then -- Non-empty line
Get (Line, Pointer, Low);
Get (Line, Pointer); -- Skip blanks
if Pointer > Line'Last then
ID := Low; -- This is ID
...
elsif Line (Pointer) = '-' then
Pointer := Pointer + 1;
Get (Line, Pointer); -- Skip blanks
Get (Line, Pointer, High);
else
raise Data_Error;
end if;
end if;
end;
end loop;
Maybe an interval library would be a good thing
A set of discrete elements is meant for the job:
Simple components for Ada
You can put ranges of elements into it and then check if an element is in the set.
if Pointer > Line'Last then ID := Low; -- This is ID ... elsif Line (Pointer) = '-' then Pointer := Pointer + 1; Get (Line, Pointer); -- Skip blanks Get (Line, Pointer, High); else raise Data_Error; end if;
That is good way to parse! I didn’t know that you could use “pointers” in the Get function. I checked the hyphen by using a separate boolean. Also used this trick to see if I am done reading the ranges and have to start checking the values.
procedure Read_Range_List (Line : String; Line_Index : Integer) is
number1 : Unbounded_String := To_Unbounded_String ("");
number2 : Unbounded_String := To_Unbounded_String ("");
read_one : Boolean := True;
begin
-- Get Ranges
if Line'Length = 0 then
reading_ranges := False;
return;
end if;
for I in 1 .. Line'Length loop
if Line (I) = '-' then
read_one := False;
elsif read_one then
number1 := number1 & Line (I);
else
number2 := number2 & Line (I);
end if;
end loop;
...
end Read_Range_List;
The way I read in the numbers might also be dumb but at least it works ¯\_(ツ)_/¯
I actually had a lot of problems again…
especially parsing strings. I still have to look at a good string parsing library but I barely fit in the time to do AoC.
At least I learned from the past days to use Long_Long_Integer!
I didn’t do this one yet, so I’m only going off what yall are talking about in this thread, but if it is just two numbers separated by a hyphen per line, each line can be parsed this way:
procedure Get_2(File : in out File_Type; Left, Right : out Integer) is
Line : constant String := Ada.Text_IO.Get_Line(File);
Hyphen : constant Positive := Ada.Strings.Fixed.Index(Line, "-");
begin
Left := Integer'Value(Line(Line'First .. Hyphen - 1));
Right := Integer'Value(Line(Hyphen + 1 .. Line'Last));
end Get_2;
You can call this for each line and then save each Left, Right pair however you like.
I have progressively lost patience with sophisticated parsers for that game.
So my parser for Day 5 is:
input_name : constant VString := +"aoc_2025_05"; ranges : constant := 187; ingredients : constant := 1000;
from, to : array (1 .. ranges) of Integer;
id : array (1 .. ingredients) of Integer;
procedure Read_Data is
f : File_Type;
begin
Open (f, input_name & ".txt");
for i in 1 .. ranges loop
Get (f, from (i)); -- NB: replaced all '-' by ' '.
Get (f, to (i));
end loop;
for i in 1 .. ingredients loop
Get (f, id (i));
end loop;
Close (f);
end Read_Data;
NB: Open, Get, Closeand so on are from the HAT package for HAC (but not only). They resemble and actually use Ada.Text_IO’s routines.
A nice thing with Ada.Text_IO’s number parsing is that you don’t have to worry about blanks or line breaks.
The only thing I was about to do with that AoC parser would have been to check End_Of_Line for the empty line which separates the ranges from the ingredient list, so ranges and ingredients would not have been hardcoded…
I have progressively lost patience with sophisticated parsers for that game.
Ada is for patient careful people.
A nice thing with
Ada.Text_IO’s number parsing is that you don’t have to worry about blanks or line breaks.
No, not in the production code. A parsing action must do exactly one thing. The definition of “blank”
or semantics of line end varies greatly in practice.
The big lie about all these examples and scripting hacks is that it is not how you parse in real life, where you need to validate input and give reasonable diagnostics. Ada is exceptionally good at that, but its code always looks longer in these exercises while wrong run-once code looks shorter.
A nice thing with
Ada.Text_IO’s number parsing
For non-text values input as text, I follow the basic rule: Always read in a complete line of text, then parse it. This avoids a number of common problems when reading the text directly as the image of a non-text value. You can use Ada.Text_IO’s sub-packages to help with the parsing, though I usually parse out substrings using tools like Ada.Strings.Fixed and pass them to ‘Value.