It's once again time for Advent of Code. That one a day programming
competition that's been running now for three years.
Here are some thoughts on day 1. The core of the problem is that you
have a large string of digits and you have to calculate a
checksum. This is done by adding the sum of a subset of the
digits. Only the ones that are identical to the digit to their
right. To make it a little more interesting, the last digit wraps
around to the first for checksum purposes.
For example, the input 2234335 leads to the calculate 2 + 3
or
5
. The input 234445662 leads to 4+4+6+2
or 16
. We add 4 for
twice because the first four is adjacent the second and the second the
third. We add the 2 at the end because it wraps around to match the
one at the front.
We first read in the data and strip off the trailing newline
f = open("input.txt")
origdata = f.readline()
origdata = origdata.strip()
data = origdata
Then, since we can access the elements of the string as a list (or
array) it's a simple loop to calculate the sum:
s = 0
l = len(data)
for i in range(l-1):
if data[i]==data[i+1]:
s = s + int(data[i]) # Don't forget to turn the string into an int
# data[-1] is the python way of getting the last element - here we check the wraparound
if data[0] == data[-1]:
s = s + int(data[0])
print("Checksum: ",s)
Pretty straightforward but I don't like the special case of checking
the last element for the wraparound. Sometimes it's possible to get
rid of edge cases like this by changing the data. We can do that here
by simply appending a copy of the first character to the end of the
list.
This leads to a slightly cleaner solution:
data = data + data[0]
s = 0
l = len(data)
for i in range(l-1):
if data[i]==data[i+1]:
s = s + int(data[i]) #don't forget to turn the string into an int
print("Checksum: ",s)
This is pretty much what I'd expect from a Python programmer that's
just starting out. We can use a couple of more advanced Python
features to make what I consider a more elegant solution.
Python's zip function takes two lists and interleaves them.
zip("abc","def")
will yield [ (a,d), (b,e), (c,f)]
. If the lists
are of different length, it just zips up until the shorter list is
exhausted. We can use array slicing to zip the input string with it's
neighbor by using new_list = zip(data,data[1:])
. For the string "122344'
zipping gives us [(1,2),(2,2),(2,3),(3,4),(4,4)]
. We can put this in
a list comprehension that only keeps the tuples representing an
element with an identical neighbor and also converts it to an int:
new_list = [int(a) for a,b in new_list if a==b]
.
Finally, we can just calculate the sum. This leads to the following
complete solution:
f = open("input.txt")
data = f.readline().strip()
data = data + data[0]
checksum= sum([ int(a) for a,b in zip(data,data[1:]) if a==b])
print(checksum)
List comprehensions for the win!!!!
Each Advent of Code problem has two parts. You unlock the second by
solving the first. Here, the wrinkle is that instead of checking each
digit with it's neighbor to the right, you check it with the one
that's halfway around the list.
With loops, the solution is just a quick modification of part 1. We
just add half the length and use mod to find the digit to compare with:
f = open("input.txt")
data = f.readline().strip()
data = data + data[0]
s = 0
l = len(data)
for i in range(l-1):
if data[i]==data[(i+l//2)%l]: # check halfway around instead of adjacent
s = s + int(data[i])
print("part 2loop version: ",s)
I wanted to see if I could do this with a list comprehension
though. The trick was to figure out how to make two lists to zip
together to get the pairs to check then add. Here's the solution:
f = open("input.txt")
data = f.readline().strip()
l = len(data)
d2 = data[l//2:]+data
checksum = sum([ int(a) for a,b in zip(data,d2)if a==b])
print(checksum)
The insight was that we could just make a second list that starts
halfway through and then wraps around. I did this by adding
data[l//2:] + data
. l//2
is the integer division of the length (in
Python3). data[l//2:]
represents the second half of data (from the
midway point to the end). Technically I should have only added the second
half of data: data[l//2:] + data[:l//2]
where data[:l//2]
gives us
the first half of the list but since zip will just stop when it
exhausts the shorter list, this wasn't necessary.
Day 2 also has a nice list comprehension based solution. Maybe I'll
write that up later.