aoa_22_20220720.0.0_6d9ebd71/src/day11_2.adb

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
with Ada.Containers.Vectors;
with Ada.Strings.Fixed;
with Ada.Text_IO;
with PragmARC.Math;

procedure Day11_2 is
   subtype Monkey_ID is Integer range 0 .. 7;

   package Item_Lists is new Ada.Containers.Vectors (Index_Type => Positive, Element_Type => Natural);

   type Operand_Info (Self : Boolean := True) is record
      case Self is
      when False =>
         Value : Natural;
      when True =>
         null;
      end case;
   end record;

   type Destination_List is array (Boolean) of Monkey_ID;

   type U64 is mod 2 ** 64;

   type Monkey_Info is record
      Item        : Item_Lists.Vector;
      Operation   : Character;
      Operand     : Operand_Info;
      Modulus     : Natural;
      Destination : Destination_List;
      Inspections : U64 := 0;
   end record;

   type Monkey_List is array (Monkey_ID) of Monkey_Info;

   procedure Apply_Round (ID : in Monkey_ID; Monkey : in out Monkey_List);
   -- Performs a round for monkey ID, emptying Item and updating Inspections

   LCM : Natural;

   procedure Apply_Round (ID : in Monkey_ID; Monkey : in out Monkey_List) is
      Item    : Natural;
      Operand : Natural;
   begin -- Apply_Round
      All_Items : loop
         exit All_Items when Monkey (ID).Item.Is_Empty;

         Item := Monkey (ID).Item.First_Element;
         Monkey (ID).Item.Delete_First;
         Monkey (ID).Inspections := Monkey (ID).Inspections + 1;
         Operand := (if Monkey (ID).Operand.Self then Item else Monkey (ID).Operand.Value);

         if Monkey (ID).Operation = '+' then
            Item := (Item + Operand) rem LCM;
         else
            Item := (Item * Operand) rem LCM;
         end if;

         Monkey (Monkey (ID).Destination (Item rem Monkey (ID).Modulus = 0) ).Item.Append (New_Item => Item);
      end loop All_Items;
   end Apply_Round;

   type Max_Info is record
      ID    : Monkey_ID;
      Value : U64 := 0;
   end record;

   Input  : Ada.Text_IO.File_Type;
   Monkey : Monkey_List;
   Max_1  : Max_Info;
   Max_2  : Max_Info;
begin -- Day11_2
   Ada.Text_IO.Open (File => Input, Mode => Ada.Text_IO.In_File, Name => "input_11");

   Read_Monkeys : for ID in Monkey'Range loop
      exit Read_Monkeys when Ada.Text_IO.End_Of_File (Input);

      All_Lines : for L in 1 .. 7 loop
         exit Read_Monkeys when ID = Monkey'Last and L = 7;

         One_Line : declare
            Line : constant String := Ada.Text_IO.Get_Line (Input);
         begin -- One_Line
            case L is
            when 1 | 7 => -- Monkey # & final null line; ignore
               null;
            when 2 => -- Item list
               Parse_List : declare
                  Start : Positive := Ada.Strings.Fixed.Index (Line, ":") + 1;
                  Stop  : Natural;
               begin -- Parse_List
                  Parse_All : loop
                     exit Parse_All when Start not in Line'Range;

                     Stop := Ada.Strings.Fixed.Index (Line (Start .. Line'Last), ",");
                     Stop := (if Stop = 0 then Line'Last else Stop - 1);
                     Monkey (ID).Item.Append (New_Item => Natural'Value (Line (Start .. Stop) ) );
                     Start := Stop + 2;
                  end loop Parse_All;
               end Parse_List;
            when 3 => -- Operation & operand
               Parse_Op : declare
                  Pos : constant Positive := Ada.Strings.Fixed.Index (Line, "old") + 4; -- Operator position
               begin -- Parse_Op
                  Monkey (ID).Operation := Line (Pos);

                  if Line (Pos + 2) = 'o' then -- Operand is "old"
                     Monkey (ID).Operand := (Self => True);
                  else -- Operand is a number
                     Monkey (ID).Operand := (Self => False, Value => Natural'Value (Line (Pos + 2 .. Line'Last) ) );
                  end if;
               end Parse_Op;
            when 4 => -- Modulus
               Parse_Mod : declare
                  Pos : constant Positive := Ada.Strings.Fixed.Index (Line, " ", Going => Ada.Strings.Backward);
               begin -- Parse_Mod
                  Monkey (ID).Modulus := Natural'Value (Line (Pos .. Line'Last) );
               end Parse_Mod;
            when 5 .. 6 => -- Destinations
               Parse_Dest : declare
                  Pos : constant Positive := Ada.Strings.Fixed.Index (Line, " ", Going => Ada.Strings.Backward);
               begin -- Parse_Dest
                  Monkey (ID).Destination (L = 5) := Integer'Value (Line (Pos .. Line'Last) );
               end Parse_Dest;
            end case;
         end One_Line;
      end loop All_Lines;
   end loop Read_Monkeys;

   Ada.Text_IO.Close (File => Input);

   LCM := Monkey (Monkey'First).Modulus;

   Find_LCM : for ID in Monkey'First + 1 .. Monkey'Last loop
      LCM := PragmARC.Math.LCM (Monkey(ID).Modulus, LCM);
   end loop Find_LCM;

   All_Rounds : for R in 1 .. 10_000 loop
      All_Monkeys : for ID in Monkey'Range loop
         Apply_Round (ID => ID, Monkey => Monkey);
      end loop All_Monkeys;
   end loop All_Rounds;

   Find_1 : for ID in Monkey'Range loop
      if Monkey (ID).Inspections > Max_1.Value then
         Max_1 := (ID => ID, Value => Monkey (ID).Inspections);
      end if;
   end loop Find_1;

   Find_2 : for ID in Monkey'Range loop
      if ID /= Max_1.ID and Monkey (ID).Inspections > Max_2.Value then
         Max_2 := (ID => ID, Value => Monkey (ID).Inspections);
      end if;
   end loop Find_2;

   Ada.Text_IO.Put_Line (Item => Max_1.ID'Image & Max_1.Value'Image);
   Ada.Text_IO.Put_Line (Item => Max_2.ID'Image & Max_2.Value'Image);
   Ada.Text_IO.Put_Line (Item => U64'Image (Max_1.Value * Max_2.Value) );
end Day11_2;