Learning/AoC/2024/day_2/day_2.adb
2025-03-05 22:51:09 +01:00

211 lines
6.5 KiB
Ada

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Containers.Vectors;
procedure Day_2 is
type Safety is (Unsafe, Safe);
type Ordering is (Increasing, Decreasing);
type Levels is array (Natural range <> ) of Integer;
type Report is
record
Values : Levels(0 .. 9) := (others => 0);
Count : Natural := 0;
end record;
type Reports is array (Natural range <>) of Report;
function Is_Level_Safe( First, Second : Integer; Prev : Ordering ) return Safety is
Diff : Integer := Second - First;
ADiff : Natural := abs(Diff);
begin
if ADiff < 1 or ADiff > 3 then
return Unsafe;
else
if Diff > 0 and Prev = Increasing then
return Safe;
elsif Diff < 0 and Prev = Decreasing then
return Safe;
else
return Unsafe;
end if;
end if;
end Is_Level_Safe;
-- Safe for day 2 2:
-- 5 6 4 3 2 1
-- 5 4 6 7 8 9
-- 5 5 6 7 8 9
-- 5 5 4 3 2 1
-- For all wrong levels in the middle of the report, where a prevailing
-- order can already be determined, and easy algorithm is to jump ahead
-- n levels from the unsafe integer and determine if that is safe. If the
-- jump is less than the amount of chances, then it will catch all errors.
--
-- The problem resides in determining the correct order. In previous
-- iterations, I used the first two levels to determine the errors,
-- but in the above examples, the first order was incorrect.
--
-- The easiest solution I have found is to nest the above algorithm
-- in another loop that skips ahead from the report. This does increase
-- the time complexity, although in the case of day 2.2 only to 2*n.
function Calculate_Report( R : Report; Chances : Integer ) return Safety is
Current_Chances : Integer := Chances;
Skip_Ahead : Natural := 0;
Prev_Order : Ordering;
Sum : Integer := 0;
begin
for I in R.Values'First .. R.Values'Last - 1 loop
declare
Diff : Integer := R.Values(I+1) - R.Values(I);
begin
if Diff > 0 then
Sum := @ + 1;
elsif Diff < 0 then
Sum := @ - 1;
end if;
end;
end loop;
if abs(Sum) < Chances then
return Unsafe;
else
if Sum > 0 then
Prev_Order := Increasing;
else
Prev_Order := Decreasing;
end if;
end if;
while Skip_Ahead <= Chances loop
Current_Chances := Chances - Skip_Ahead;
declare
First_Index : Natural := R.Values'First + Skip_Ahead;
Second_Index : Natural := First_Index + 1;
Diff : Integer := R.Values(Second_Index) - R.Values(First_Index);
begin
while (Second_Index < R.Count) and (Current_Chances >= 0) loop
if Is_Level_Safe(R.Values(First_Index), R.Values(Second_Index), Prev_Order) = Safe then
First_Index := Second_Index;
Second_Index := @ + 1;
else
Current_Chances := @ - 1;
Second_Index := @ + 1;
end if;
end loop;
if Current_Chances >= 0 then
return Safe;
else
Skip_Ahead := @ + 1;
end if;
end;
end loop;
return Unsafe;
end Calculate_Report;
function Get_Safe_Reports( R : Reports; Chances : Integer ) return Natural is
Sum : Natural := 0;
begin
for Rep of R loop
if Calculate_Report( Rep, Chances ) = Safe then
Sum := @ + 1;
end if;
end loop;
return Sum;
end Get_Safe_Reports;
procedure Diff_Reports( R : Reports ) is
begin
Put_Line("Running diff");
for Rep of R loop
declare
Result_First : Safety := Calculate_Report(Rep, 0);
Result_Second : Safety := Calculate_Report(Rep, 1);
begin
if Result_First /= Result_Second then
Put_Line("Result First: " & Result_First'Image);
Put_Line("Result Second: " & Result_Second'Image);
Put_Line("Report: " & Rep'Image);
end if;
end;
end loop;
end;
function ToNum( Num : String ) return Integer is
begin
return Integer'Value(Num);
end ToNum;
function Read_Report( Line : String ) return Report is
In_Num : Boolean := False;
Start_Num : Natural;
Result : Report;
begin
for I in Line'Range loop
case Line(I) is
when '-' | '0' .. '9' =>
if not In_Num then
In_Num := True;
Start_Num := I;
end if;
when others =>
if In_Num then
Result.Values(Result.Count) :=
ToNum(Line(Start_Num .. I-1));
Result.Count := @ + 1;
In_Num := False;
end if;
end case;
end loop;
if In_Num then
Result.Values(Result.Count) :=
ToNum(Line(Start_Num .. Line'Last));
Result.Count := @ + 1;
end if;
return Result;
end Read_Report;
function Read_File( File_Name : String ) return Reports is
File : File_Type;
package vec is new
Ada.Containers.Vectors( Natural, Report );
use vec;
Result : vec.Vector;
begin
Open( File, In_File, File_Name );
while not End_Of_File(File) loop
Result.Append(Read_Report(Get_Line(File)));
end loop;
Close( File);
return R : Reports( 0 .. Natural(Result.Length) - 1) do
for I in Result.First_Index .. Result.Last_Index loop
R(Natural(I)) := Result(Natural(I));
end loop;
end return;
end Read_File;
File_Name : String := "input_day_2.txt";
Reps : Reports := Read_File(File_Name);
begin
Put_Line("Day 2 1:" & Get_Safe_Reports(Reps, 0)'Image);
Put_Line("Day 2 2:" & Get_Safe_Reports(Reps, 1)'Image);
Put_Line("Day 2 3:" & Get_Safe_Reports(Reps, 2)'Image);
end Day_2;