Skip to content

Instantly share code, notes, and snippets.

@Dotrar
Created December 14, 2022 09:23
Show Gist options
  • Save Dotrar/e5230e8c9696c04fbeb0db82e3c4cf35 to your computer and use it in GitHub Desktop.
Save Dotrar/e5230e8c9696c04fbeb0db82e3c4cf35 to your computer and use it in GitHub Desktop.
#!/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