Packet Scanners

Part 1


In [120]:
import csv
from collections import defaultdict

def parse_scanners(input_file):
    scanners = defaultdict(int)
    with open(input_file, 'rt') as f_input:
        csv_reader = csv.reader(f_input, delimiter=' ')
        for l in csv_reader:
            scanners[int(l[0].rstrip(':'))] = int(l[1].rstrip())
    return scanners

In [175]:
def tick(lrank, time):
    r = time % (2 * (lrank - 1))
    return  (r <= lrank - 1) * r + (r > lrank - 1) * (2 * (lrank - 1) - r)


def get_state(time, scanners):    
    state = dict(zip(list(scanners.keys()), [0] * len(scanners)))
    if time == 0:
        return state
    elif time > 0:
        for t in range(time + 1):
            for scanner in scanners:
                state[scanner] = tick(scanners[scanner], t)
    return state

Some nice time-lapse plotting :)


In [221]:
from time import sleep

def print_state(time, scanners):
    stt = get_state(time + 1, scanners)
    depths = range(max(list(scanners.keys())) + 1)
    depths_printable = '\t'.join(map(str, depths))
    print(depths_printable)
    max_range = max(list(scanners.values()))
    printable = '\t'.join(['(S)' if ((scanners[j] >= 1) and (stt[j] == 0) and (time == j)) \
                           else '[S]' if ((scanners[j] >= 1) and (stt[j] == 0) and (time != j)) \
                           else '( )' if ((scanners[j] >= 1) and (stt[j] != 0) and (time == j)) \
                           else '[ ]' if ((scanners[j] >= 1) and (stt[j] != 0) and (time != j)) \
                           else '(.)' if (time == j) \
                           else '...' for j in depths])
    print(printable)
    for i in range(2, max_range + 1):
        printable = '\t'.join(['[S]' if ((scanners[j] >= i) and (stt[j] == i-1)) \
                               else '[ ]' if ((scanners[j] >= i) and (stt[j] != i-1)) \
                               else ' ' for j in depths])
        print(printable)

def timelapse(input_path): 
    scanners = parse_scanners(input_path)
    for t in range(max(list(scanners.keys())) + 10):
        if t > 0: sleep(1)
        print('After Picosecond {}:'.format(t))
        print_state(t, scanners)

Test


In [222]:
timelapse('input.test1.txt')


After Picosecond 0:
0	1	2	3	4	5	6
( )	[ ]	...	...	[ ]	...	[ ]
[S]	[S]	 	 	[S]	 	[S]
[ ]	 	 	 	[ ]	 	[ ]
 	 	 	 	[ ]	 	[ ]
After Picosecond 1:
0	1	2	3	4	5	6
[ ]	(S)	...	...	[ ]	...	[ ]
[ ]	[ ]	 	 	[ ]	 	[ ]
[S]	 	 	 	[S]	 	[S]
 	 	 	 	[ ]	 	[ ]
After Picosecond 2:
0	1	2	3	4	5	6
[ ]	[ ]	(.)	...	[ ]	...	[ ]
[S]	[S]	 	 	[ ]	 	[ ]
[ ]	 	 	 	[ ]	 	[ ]
 	 	 	 	[S]	 	[S]
After Picosecond 3:
0	1	2	3	4	5	6
[S]	[S]	...	(.)	[ ]	...	[ ]
[ ]	[ ]	 	 	[ ]	 	[ ]
[ ]	 	 	 	[S]	 	[S]
 	 	 	 	[ ]	 	[ ]
After Picosecond 4:
0	1	2	3	4	5	6
[ ]	[ ]	...	...	( )	...	[ ]
[S]	[S]	 	 	[S]	 	[S]
[ ]	 	 	 	[ ]	 	[ ]
 	 	 	 	[ ]	 	[ ]
After Picosecond 5:
0	1	2	3	4	5	6
[ ]	[S]	...	...	[S]	(.)	[S]
[ ]	[ ]	 	 	[ ]	 	[ ]
[S]	 	 	 	[ ]	 	[ ]
 	 	 	 	[ ]	 	[ ]
After Picosecond 6:
0	1	2	3	4	5	6
[ ]	[ ]	...	...	[ ]	...	( )
[S]	[S]	 	 	[S]	 	[S]
[ ]	 	 	 	[ ]	 	[ ]
 	 	 	 	[ ]	 	[ ]
After Picosecond 7:
0	1	2	3	4	5	6
[S]	[S]	...	...	[ ]	...	[ ]
[ ]	[ ]	 	 	[ ]	 	[ ]
[ ]	 	 	 	[S]	 	[S]
 	 	 	 	[ ]	 	[ ]
