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;