Created
December 14, 2022 09:23
-
-
Save Dotrar/e5230e8c9696c04fbeb0db82e3c4cf35 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
from __future__ import annotations | |
import dataclasses | |
import itertools | |
import math | |
import os | |
import time | |
from functools import cmp_to_key | |
from typing import Callable | |
import aocd | |
import parse | |
AOC_DAY = 14 | |
test_data = """\ | |
498,4 -> 498,6 -> 496,6 | |
503,4 -> 502,4 -> 502,9 -> 494,9 | |
""" | |
def data_parse(dstr): | |
return dstr.splitlines() | |
test_data = data_parse(test_data) | |
assert len(test_data) == 2 | |
test_answer_one = 24 | |
test_answer_two = 93 | |
def intify_string_tuple(a): | |
ax, ay = a.split(",") | |
ax, ay = int(ax), int(ay) | |
return ax, ay | |
def draw_sand(walls, sand_pos, sand): | |
minx = min(x for x, y in walls) - 1 | |
maxx = max(x for x, y in walls) | |
maxy = min(max(y for x, y in walls), 40) | |
origin = (500 - minx, 0) | |
buffer = "Sand Sim\n" | |
for y in range(maxy + 1): | |
for x in range(maxx - minx): | |
c = " " | |
if (x, y) == origin: | |
c = "+" | |
elif (minx + x, y) in walls: | |
c = "#" | |
elif (minx + x, y) in sand_pos: | |
c = "o" | |
elif (minx + x, y) == sand: | |
c = "." | |
buffer += c | |
buffer += "\n" | |
buffer = "\b" * len(buffer) + buffer | |
print(buffer) | |
time.sleep(0.08) | |
def part_one(data: list[str]) -> int: | |
walls = set() | |
for line in data: | |
parts = line.split(" -> ") | |
for pair in itertools.pairwise(parts): | |
a, b = pair | |
(ax, ay) = intify_string_tuple(a) | |
(bx, by) = intify_string_tuple(b) | |
(ax, ay), (bx, by) = sorted([(ax, ay), (bx, by)]) | |
if ax == bx: | |
walls |= {(ax, y) for y in range(ay, by + 1)} | |
else: | |
walls |= {(x, ay) for x in range(ax, bx + 1)} | |
lowest_point = max([y for x, y in walls]) | |
# simulate sand | |
STARTING_POS = (500, 0) | |
sand = STARTING_POS | |
sand_pos = set() | |
n_sand = 1 | |
def get_next_position(sand): | |
x, y = sand | |
y += 1 | |
blockers = sand_pos | walls | |
# newpos = (x, y) | |
# c = 0 | |
# while newpos not in blockers: | |
# newpos = (x, newpos[1] + 1) | |
# c += 1 | |
# if c > 200: | |
# return x, y | |
# # newpos in blockers | |
# x, y = newpos | |
if (x, y) not in blockers: | |
return x, y | |
if (x - 1, y) in blockers: | |
if (x + 1, y) in blockers: | |
return x, y - 1 | |
return (x + 1, y) # right not in blockers | |
return (x - 1, y) # left not in blockers | |
while True: | |
# generate a sand | |
next_position = get_next_position(sand) | |
if next_position == sand: | |
# we've stopped moving. start again | |
sand_pos.add(sand) | |
n_sand += 1 | |
sand = STARTING_POS | |
elif sand[1] > lowest_point: | |
return n_sand - 1 | |
else: | |
sand = next_position | |
draw_sand(walls, sand_pos, sand) | |
return 1 | |
def part_two(data: list[str]) -> int: | |
walls = set() | |
for line in data: | |
parts = line.split(" -> ") | |
for pair in itertools.pairwise(parts): | |
a, b = pair | |
(ax, ay) = intify_string_tuple(a) | |
(bx, by) = intify_string_tuple(b) | |
(ax, ay), (bx, by) = sorted([(ax, ay), (bx, by)]) | |
if ax == bx: | |
walls |= {(ax, y) for y in range(ay, by + 1)} | |
else: | |
walls |= {(x, ay) for x in range(ax, bx + 1)} | |
lowest_point = max([y for x, y in walls]) | |
# simulate sand | |
STARTING_POS = (500, 0) | |
sand = STARTING_POS | |
sand_pos = set() | |
n_sand = 1 | |
def get_next_position(sand): | |
x, y = sand | |
y += 1 | |
blockers = sand_pos | walls | {(xx, lowest_point + 2) for xx in (x, x + 1, x - 1)} | |
newpos = (x, y) | |
while newpos not in blockers: | |
newpos = (x, newpos[1] + 1) | |
# newpos in blockers | |
x, y = newpos | |
if (x - 1, y) in blockers: | |
if (x + 1, y) in blockers: | |
return x, y - 1 # don't move | |
return (x + 1, y) # right not in blockers | |
return (x - 1, y) # left not in blockers | |
print() | |
print("0") | |
while True: | |
next_position = get_next_position(sand) | |
if next_position == sand: | |
if sand == STARTING_POS: | |
return n_sand | |
sand_pos.add(sand) | |
n_sand += 1 | |
sand = STARTING_POS | |
else: | |
sand = next_position | |
print("\b" * len(str(n_sand)) + str(n_sand), end="") | |
# draw_sand(walls, sand_pos, sand) | |
return 1 | |
if __name__ == "__main__": | |
part_one_ans = part_one(test_data) | |
assert part_one_ans == test_answer_one, f"{part_one_ans=}, not {test_answer_one=}" | |
real_data = data_parse(aocd.get_data(day=AOC_DAY, year=2022)) | |
print("part 1:", part_one(real_data)) | |
part_two_ans = part_two(test_data) | |
assert part_two_ans == test_answer_two, f"{part_two_ans=}, not {test_answer_two=}" | |
print("part 2:", part_two(real_data)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment