79 lines
2.4 KiB
Python
Executable file
79 lines
2.4 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
import sys
|
|
from collections import Counter, defaultdict
|
|
from typing import NamedTuple
|
|
|
|
|
|
class DateTime(NamedTuple):
|
|
year: int
|
|
month: int
|
|
day: int
|
|
hour: int
|
|
min: int
|
|
|
|
|
|
class Log(NamedTuple):
|
|
datetime: DateTime
|
|
event: str
|
|
|
|
|
|
def solve(input: str) -> int:
|
|
def parse_datetime(input: str) -> DateTime:
|
|
date, time = input.split()
|
|
year, month, day = map(int, date.split("-"))
|
|
hour, min = map(int, time.split(":"))
|
|
return DateTime(year, month, day, hour, min)
|
|
|
|
def parse_log(input: str) -> Log:
|
|
date, event = input.split("] ")
|
|
return Log(parse_datetime(date.removeprefix("[")), event)
|
|
|
|
def parse(input: list[str]) -> list[Log]:
|
|
return [parse_log(line) for line in input]
|
|
|
|
def parse_guard_number(log: Log) -> int:
|
|
assert log.event.startswith("Guard #") # Sanity check
|
|
assert log.event.endswith(" begins shift") # Sanity check
|
|
return int(log.event.split()[1].removeprefix("#"))
|
|
|
|
def guards_events(logs: list[Log]) -> dict[int, list[Log]]:
|
|
res: dict[int, list[Log]] = defaultdict(list)
|
|
current_guard = parse_guard_number(logs[0])
|
|
for log in logs[1:]:
|
|
if log.event == "falls asleep" or log.event == "wakes up":
|
|
res[current_guard].append(log)
|
|
else:
|
|
current_guard = parse_guard_number(log)
|
|
return res
|
|
|
|
def sleep_counts(logs: list[Log]) -> Counter[int]:
|
|
res: Counter[int] = Counter()
|
|
assert len(logs) % 2 == 0 # Sanity check
|
|
for i in range(0, len(logs), 2):
|
|
assert logs[i].event == "falls asleep" # Sanity check
|
|
assert logs[i + 1].event == "wakes up" # Sanity check
|
|
start, end = logs[i].datetime, logs[i + 1].datetime
|
|
res.update(range(start.min, end.min))
|
|
return res
|
|
|
|
def guard_sleep_counts(
|
|
guard_logs: dict[int, list[Log]],
|
|
) -> dict[int, Counter[int]]:
|
|
return {guard: sleep_counts(logs) for guard, logs in guard_logs.items()}
|
|
|
|
logs = sorted(parse(input.splitlines()))
|
|
guard_logs = guards_events(logs)
|
|
guard_sleeps = guard_sleep_counts(guard_logs)
|
|
most_asleep = max(guard_sleeps, key=lambda guard: sum(guard_sleeps[guard].values()))
|
|
return most_asleep * guard_sleeps[most_asleep].most_common()[0][0]
|
|
|
|
|
|
def main() -> None:
|
|
input = sys.stdin.read()
|
|
print(solve(input))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|