For part 2, I took the naive approach, inserting all of the totals into a Vector, sorted, reversed, then summed the top 3 elements. The Reverse_Elements is expensive and could be avoided, but I think counting from 1 … 3 makes the code easier to read.
with Advent_IO; use Advent_IO;
with Advent_IO.Integers; use Advent_IO.Integers;
with Ada.Containers.Vectors;
procedure Day1_2 is
package Natural_Vectors is new Ada.Containers.Vectors
(Index_Type => Positive,
Element_Type => Natural);
use Natural_Vectors;
package Natural_Sorting is new Natural_Vectors.Generic_Sorting;
use Natural_Sorting;
V : Vector := Empty_Vector;
Total : Natural;
begin
while not End_Of_Input loop
declare
Line : constant String := Read_Until (Input, CRLF);
begin
if Line'Length = 0 then
Append (V, Total);
Total := 0;
else
Total := Total + Natural'Value (Line);
end if;
end;
end loop;
Append (V, Total);
Sort (V);
Reverse_Elements (V);
Total := V (1) + V (2) + V (3);
Put (Output, Total);
New_Line (Output);
end Day1_2;
[rocher][1][Ada] My solution for part 1. Quite simple, it’s just the first day…
with Ada.Text_IO; use Ada.Text_IO;
procedure Day01_P1 is
Input : File_Type;
Calories : Natural := 0;
Max_Calories : Natural := 0;
function Get_Max_Calories (K: Natural) return Natural is
(if K > Max_Calories then K else Max_Calories);
begin
Open (Input, In_File, "input.txt");
loop
declare
Line : String := Get_Line (Input);
begin
if Line'Length = 0 then
Max_Calories := Get_Max_Calories (Calories);
Calories := 0;
else
Calories := @ + Natural'Value (Line);
end if;
end;
exit when End_Of_File (Input);
end loop;
Max_Calories := Get_Max_Calories (Calories);
Close (Input);
Put_Line ("Answer:" & Max_Calories'Image);
end Day01_P1;
Here part 2 using an array:
with Ada.Text_IO; use Ada.Text_IO;
procedure Day01_P2 is
Input : File_Type;
Calories : Natural := 0;
Max_Calories : array (1 .. 3) of Natural := (0, 0, 0);
procedure Get_Max_Calories is
begin
for I in Max_Calories'Range loop
if Calories > Max_Calories (I) then
Max_Calories (I+1 .. Max_Calories'Last) := Max_Calories (I .. Max_Calories'Last-1);
Max_Calories (I) := Calories;
exit; -- this loop
end if;
end loop;
end Get_Max_Calories;
begin
Open (Input, In_File, "input.txt");
loop
declare
Line : String := Get_Line (Input);
begin
if Line'Length = 0 then
Get_Max_Calories;
Calories := 0;
else
Calories := @ + Natural'Value (Line);
end if;
end;
exit when End_Of_File (Input);
end loop;
Get_Max_Calories;
Close (Input);
Put_Line ("Answer:" & Natural'Image
(Max_Calories (1) + Max_Calories (2) + Max_Calories (3)));
end Day01_P2;
I decided to have fun with it and layout a design first. I also wanted to use some various features from Ada, so I embellished a bit on the code. I didn’t generate a github repo yet for it and I may not do all days. Not sure yet.
Day_01.ads
with Ada.Containers.Vectors;
-- Top Level package
package Day_01 is
-- Holds the number of calories per item
type Calories is new Natural;
package Calories_Vectors is new Ada.Containers.Vectors
(Index_Type => Positive,
Element_Type => Calories);
-- Holds info about each elf
type Elf is record
Calories : Calories_Vectors.Vector;
end record;
-- Returns the total calorie consumption for the elf
function Total_Calories(Self : Elf) return Calories;
package Elf_Vectors is new Ada.Containers.Vectors
(Index_Type => Positive,
Element_Type => Elf);
-- A list of elves
subtype Elf_List is Elf_Vectors.Vector;
-- Open a file and generate a list of elves with calories
function Parse_File(Filename : String) return Elf_List;
-- This holds the results of an Elf_List search for who
-- has the most calories. If Valid = False, then no
-- elves were in the list
type Search_Result(Valid : Boolean := False) is record
case Valid is
when False => null;
when True =>
Index : Positive := 1;
Calories : Day_01.Calories := 0;
end case;
end record;
-- Searches through an elf list for the one with the
-- most calories;
function Most_Calories(List : Elf_List) return Search_Result;
end Day_01;
Day_01.adb
with Ada.Text_IO;
with Ada.Strings.Fixed;
with Ada.Exceptions;
with Ada.Command_Line;
package body Day_01 is
function Total_Calories(Self : Elf) return Calories is
Total : Calories := 0;
begin
for Calories of Self.Calories loop
Total := @ + Calories;
end loop;
return Total;
end Total_Calories;
function Parse_File(Filename : String) return Elf_List is
File : Ada.Text_IO.File_Type;
List : Elf_List;
Elf : Day_01.Elf;
-- Attemps to add an elf to the list if
-- one has data associated with it. Once
-- an elf is added, the Elf variable's
-- data is reset for the next one.
procedure Add_Existing_Elf is
use type Ada.Containers.Count_Type;
begin
if Elf.Calories.Length > 0 then
List.Append(Elf);
Elf.Calories := Calories_Vectors.Empty_Vector;
end if;
end Add_Existing_Elf;
begin
Ada.Text_IO.Open
(File => File,
Name => Filename,
Mode => Ada.Text_IO.In_File);
while not Ada.Text_IO.End_Of_File(File) loop
declare
Line : constant String
:= Ada.Strings.Fixed.Trim
(Source => Ada.Text_IO.Get_Line(File),
Side => Ada.Strings.Both);
Calories : Day_01.Calories := 0;
begin
if Line'Length = 0 then
-- If we received a blank line, then try to
-- add any existing elf to the list
Add_Existing_Elf;
else
-- Format the string to a just a number
-- using the Value attribute of the
-- Calories type
Calories := Day_01.Calories'Value(Line);
Elf.Calories.Append(Calories);
end if;
end;
end loop;
-- Get last elf if there is one
Add_Existing_Elf;
Ada.Text_IO.Close(File);
return List;
exception
-- If an exception was raised, it means there is an
-- input file error. Print out some debug and return
-- an empty list
when e : others =>
Ada.Text_IO.Put_Line("Input File Error");
Ada.Text_IO.Put_Line(Ada.Exceptions.Exception_Information(e));
return Elf_Vectors.Empty_Vector;
end Parse_File;
function Most_Calories(List : Elf_List) return Search_Result is
Calories : Day_01.Calories := 0;
Max_Calories : Day_01.Calories := 0;
Elf_With_Max : Positive := 1;
Index : Positive := 1;
begin
for Elf of List loop
Calories := Total_Calories(Elf);
if Calories > Max_Calories then
Elf_With_Max := Index;
Max_Calories := Calories;
end if;
-- This check is for the unrealistic situation
-- that the vector has Positive'Last number of
-- elements
exit when Index = Positive'Last;
Index := @ + 1;
end loop;
-- If the loop was entered at all, Index will increment.
-- This is how we determine at least one elf was found
-- in the list.
if Index > 1 then
return
(Valid => True,
Index => Elf_With_Max,
Calories => Max_Calories);
else
return (Valid => False);
end if;
end Most_Calories;
-- Program variables
Filename : constant String
:= (if Ada.Command_Line.Argument_Count > 0 then
Ada.Command_Line.Argument(1)
else
"input.txt");
List : constant Elf_List := Parse_File(Filename);
Elf_Count : constant Ada.Containers.Count_Type := List.Length;
Elf_With_Max : constant Search_Result := Most_Calories(List);
begin
-- If an elf was found, print it along with the total calories
-- the elf had.
if Elf_With_Max.Valid then
Ada.Text_IO.Put_Line("Found" & Elf_Count'Image & " elves");
Ada.Text_IO.Put_Line
("Elf #" & Elf_With_Max.Index'Image
& " has" & Elf_With_Max.Calories'Image
& " total calories");
else
Ada.Text_IO.Put_Line("No Elves Found");
end if;
end Day_01;
I just withed that in an bare empty main adb file.
I’ve tried 'Reduce from Ada 2022 and found a compiler bug. Also I’ve spent time with SPARK. This my very first experience with SPARK and it seems it works for day 1.
As usual, it runs on both HAC (command line: hac aoc_2022_01.adb; LEA: press F9 key), and a “full Ada” compiler. For GNAT, you can use the aoc_2022.gpr project file.
Hello there
First time doing the AoC, here’s my solution (on compiler-explorer): part 1 and part 2
Had very little time so went for the most naive solution I could come up with
If anyone’s interested in benchmarking their code, I wrote a Python script to generate an input file with 10M elves. My best solution so far solves it in 12.216 seconds (user time, measured by perf stat) on an i7-8700.
with Ada.Containers; use Ada.Containers;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
procedure Day1 is
-- Decided to use a vector for this, since it was the quickest
package Nat_Vect is new Ada.Containers.Vectors
(Index_Type => Natural, Element_Type => Natural);
package Nat_Sort is new Nat_Vect.Generic_Sorting;
use Nat_Vect, Nat_Sort;
My_File : File_Type;
Sums : Vector;
Sum : Natural := 0;
begin
Open (My_File, In_File, "input/Day1-1.txt");
while not End_Of_File (My_File) loop
declare
Next_Line : constant String := Get_Line (My_File);
begin
if Next_Line = "" then
-- Add this sum to our vector.
Sums.Append (Sum);
-- Reset sum
Sum := 0;
else
-- Continue to accumulate Sum
Sum := Sum + Natural'Value (Next_Line);
end if;
end;
end loop;
-- Add whatever is left. TODO: Stop doing this?
Sums.Append (Sum);
-- Now sort our vector
Sort (Sums);
Put_Line
("The highest calory elf has " & Natural'Image (Sums.Last_Element) &
" calories.");
-- Now all we have to do is sum the top 3.
Sum := 0;
for I in -- This gets our top 3 picks, since it's been sorted.
(if Sums.Length > 2 then Sums.Last_Index - 2 else Sums.First_Index) ..
Sums.Last_Index
loop
Sum := Sum + Sums (I);
end loop;
Put_Line ("Top 3 elf calories combined are: " & Sum'Image);
end Day1;
Great minds think alike I suppose; seems like most people did a very similar solution, with sorted vectors.
I’m really looking forward to how the more complicated ones get solved.
I guess your test is for the following trap: if you test the maxima after each separator but not after the end of the file, you get wrong answers (111 on input #1 and the initial value for the maximum on input #2).
It did feel a bit sacrilegious writing it in Python, but a lot of people want to play with jumbo inputs that aren’t using Ada and don’t have a compiler for it. I figure nearly every machine has a Python interpreter available.