After Picosecond 8:
0	1	2	3	4	5	6
[ ]	[ ]	...	...	[ ]	...	[ ]
[S]	[S]	 	 	[ ]	 	[ ]
[ ]	 	 	 	[ ]	 	[ ]
 	 	 	 	[S]	 	[S]
After Picosecond 9:
0	1	2	3	4	5	6
[ ]	[S]	...	...	[ ]	...	[ ]
[ ]	[ ]	 	 	[ ]	 	[ ]
[S]	 	 	 	[S]	 	[S]
 	 	 	 	[ ]	 	[ ]
After Picosecond 10:
0	1	2	3	4	5	6
[ ]	[ ]	...	...	[ ]	...	[ ]
[S]	[S]	 	 	[S]	 	[S]
[ ]	 	 	 	[ ]	 	[ ]
 	 	 	 	[ ]	 	[ ]
After Picosecond 11:
0	1	2	3	4	5	6
[S]	[S]	...	...	[S]	...	[S]
[ ]	[ ]	 	 	[ ]	 	[ ]
[ ]	 	 	 	[ ]	 	[ ]
 	 	 	 	[ ]	 	[ ]
After Picosecond 12:
0	1	2	3	4	5	6
[ ]	[ ]	...	...	[ ]	...	[ ]
[S]	[S]	 	 	[S]	 	[S]
[ ]	 	 	 	[ ]	 	[ ]
 	 	 	 	[ ]	 	[ ]
After Picosecond 13:
0	1	2	3	4	5	6
[ ]	[S]	...	...	[ ]	...	[ ]
[ ]	[ ]	 	 	[ ]	 	[ ]
[S]	 	 	 	[S]	 	[S]
 	 	 	 	[ ]	 	[ ]
After Picosecond 14:
0	1	2	3	4	5	6
[ ]	[ ]	...	...	[ ]	...	[ ]
[S]	[S]	 	 	[ ]	 	[ ]
[ ]	 	 	 	[ ]	 	[ ]
 	 	 	 	[S]	 	[S]
After Picosecond 15:
0	1	2	3	4	5	6
[S]	[S]	...	...	[ ]	...	[ ]
[ ]	[ ]	 	 	[ ]	 	[ ]
[ ]	 	 	 	[S]	 	[S]
 	 	 	 	[ ]	 	[ ]

Trip severity calculator:


In [187]:
def trip_severity(input_path):
    severity = 0
    scanners = parse_scanners(input_path)
    layers = max(list(scanners.keys()))
    for t in range(layers + 1):
        if scanners[t] != 0:
            tick_before = tick(scanners[t], t)
            tick_now = tick(scanners[t], t + 1)
            if (tick_before == 0):
                severity += scanners[t] * t
    return severity

Test


In [188]:
assert(trip_severity('input.test1.txt') == 24)

Solution


In [189]:
trip_severity('input.txt')


Out[189]:
1876

Part 2A: Simple solution based on the trip severity calculator


In [303]:
def trip_caught_delayed(delay):
    severity = 0
    caught = False
    layers = max(list(scanners.keys()))
    for t in range(0, layers + 1):
        if scanners[t] != 0:
            tick_before = tick(scanners[t], t + delay)
            if (tick_before == 0):
                caught = True
                severity += scanners[t] * t
    return caught

In [304]:
def minimum_delay():
    delay = 0
    while trip_caught_delayed(delay):
        delay += 1
    return delay

Test


In [308]:
%%time
scanners = parse_scanners('input.test1.txt')
trip_caught_delayed(10)


CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 437 µs

In [309]:
%%time
minimum_delay()


CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 359 µs
Out[309]:
10

Solution


In [311]:
%%time
scanners = parse_scanners('input.txt')
print(minimum_delay())


3964778
CPU times: user 2min 6s, sys: 0 ns, total: 2min 6s
Wall time: 2min 6s

Part 2B: A little bit more self contained and efficient :)


In [349]:
def alt_minimum_delay():
    delay = 0
    caught = True
    layers = list(scanners.keys())
    while (delay >= 0) and caught:
        caught = False
        for j in layers:
            if (delay + 1 + j) % (2 * scanners[j] - 2) == 0:
                caught = True
        delay += 1
    return delay

Test


In [352]:
%%time
scanners = parse_scanners('input.test1.txt')
print(alt_minimum_delay())


10
CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 1.9 ms

Solution


In [354]:
%%time
scanners = parse_scanners('input.txt')
print(alt_minimum_delay())


3964778
CPU times: user 40.6 s, sys: 0 ns, total: 40.6 s
Wall time: 40.6 s