Friday, July 4, 2025

Advent of Code 2022 Day 1 part 2

 Like most AoC second-star problems, this one builds on the first star, and, actually, in this case, requires only minimal changes.

The second star, accessible only if you have completed the first, asks this time for the total consumed by the three hungriest elves—the champion you found for the first star, plus the second- and third-place elves.

That can be done with this very simple method, which, including comments and white space, is not even 10 lines:

private static int getSumOfTopNElements(List<Integer> elfCalories, int maxIndex) {
     int topNElfCals = 0;
     // given a sorted list, take the running sum of the elements up to maxIndex
    
for (int i = 0; i < maxIndex; i++) {
          topNElfCals += elfCalories.get(i);
     }
     // return that sum
    
return topNElfCals;
}

maxIndex in this case is always 3, and a good IDE like IntelliJ (even the Community Edition) will flag this. But I have a reason for building it like this, and not hardcoding the three.

Building the method like this, and allowing any index to be passed—even if we know that, for the elves’ purposes, that index will always be 3—allows the code to be much more portable, and called on by someone else in totally different circumstances who needs to find the total of the first few elements of a sorted list of Grades or Salaries or SquareFootages or what have you. This way, you do not care what the list is for—it could be elves and their diets, or anything else—nor do you care about which index you go to.

There’s another reason for writing like this over hardcoding a 3 or another number: if you hard-code it, you’ll have to remember to find and change every hard-coded instance, in order not to have inconsistencies, if you ever want more or less than the top 3. But if you pass in the 3 (or any other number) dynamically as we have done here, you make it so that the number can change freely without ever having to make those changes—or, by greatly diminishing the number of places where code would have to change if you wanted to modify the logic. It is a good idea to write as generically as possible, hardcoding as little as possible, because each time you hard-code something, you make your code a little more restrictive, harder to maintain, and harder to understand.

 Here's the whole implementation:

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);

          // find the sum of the top 3 best-performing elves
         
int top3Cals = getSumOfTopNElements(elfCalories, 3);

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

          // display the calorie count of the 3 most productive, summed together
         
System.out.println("The calories collected by the top 3 most productive elves: " + top3Cals);
          System.out.println("elfCalories.size() = " + elfCalories.size());
     }

     private static int getSumOfTopNElements(List<Integer> elfCalories, int maxIndex) {
          int topNElfCals = 0;
          // given a sorted list, take the running sum of the elements up to maxIndex
         
for (int i = 0; i < maxIndex; i++) {
               topNElfCals += elfCalories.get(i);
          }
          // return that sum
         
return topNElfCals;
     }

     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...