Beispiel #1
0
from utils import cmap, compose, invoker, unpack, at, reduceUntil
from functools import partial, reduce
from itertools import permutations, chain
from aoc.intcode import Runner

parse = compose(list, cmap(int), invoker('split', ','))


# runs Intcode several times by piping their input/output with [phase[index], previousOutput]
def runSequence(sequence, intcode, endCondition, startSignal=0):
    runners = [Runner(iter([phase])) for phase in sequence]
    runnerIterators = [runner.output_iterator(intcode) for runner in runners]

    # pipe last signal to current runner's input and add its next output signal to signals
    def step(signals, index):
        runners[index].feed(signals[-1])
        signal = next(runnerIterators[index], None)
        return signals + [signal], (index + 1) % len(runners)

    return reduceUntil(endCondition, unpack(step), ([startSignal], 0))[0]


oneLoop = unpack(lambda outputs, index: index == 0 and len(outputs) > 1)
noMoreValues = unpack(lambda outputs, index: outputs[-1] is None)
# -1 to get last in part A; -2 to get last in part B, since B stops on last = None (no more values)
one = compose(
    max, cmap(at(-1)), lambda intcode: [
        runSequence(sequence, intcode, oneLoop)
        for sequence in permutations(range(0, 5))
    ], parse)
two = compose(
Beispiel #2
0
from utils import cmap, compose, invoker, unpack, chunks
from functools import partial
import re

N = 25
M = 6
L = N * M

BLACK = '0'
WHITE = '1'
TRANSPARENT = '2'

parse = partial(chunks, L)

isOpaque = lambda value: value != TRANSPARENT
showImage = lambda layer: '\n'.join(row for row in (''.join(layer[i:i + N])
                                                    for i in range(0, L, N)))

# sub 0s for whitespaces and add a space between columns for readability
makeReadable = compose(partial(re.sub, r'0', ' '),
                       partial(re.sub, r'(0|1)', r'\g<1> '))

one = compose(lambda layer: layer.count('1') * layer.count('2'),
              partial(min, key=invoker('count', '0')), parse)
two = compose(makeReadable, showImage, list,
              cmap(compose(next, partial(filter, isOpaque))), unpack(zip),
              parse)
Beispiel #3
0
# use a custom generator to efficiently skip ranges of numbers that contain decreasing sequences
def generator(lower, upper):
    current = lower
    while current <= upper:
        index = next((i for i, n in enumerate(current[:-1])
                      if current[i] > current[i + 1]), None)
        if index is None:
            yield current
            current = str(int(current) + 1)
        else:
            current = current[:index +
                              1] + current[index] * len(current[index + 1:])


rulesA = [
    lambda password: re.findall(r'(\d)\1', password),
]

rulesB = [
    lambda password: any(
        len(match) == 2 for match, char in re.findall(r'((\d)\2+)', password)),
]

validator = lambda rules: lambda password: all(
    rule(password) for rule in rules)

one = compose(len, list, partial(filter, validator(rulesA)), unpack(generator),
              invoker('split', '-'))
two = compose(len, list, partial(filter, validator(rulesB)), unpack(generator),
              invoker('split', '-'))
Beispiel #4
0
from utils import cmap, compose, invoker, at, unpack, dictBuilder, switch, identity
from text import patternGroups, toLines
from functools import partial
from itertools import product, permutations
from space import Point
import re

CENTER = Point((0, 0))

# split input into lines, and each line (a wire) into a series of steps: [{'direction': 'U', 'length': 23}, {'direction' ....}]
formatStep = compose(dictBuilder({'direction': str, 'length': int}), partial(patternGroups, r'([URDL])(\d+)'))
formatWire = compose(cmap(formatStep), invoker('split', ','))
parseAll = compose(cmap(formatWire), toLines)

# transforms a wire (a list of steps) into a list of segments
def trace(wire):
    edges = [CENTER]
    for stretch in wire:
        edges.append(travel(stretch, edges[-1]))
    return list(zip(edges[:-1], edges[1:]))

# given a wire stretch (e.g. U41) and a starting Point, returns the destination point
def travel(stretch, origin):
    vector = switch(lambda direction, length: direction, {
        'U': lambda direction, length: Point((0, length)),
        'R': lambda direction, length: Point((length, 0)),
        'D': lambda direction, length: Point((0, -length)),
        'L': lambda direction, length: Point((-length, 0)),
    })
    return Point(origin) + vector(**stretch)
Beispiel #5
0
from utils import cmap, compose, invoker, identity
from text import toLines
from functools import partial
from collections import defaultdict

ROOT = 'COM'

parse = compose(list, cmap(invoker('split', ')')), toLines)


def buildOrbits(pairs):
    orbits = defaultdict(list)
    for static, orbiter in pairs:
        orbits[static].append(orbiter)
    return orbits


def countOrbits(source, orbits):
    def count(source, level=0):
        return level + sum(
            [count(orbiter, level + 1) for orbiter in orbits[source]])

    return count(source)


def path(target, orbits):
    def trace(current):
        orbiters = orbits[current[-1]]
        if len(orbiters) == 0:
            return current if current[-1] == target else None
        paths = (trace(current + [orbiter]) for orbiter in orbiters)