Part 1


In [5]:
initial = '.##..#.#..##..##..##...#####.#.....#..#..##.###.#.####......#.......#..###.#.#.##.#.#.###...##.###.#'

In [6]:
r = ! cat input.txt | tr '\n' ';'
r = dict(list(map(lambda x: tuple(x.split(' => ')), r[0].split(';')[:-1])))

In [31]:
def evolve(state, rules, time):
    s = state
    for t in range(1, time + 1):
        n = len(s)
        s = '....' + s + '....'
        new = []
        for i in range(n + 5):
            k = s[i: i + 5]
            new.append(rules.get(k, '.'))
        s = ''.join(new)
        yield list(filter(lambda x: s[x + 2 * t] == '#', range(-2 * t, len(state) + 2 * t)))

def sumofpots(state, rules, time):
    final = None
    for l in evolve(state, rules, time):
        final = l
    return sum(final)

Test


In [32]:
initial_test = '#..#.#..##......###...###'
rules_test = ! cat input_test.txt | tr '\n' ';'
rules_test = dict(list(map(lambda x: tuple(x.split(' => ')), rules_test[0].split(';')[:-1])))

In [33]:
sumofpots(initial_test, rules_test, 20)


Out[33]:
325

Solution


In [34]:
sumofpots(initial, r, 20)


Out[34]:
3738

Part 2

A pattern is a configuration of plants modulo shifting. Every pattern has the following signature: the sequence of numbers occupied by plants, with respect to the leftmost position which we take as 0.


In [43]:
forever = 50000000000

In [62]:
def pattern_repetition(state, rules, time):
    hashes = {}
    period = None
    for i, l in enumerate(evolve(state, rules, time)):
        sig = hash(tuple([c - l[0] for c in l]))
        if sig in hashes:
            period = i - hashes[sig]
            break
        hashes[hash(sig)] = i
    return i + 1, period, l

In [65]:
generation, period, l = pattern_repetition(initial, r, forever)
generation, period


Out[65]:
(100, 1)

In [66]:
len(l) * (forever - generation) + sum(l)


Out[66]:
3900000002467