2024-12-14 10:57:36 +00:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- encoding: utf-8; py-indent-offset: 4 -*-
|
|
|
|
|
|
|
|
# Author: Sebastian Mark
|
|
|
|
# CC-BY-SA (https://creativecommons.org/licenses/by-sa/4.0/deed.de)
|
|
|
|
|
|
|
|
# pylint: disable=missing-module-docstring,missing-function-docstring,consider-using-f-string,no-else-return
|
|
|
|
|
|
|
|
import re
|
2024-12-14 14:32:32 +00:00
|
|
|
import matplotlib.pyplot as plt
|
2024-12-14 10:57:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
def readinput():
|
|
|
|
with open("input", "r", encoding="utf-8") as file:
|
|
|
|
robots = []
|
|
|
|
for line in file:
|
|
|
|
regex = r"p=(.?\d+),(.?\d+) v=(.?\d+),(.?\d+)"
|
|
|
|
m = re.findall(regex, line)[0]
|
|
|
|
robots.append(
|
|
|
|
{
|
|
|
|
"pos": (int(m[0]), int(m[1])), # x,y
|
|
|
|
"move": (int(m[2]), int(m[3])), # x,y
|
|
|
|
}
|
|
|
|
)
|
|
|
|
return robots
|
|
|
|
|
|
|
|
|
|
|
|
def get_robot_pos_after_time(area: dict, robot: tuple, seconds: int) -> tuple:
|
2024-12-14 14:32:32 +00:00
|
|
|
finx = (robot["pos"][0] + robot["move"][0] * seconds) % area["width"]
|
|
|
|
finy = (robot["pos"][1] + robot["move"][1] * seconds) % area["height"]
|
2024-12-14 10:57:36 +00:00
|
|
|
|
2024-12-14 14:32:32 +00:00
|
|
|
return (finx, finy)
|
2024-12-14 10:57:36 +00:00
|
|
|
|
|
|
|
|
2024-12-14 14:55:22 +00:00
|
|
|
def get_quadrant_for_pos(area: dict, position: tuple) -> int:
|
2024-12-14 10:57:36 +00:00
|
|
|
middle_x = area["width"] // 2
|
|
|
|
middle_y = area["height"] // 2
|
|
|
|
|
|
|
|
if position[0] == middle_x or position[1] == middle_y:
|
|
|
|
return 0
|
|
|
|
|
|
|
|
if position[1] < middle_y:
|
|
|
|
if position[0] < middle_x:
|
|
|
|
return 1
|
|
|
|
else:
|
|
|
|
return 2
|
|
|
|
else:
|
|
|
|
if position[0] < middle_x:
|
|
|
|
return 3
|
|
|
|
else:
|
|
|
|
return 4
|
|
|
|
|
|
|
|
|
2024-12-14 14:55:22 +00:00
|
|
|
def count_positions_in_quadrants(area: dict, positions: dict) -> list:
|
2024-12-14 10:57:36 +00:00
|
|
|
quadrant_count = {}
|
|
|
|
for pos, count in positions.items():
|
2024-12-14 14:55:22 +00:00
|
|
|
quadrant = get_quadrant_for_pos(area, pos)
|
2024-12-14 10:57:36 +00:00
|
|
|
if quadrant != 0:
|
|
|
|
quadrant_count[quadrant] = quadrant_count.get(quadrant, 0) + count
|
|
|
|
|
|
|
|
return quadrant_count
|
|
|
|
|
|
|
|
|
2024-12-14 14:55:22 +00:00
|
|
|
def draw_area(area: dict, positions: dict, filename: str):
|
2024-12-14 14:32:32 +00:00
|
|
|
grid = []
|
|
|
|
for _ in range(area["height"]):
|
|
|
|
row = [0] * area["width"]
|
|
|
|
grid.append(row)
|
|
|
|
|
|
|
|
for pos in positions.keys():
|
|
|
|
x, y = pos
|
|
|
|
grid[y][x] = 1
|
|
|
|
|
|
|
|
plt.figure()
|
2024-12-14 14:55:22 +00:00
|
|
|
plt.imshow(grid, cmap="gray")
|
2024-12-14 14:32:32 +00:00
|
|
|
plt.axis("off")
|
2024-12-14 14:55:22 +00:00
|
|
|
plt.savefig(f"{filename}.jpg", bbox_inches="tight")
|
2024-12-14 14:32:32 +00:00
|
|
|
plt.close()
|
|
|
|
|
|
|
|
|
2024-12-14 10:57:36 +00:00
|
|
|
def main():
|
|
|
|
robots = readinput()
|
|
|
|
|
|
|
|
area = {"width": 101, "height": 103}
|
|
|
|
|
|
|
|
# part 1
|
2024-12-14 14:32:32 +00:00
|
|
|
seconds = 100
|
2024-12-14 10:57:36 +00:00
|
|
|
positions = {}
|
|
|
|
for robot in robots:
|
|
|
|
pos = get_robot_pos_after_time(area, robot, seconds)
|
|
|
|
positions[pos] = positions.get(pos, 0) + 1
|
|
|
|
|
|
|
|
safety_factor = 1
|
2024-12-14 14:55:22 +00:00
|
|
|
quadrant_count = count_positions_in_quadrants(area, positions)
|
2024-12-14 10:57:36 +00:00
|
|
|
for c in quadrant_count.values():
|
|
|
|
safety_factor *= c
|
|
|
|
|
|
|
|
print("Safety factor: %d" % safety_factor)
|
|
|
|
|
2024-12-14 14:32:32 +00:00
|
|
|
# part 2
|
|
|
|
seconds = 10_000
|
|
|
|
for i in range(seconds):
|
|
|
|
positions = {}
|
|
|
|
for robot in robots:
|
|
|
|
pos = get_robot_pos_after_time(area, robot, i)
|
|
|
|
positions[pos] = positions.get(pos, 0) + 1
|
2024-12-14 14:55:22 +00:00
|
|
|
print(f"\rSaving image for positions after {i} seconds", end="", flush=True)
|
|
|
|
draw_area(area, positions, str("%05d" % i))
|
2024-12-14 14:32:32 +00:00
|
|
|
|
2024-12-14 10:57:36 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|