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