2022 Day 2: Rock Paper Scissors

SO MANY ENUMS

1 Like

Here is a HAC solution…

My Solution for Day 2:

Day_02.ads

with Ada.Containers.Vectors;

-- Top Level package
package Day_02 is 

   -- Chosen hand shape
   type Shape is (Rock, Paper, Scissors);

   -- How the game concluded
   type Outcome is (Loss, Draw, Win);

   -- Overall score is any natural value
   type Score is new Natural;

   -- Determines the outcome of the round
   function Round_Outcome(You, Opponent : Shape) return Outcome;

   -- Scores a round based on the shapes you and your opponent chose
   function Score_Round(You, Opponent : Shape) return Score;

   -- Scores a round based on the shape you chose and the round outcome
   function Score_Round
      (Shape   : Day_02.Shape; 
       Outcome : Day_02.Outcome) 
       return Score;

   -- Holds information about each round
   type Round is record
      Your_Shape      : Shape := Rock;
      Opponents_Shape : Shape := Rock;
      Total_Score     : Score := 0;
   end record;

   -- Using vectors for simplicity
   package Round_Vectors is new Ada.Containers.Vectors
      (Index_Type   => Positive,
       Element_Type => Round);

   -- List of rounds
   subtype Rounds is Round_Vectors.Vector;

   -- Parses the supplied file and returns a list of rounds
   function Parse_File(Filename : String) return Rounds;

   -- Takes in a rounds lists and adds them all together
   function Score_All_Rounds(Rounds : Day_02.Rounds) return Score;

end Day_02;

Day_02.adb

with Ada.Text_IO;
with Ada.Strings.Fixed;
with Ada.Exceptions;
with Ada.Command_Line;

