Advent of code 2022 python walk through.

Day 1: Calorie Counting.

Joseph Amukun
5 min readJun 5, 2023
Top-down picture of berries, nuts, and another fruit, on a white background.
All composite images in this article were made using Photos by Mockup Graphics on Unsplash

Part One.

Read the full puzzle prose here: https://adventofcode.com/2022/day/1

The TL;DR of the sample puzzle input.

  • Our sample puzzle input represents the number of calories that each of the jungle-trotting elves has brought with them.
  • An empty line means the end of one elf’s caloric notes, and the start of another’s.
1000
2000
3000

4000

5000
6000

7000
8000
9000

10000

In the sample input above, 5 elves have noted down their calorie counts for various food items.

The task.

Our task is to find the elf that is carrying the most calories, and to find the total calories that that elf is carrying.

The thought process, with code.

  • Store that puzzle input as a data-structure of some sort.
    A python list is sufficient for this problem.
sample_input = """
1000
2000
3000

4000

5000
6000

7000
8000
9000

10000
""".strip().splitlines()

sample_input now represents a list of calories (stored as strings), along with the empty spaces(stored as empty strings) between each elf’s input.

['1000', '2000', '3000', '', '4000', '', '5000', '6000', '', '7000', '8000', '90
00', '', '10000']

.strip() exists solely to remove the first empty string that would’ve been present in the calories list. Without .strip(), the list would’ve looked like this:

# The list of calories, had .strip() not been applied.

['', '1000', '2000', '3000', '', '4000', '', '5000', '6000', '', '7000', '8000',
'9000', '', '10000']

.splitlines() is what does the heavy lifting of turning our sample puzzle input from the triple-quoted string into a list.

Had .splitlines() not been applied, the data-structure wouldn’t even be a list . We would’ve been dealing with a string instead…

# The string of calories, had just .strip() but not 
# .splitlines() been applied.

'1000\n2000\n3000\n\n4000\n\n5000\n6000\n\n7000\n8000\n9000\n\n10000'
  • Visit every element in the calories list, making sure to add the calorie we find to the current elf’s total.
  • Aside from this per-elf calorie total, let’s also create a global total, that represents the highest total calories of any elf we have seen so far. At the end of our program, this highest total will be our puzzle solution.
  • If, while adding calories, we encounter an empty string, we must take note of the current elf’s total and compare it with the highest total so far.
    If the current elf’s total is greater than our highest total, then we update the highest total. Otherwise, we do nothing.
highest_elf_total = 0
current_elf_total = 0

for calorie in sample_input:
if calorie == '': # denotes next elf's input
highest_elf_total = max(highest_elf_total, current_elf_total)
current_elf_total = 0 # reset before reading the next elf's calories
else:
current_elf_total += int(calorie)

print(highest_elf_total)

Of some note, is this line:

highest_elf_total = max(highest_elf_total, current_elf_total)

The highest_elf_total is whichever is bigger between the current_elf_total and whatever the highest_elf_total was before.

The above line is a more terse, and yet still readable, version of:

if current_elf_total > highest_elf_total:
highest_elf_total = current_elf_total

The final program looks like this:

sample_input = """
1000
2000
3000

4000

5000
6000

7000
8000
9000

10000
""".strip().splitlines()

highest_elf_total = 0
current_elf_total = 0

for calorie in sample_input:
if calorie == '': # denotes next elf's input
highest_elf_total = max(highest_elf_total, current_elf_total)
current_elf_total = 0 # reset before reading the next elf's calories
else:
current_elf_total += int(calorie)

print(highest_elf_total)

Though this program works for both our sample input, and the much longer puzzle input that you get from advent of code, there is a subtle bug in it.

You may have noticed that if our input only had one elf’s notes (if there are no empty lines), then highest_elf_total would be 0.
This is because the conditional on line 22 would remain False for the entirety of the program.

One way to amend this is to change line 28 such that we are printing not just highest_elf_total, but either current_elf_total or highest_elf_total, whichever is bigger.

We can use max like before, thereby transforming line 28 into:

print(max(highest_elf_total, current_elf_total))

Part Two.

A top-down picture of a mug filled with coffee

Recalling the sample puzzle input:

1000
2000
3000

4000

5000
6000

7000
8000
9000

10000

The new task.

Our new task is to find the top 3 elves (not just the top 1) that are carrying the most calories, and to find the total calories that those top 3 elves are carrying.

  • We will still store this input like we did previously, with sample_input representing this list:
['1000', '2000', '3000', '', '4000', '', '5000', '6000', '', '7000', '8000', '90
00', '', '10000']

The thought process, with code.

  • Visit every element in the calories list, making sure to add the calorie we find to the current elf’s total.
  • If, while adding calories, we encounter an empty string, we must take note of the current elf’s total, and store that total inside a list of totals. This totals list, named total_calories_per_elf, will hold all the totals of calories for every elf.
total_calories_per_elf = []
current_elf_total = 0

for calorie in sample_input:
if calorie == '': # denotes next elf's input
total_calories_per_elf.append(current_elf_total)
current_elf_total = 0 # reset before the next batch of calories
else:
current_elf_total += int(calorie)

Did you spot the subtle bug there?

There are still some calories missing.
Specifically, the last elf’s calories were not added to the total_calories_per_elf list, due to the nature of our data structure and how the data is stored in it.

Looking at the contents of sample_input again:

['1000', '2000', '3000', '', '4000', '', '5000', '6000', '', '7000', '8000', '90
00', '', '10000']

The current iteration of our code only adds calorie totals for the current elf, if we encounter an empty string, '' after the current elf’s last calorie.
The last 10000 does not have an empty string after it, and so it will not be added to the total_calories_per_elf list.
To fix this, we can — after safely assuming that there are still left-over calories stored in current_elf_total — add those left-over calories to total_calories_per_elf before going any further.

# add any left-over calories to `total_calories_per_elf`
total_calories_per_elf.append(current_elf_total)
  • Sort the totals list in ascending order. The first 3 elements of the sorted list are the top 3 elves that are carrying the most calories.

We will use the builtin sorted() method which, by default, sorts a list of integers from smallest to biggest. We will need to remember to reverse that default behavior, to get the ascending order we want.

# sort in ascending order
sorted_calories = sorted(total_calories_per_elf, reverse=True)

We can then extract the first three elements of this sorted list using python’s list slicing feature, recalling that the second list index in the slice is excluded from the new list that is returned.

top_three_calories = sorted_calories[0:3]
  • The sum of these top three calories is the solution to our task.
print(sum(top_three_calories))

Altogether, the final program looks like this:

sample_input = """
1000
2000
3000

4000

5000
6000

7000
8000
9000

10000
""".strip().splitlines()

total_calories_per_elf = []
current_elf_total = 0

for calorie in sample_input:
if calorie == '': # denotes next elf's input
total_calories_per_elf.append(current_elf_total)
current_elf_total = 0 # reset before the next batch of calories
else:
current_elf_total += int(calorie)

# add any left-over calories to `total_calories_per_elf`
total_calories_per_elf.append(current_elf_total)

# sort in ascending order
sorted_calories = sorted(total_calories_per_elf, reverse=True)
top_three_calories = sorted_calories[0:3]
print(sum(top_three_calories))

Congrats! That wraps up Day 1. 🎉🎉🎉

See the Day 2 walk through →

--

--

Joseph Amukun

Always sharing what I have learned and how I have learned it, most times, in excruciating detail.