2018: d04: ex2: add solution

This commit is contained in:
Bruno BELANYI 2024-12-29 15:54:22 -05:00
parent 74ebba0d4c
commit 038c4830dc

80
2018/d04/ex2/ex2.py Executable file
View file

@ -0,0 +1,80 @@
#!/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: guard_sleeps[guard].most_common()[0][1]
)
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()