For the second day of the Advent of Code, the story problem was about password validation. The first part was to determine if a string fit a rule about the number of characters. Each line has the form:

<lower bound>-<upper bound> <character>: <string>

The rule is that a line was valid if the password string has from lower bound to upper bound instances of character in string. A naive implementation of this in Python might look something like this:

s = "aaaabcddefg"
counts = {}
for c in s:
  if c not in counts:
    counts[c] = 0
  counts[c] += 1
print(counts)
# -> {'a': 4, 'b': 1, 'c': 1, 'd': 2, 'e': 1, 'f': 1, 'g': 1}
print(counts["a"])
# -> 4

Fortunately, this is something that comes up a lot (as many of the AoC challenges do), and so there is a standard library class in Python called Counter that will do this for us:

from collections import Counter
s = "aaaabcddefg"
counts = Counter(s)
print(counts)
# -> Counter({'a': 4, 'd': 2, 'b': 1, 'c': 1, 'e': 1, 'f': 1, 'g': 1})
print(counts["a"])
# -> 4

Much cleaner! To actually perform the check, Python has nice syntax for expressing relationships like $$ a \le b \le c $$:

a = 1
b = 2
c = 3
print(a <= b <= c)
# -> True
print(a <= c <= b)
# -> False

Naturally, it goes the other way, too:

a = 1
b = 2
c = 3
print(c >= b >= a)
# -> True
print(a >= b >= c)
# -> False

This makes the check very simple:

password_valid = lower_bound <= counts[c] <= upper_bound

Everything else is just parsing the lines and keep a counter.

The second part was to index string at lower bound and upper bound and require that either one or the other of the indices was character, with the caveat that the indexing is one-based (presumably to make your life just a little more difficult). Strings in Python are arrays of characters:

s = "aaaabcddefg"
print(s[0])
# -> a

So the fundamental idea isn’t hard. The slightly tricky part is that this is an exclusive or (XOR) operation. XOR is true iff exactly one of the inputs is true. Here’s a truth table:

x y XOR
0 0 0
1 0 1
0 1 1
1 1 0

In Python, the XOR operator is ^, and it’s usually a bitwise operation, defined for numeric types like int. You can implement a boolean XOR like this:

cond1 != cond2

Where cond1 and cond2 are boolean expressions. For this problem, the expression would look like this (assuming the lower bound is lb, the upper bound is ub and the character is c):

password_valid = (password[lb - 1] == c) != (password[ub - 1] == c)

This is the tricky bit of part 2, everything else is just iterating over the lines and keeping a counter.