package body Day_02 is 

   -- Enables checking Static_Predicate, Pre, Post, etc.
   pragma Assertion_Policy (Check);

   -- Score Constants
   Used_Rock     : constant := 1;
   Used_Paper    : constant := 2;
   Used_Scissors : constant := 3;
   Game_Lost     : constant := 0;
   Game_Tied     : constant := 3;
   Game_Won      : constant := 6;

   -- Individual score for each shape used in the game
   subtype Shape_Score is Score range Used_Rock..Used_Scissors;

   -- Individual score for each game
   subtype Outcome_Score is Score
      with Static_Predicate => Outcome_Score in Game_Lost | Game_Tied | Game_Won;

   -- Returns the assigned score based on the shape
   function Get_Score(Shape : Day_02.Shape) return Shape_Score 
      with Inline;

   -- Returns the assigned score based on how the game concluded
   function Get_Score(Outcome : Day_02.Outcome) return Outcome_Score 
      with Inline;

   -- Array used to convert a shape to its corresponding score
   To_Shape_Score : constant array(Day_02.Shape) of Shape_Score :=
      [Rock     => Used_Rock,
       Paper    => Used_Paper,
       Scissors => Used_Scissors];

   -- Array used to convert an outcome to its corresponding score
   To_Outcome_Score : constant array(Day_02.Outcome) of Outcome_Score :=
      [Loss => Game_Lost,
       Draw => Game_Tied,
       Win  => Game_Won];

   function Get_Score(Shape : Day_02.Shape) return Shape_Score is
      (To_Shape_Score(Shape));

   function Get_Score(Outcome : Day_02.Outcome) return Outcome_Score is
      (To_Outcome_Score(Outcome));

   -- Maps <your_shape>,<opponents_shape> to an outcome
   To_Outcome : constant array (Shape,Shape) of Outcome :=
      [Rock =>
         [Rock     => Draw,
          Paper    => Loss,
          Scissors => Win],
       Paper =>
         [Rock     => Win,
          Paper    => Draw,
          Scissors => Loss],
      Scissors =>
         [Rock     => Loss,
          Paper    => Win,
          Scissors => Draw]];

   function Round_Outcome(You, Opponent : Shape) return Outcome is
      (To_Outcome(You,Opponent));

   function Score_Round(You, Opponent : Shape) return Score is
      ( Score_Round
         (Shape   => You,
          Outcome => Round_Outcome
                        (You      => You,
                         Opponent => Opponent)));

   function Score_Round
      (Shape   : Day_02.Shape; 
       Outcome : Day_02.Outcome) 
       return Score
   is (To_Shape_Score(Shape) + To_Outcome_Score(Outcome));

   -- Encryption of round info
   subtype Opponents_Character is Character range 'A'..'C';
   subtype Delimiter_Character is Character range ' '..' ';
   subtype Your_Character      is Character range 'X'..'Z';

   -- Decrypts opponent shape info
   To_Opponents_Shape : constant array(Opponents_Character) of Shape :=
      ['A' => Rock,
       'B' => Paper,
       'C' => Scissors];

   -- Decrypts your shape info
   To_Your_Shape     : constant array(Your_Character) of Shape :=
      ['X' => Rock,
       'Y' => Paper,
       'Z' => Scissors];

   function Parse_File(Filename : String) return Rounds is
      File            : Ada.Text_IO.File_Type;
      Round           : Day_02.Round;
      Result          : Rounds;
      Opponent        : Opponents_Character;
      Dummy_Delimiter : Delimiter_Character; -- Named dummy to avoid GNAT warning
      You             : Your_Character;
   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);
         begin
            -- Only look at lines that could have the correct format.
            -- Ignore empty lines
            if Line'Length = 3 then
               
               Opponent        := Line(Line'First + 0);
               Dummy_Delimiter := Line(Line'First + 1);
               You             := Line(Line'First + 2);

               Round.Your_Shape      := To_Your_Shape(You);
               Round.Opponents_Shape := To_Opponents_Shape(Opponent);
               Round.Total_Score     := Score_Round
                                          (You      => Round.Your_Shape,
                                           Opponent => Round.Opponents_Shape);
               Result.Append(Round);

            -- Lines not exactly 3 characters or not empty are ill formatted
            elsif Line'Length /= 0 then
               raise Ada.Text_IO.Data_Error with "Line longer than expected: " & Line;
            end if;
         end;
      end loop;

      Ada.Text_IO.Close(File);

      return Result;

   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));
         if Ada.Text_IO.Is_Open(File) then
            Ada.Text_IO.Close(File);
         end if;
         return Round_Vectors.Empty_Vector;

   end Parse_File;
   
   function Score_All_Rounds(Rounds : Day_02.Rounds) return Score is
      Result : Score := 0;
   begin
      for Round of Rounds loop
         Result := @ + Round.Total_Score;
      end loop;
      return Result;
   end Score_All_Rounds;

   -- Program variables
   Filename : constant String :=
      (if Ada.Command_Line.Argument_Count = 0 then 
         "input.txt"
       else 
         Ada.Command_Line.Argument(1));
   Round_List   : constant Rounds                    := Parse_File(Filename);
   Total_Score  : constant Score                     := Score_All_Rounds(Round_List);
   Total_Rounds : constant Ada.Containers.Count_Type := Round_List.Length;

   use type Ada.Containers.Count_Type;
begin
   if Total_Rounds = 0 then
      Ada.Text_IO.Put_Line("No rounds in file");
   else
      Ada.Text_IO.Put_Line("Total # of rounds:" & Total_Rounds'Image);
      Ada.Text_IO.Put_Line("Total Score:" & Total_Score'Image);
   end if;
end Day_02;

I just withed Day_02 in a blank main file.

Indeed enums were very helpful. They did all the work for me!

4 Likes

Yep! I like pairing enums with arrays indexed by those enums to make it do all the logic work too, which is nice.

Wow, that is nice! This is very short solution!

1 Like

My solution here and on github. Now I’ll read other’s solutions :slight_smile:

1 Like

I ended up using enums as well, with a multidimensional array as a lookup table (kind of). I love the solutions with 'Val, I keep forgetting that I can do that!
Here’s my solution: adventofcode/day2.adb at 9683a101a7532e2deaf632354091dae421287dce · AJ-Ianozi/adventofcode · GitHub

1 Like