211 lines
6.5 KiB
Ada
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;
|