Sean McLemon | Advent of Code

Home | Czech | Blog | GitHub | Advent Of Code | Notes


2018-12-04 - Repose Record

(original .ipynb)
import re
from collections import defaultdict
from datetime import datetime
from enum import Enum

puzzle_input_lines = open("puzzle_input/day4.txt").readlines()

test_input_lines = """[1518-11-01 00:00] Guard #10 begins shift
[1518-11-01 00:05] falls asleep
[1518-11-01 00:25] wakes up
[1518-11-01 00:30] falls asleep
[1518-11-01 00:55] wakes up
[1518-11-01 23:58] Guard #99 begins shift
[1518-11-02 00:40] falls asleep
[1518-11-02 00:50] wakes up
[1518-11-03 00:05] Guard #10 begins shift
[1518-11-03 00:24] falls asleep
[1518-11-03 00:29] wakes up
[1518-11-04 00:02] Guard #99 begins shift
[1518-11-04 00:36] falls asleep
[1518-11-04 00:46] wakes up
[1518-11-05 00:03] Guard #99 begins shift
[1518-11-05 00:45] falls asleep
[1518-11-05 00:55] wakes up""".split("\n")

# patterns because we are gonna abuse the 're' module
raw_input_line = "^\[(....)-(..)-(..) (..):(..)\]\s+(.+)"
begins_shift = "^Guard #(\d+) begins shift$"
falls_asleep = "^falls asleep$"
wakes_up = "^wakes up$"


class GuardEvent(Enum):
    WAKES_UP = 0
    FALLS_ASLEEP = 1
    BEGINS_SHIFT = 2


def parse_line(line, guard):
    parsed = re.search(raw_input_line, line)
    assert(len(parsed.groups()) == 6)
    year = int(parsed.group(1))
    month = int(parsed.group(2))
    day = int(parsed.group(3))
    hour = int(parsed.group(4))
    minute = int(parsed.group(5))

    event_timestamp = datetime(year, month, day, hour, minute)
    event = parsed.group(6)
    
    begin_shift_match  = re.search(begins_shift, event)
    falls_asleep_match = re.search(falls_asleep, event)
    wakes_up_match     = re.search(wakes_up,     event)
    
    if begin_shift_match:
        assert(len(begin_shift_match.groups()) == 1)
        return (event_timestamp, GuardEvent.BEGINS_SHIFT, int(begin_shift_match.group(1)))
    
    elif falls_asleep_match:
        return (event_timestamp, GuardEvent.FALLS_ASLEEP, guard)
    
    elif wakes_up_match:
        return (event_timestamp, GuardEvent.WAKES_UP, guard)

    raise Exception("unknown event")
    

def log_sleep(guard_sleep_mins, guard, from_time, to_time):
    time_slept = from_time - to_time
    current_minute = from_time.minute
    
    while current_minute < to_time.minute:
        guard_sleep_mins[guard][current_minute] += 1
        current_minute += 1

def sleepiest_guard(guard_sleep_mins):
    max_sleep_guard = None
    max_sleep_mins = 0
    
    for guard in guard_sleep_mins:
        sleep_mins = sum(guard_sleep_mins[guard])
        if sleep_mins > max_sleep_mins:
            max_sleep_mins = sleep_mins
            max_sleep_guard = guard
    
    return max_sleep_guard


def guard_sleepiest_minute(sleep_mins):
    sleepiest_minute = -1
    sleepiest_minute_times = 0
    
    for minute, times_asleep in enumerate(sleep_mins):
        if times_asleep > sleepiest_minute_times:
            sleepiest_minute = minute
            sleepiest_minute_times = times_asleep
    
    return sleepiest_minute
    

def build_sleepy_lookup(input_lines):
    asleep = False
    current_guard = None
    current_timestamp = None
    guard_sleep_mins = defaultdict(lambda: [0] * 60)
    
    for line in sorted(input_lines):
        event_timestamp, event, event_guard = parse_line(line, current_guard)
        #print(event_guard)
        
        if event == GuardEvent.BEGINS_SHIFT:
            if asleep:
                log_sleep(guard_sleep_mins, current_guard, current_timestamp, event_timestamp)
        
        elif event == GuardEvent.FALLS_ASLEEP:
            asleep = True
        
        elif event == GuardEvent.WAKES_UP:
            asleep = False
            log_sleep(guard_sleep_mins, current_guard, current_timestamp, event_timestamp)
        
        current_guard = event_guard
        current_timestamp = event_timestamp
        
    return guard_sleep_mins


def part_one(input_lines):
    guard_sleep_mins = build_sleepy_lookup(input_lines)
    guard = sleepiest_guard(guard_sleep_mins)
    sleepiest_minute = guard_sleepiest_minute(guard_sleep_mins[guard])
    
    return guard * sleepiest_minute


assert 240 == part_one(test_input_lines)

print("part one:", part_one(puzzle_input_lines))
part one: 35623
def part_two(input_lines):
    guard_sleep_mins = build_sleepy_db(input_lines)

    max_times = 0
    max_minute = -1 
    max_guard = None

    for guard in guard_sleep_mins:
        for minute, time_asleep in enumerate(guard_sleep_mins[guard]):
            if time_asleep > max_times:
                max_minute = minute
                max_times = time_asleep
                max_guard = guard
            
    return max_guard * max_minute
    
print("part two:", part_two(puzzle_input_lines))
part two: 23037