Friday, July 4, 2025

Advent of Code 2022 Day 1 part 1

 This time, we’re going to look at the first submission—for which I only got one star at the time, not both—from the first day of the 2022 contest. (Again, you can do these puzzles whenever you want once they’ve been made public.)


In 2022, the problem was quite straightforward:

  • There are a lot of elves, and those elves eat meals and ingest calories
  • We know how many calories are in each food eaten by each elf
  • We have a way to delimit one elf from another in the input
  • We want to know how many calories the elf who ate the most consumed across all meals eaten by that elf

As with the previous Advent of Code articles, the best place to start is always with the sample they give you, which on that day was:

1000
2000
3000

4000

5000
6000

7000
8000
9000

10000


The rules of the problem dictate that this means there are 5 elves:

a.       One ate 3 meals of 1000, 2000, and 3000 calories

b.       One ate 1 meal of 4000 calories

c.       One ate 2 meals of 5000 and 6000 calories

d.       One ate 3 meals of 7000, 8000, and 9000 calories

e.       One ate 1 meal of 10000 calories

At this small scale, the problem is easy to solve, even relying on mental math alone: Which elf ate the most? Naturally, the elf who ate 7000+8000+9000=24000 calories (since the others ate 6000, 4000, 11000, 10000)

The first order of business, like in every AoC puzzle, is to read the input file:

This particular year, unlike the more recent args[0] solution, I actually passed in the path directly, without going through args, like this:

BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\andre\\IdeaProjects\\AdventOfCode\\elf-input.txt"));

Passing the path like this, or passing the path in through args, as long as both are done correctly, are equivalent methods

The key method is the following:

    private static List<Integer> calculateCalories(BufferedReader reader) throws IOException {

          String line;

          List<Integer> elfCalories = new ArrayList<>();

          int totalCalories = 0;

 

          // while there are still lines of calories

          while ((line = reader.readLine()) != null) {

               int calories;

               //When you hit a blank line, a given elf is done collecting calories

               // add its total to the list and zero out the total to get ready for the next elf

               if (line.equals("")) {

                    elfCalories.add(totalCalories);

                    totalCalories = 0;

               }

               // but if the same elf is still collecting calories, then the elf's running total of calories

               // is how many calories they've collected up until now, plus the calories they just collected on this line

               else {

                    calories = Integer.parseInt(line);

                    totalCalories += calories;

               }

          }

          // return a list of calorie counts where 1 number = the production of 1 elf

          return elfCalories;

     }

The key to solving the problem is making explicit what we know is obvious as human beings—that a blank line separates elf A from elf B from elf C, and so on. That is, when you see a blank line, stop attributing those calories to the current elf, and instead create a new elf to start assigning them to.

Then, since we need the top elf, we need the list to be sorted so that the top elf’s calorie count is somewhere predictable.

So it's perfectly reasonable for      private static void prepareList(List<Integer> elfCalories) {} to include the following line:

          Collections.sort(elfCalories);

But there’s actually a hidden problem with that—the best-performing elf is now last, not first, on account of the default sorting order built into Collections.sort(). However, this problem is easily fixed by using another method in Collections—namely, Collections.reverse(). Calling sort() and then calling reverse() gets us the list sorted in an order such that the hungriest elf is first.
 
The sample has 5 distinct elves; the real problem file has 250. As with other AoC problems, it’s possible to solve this elf problem in any language, or none at all—but I would strongly caution against a purely manual solution simply on account of how long it would take.

All together, the solution looks like:


import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ElfCalorieCounter {

     public static void main(String[] args) throws IOException {

          // create a reader object to read through the input file
         
BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\andre\\IdeaProjects\\JavaPractice\\src\\main\\java\\elfCalories.txt"));

          // calculate all the calorie counts of the elves
         
List<Integer> elfCalories = calculateCalories(reader);
          reader.close();


          // sort, then reverse the list
          prepareList
(elfCalories);


          //Display the calorie counts of all elves
         
System.out.println("Calories per elf: " + elfCalories);
     }

     private static void prepareList(List<Integer> elfCalories) {
          Collections.sort(elfCalories);
          Collections.reverse(elfCalories);
     }

     /**
      * @param reader BufferedReader object reads file line by line
      * @return a list containing the calorie counts of various elves
      */
    
private static List<Integer> calculateCalories(BufferedReader reader) throws IOException {
          String line;
          List<Integer> elfCalories = new ArrayList<>();
          int totalCalories = 0;

          // while there are still lines of calories
         
while ((line = reader.readLine()) != null) {
               int calories;
               //When you hit a blank line, a given elf is done collecting calories
               // add its total to the list and zero out the total to get ready for the next elf
              
if (line.isEmpty()) {
                    elfCalories.add(totalCalories);
                    totalCalories = 0;
               }
               // but if the same elf is still collecting calories, then the elf's running total of calories
               // is how many calories they've collected up until now, plus the calories they just collected on this line
              
else {
                    calories = Integer.parseInt(line);
                    totalCalories += calories;
               }
          }
          // return a list of calorie counts where 1 number = the production of 1 elf
         
return elfCalories;
     }
}

 

 

No comments:

Post a Comment

Switch

 Other than if/if-else/if-else if-else and the ternary operator, there is yet another common and important conditional expression in Java th...