diff --git a/README.md b/README.md index d9df0ce..948a238 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ - Day 12: 52:00 and 22:00 for leaderboard; had the right idea and I am good at this type of problem - ... - Day 16: 00:27:30 745; best placement so far, of course still horribly slow -- Day 17: a couple of hours... realized that I need A* after a while; reused +- Day 17: a couple of hours; I realized that I need A* after a while; reused implementation from Project Euler but improved with heapq which was super fun - +- Day 18: a couple of hours; I realized that I need shoelace algo for part two + but didn't realize that I have to compute the outer edges for a while and + after I did, I still got clockwise/counter-clockwise issues. They could have + made it meaner by using different clock directions for example and input. diff --git a/d18.py b/d18.py new file mode 100644 index 0000000..cf8b52c --- /dev/null +++ b/d18.py @@ -0,0 +1,129 @@ +from lib import * + +EXAMPLE = """ +R 6 (#70c710) +D 5 (#0dc571) +L 2 (#5713f0) +D 2 (#d2c081) +R 2 (#59c680) +D 2 (#411b91) +L 5 (#8ceee2) +U 2 (#caa173) +L 1 (#1b58a2) +U 2 (#caa171) +R 2 (#7807d2) +U 3 (#a77fa3) +L 2 (#015232) +U 2 (#7a21e3) +""" + +m = { + "R": Grid2D.E, + "D": Grid2D.S, + "L": Grid2D.W, + "U": Grid2D.N, +} + + +def solve(i: Input, second=False): + lines = i.lines() + ins = [] + for l in lines: + if not l: + continue + d, l, c = l.split() + ins.append((int(l), m[d])) + + c = (0, 0) + coords: list[tuple[int, int]] = [c] + for count, d in ins: + for _ in range(count): + c = add2(c, d) + coords.append(c) + + to_visit: list[tuple[int, int]] = [(1, 1)] + while to_visit: + c = to_visit.pop() + for n in [Grid2D.S, Grid2D.E, Grid2D.W, Grid2D.N]: + nc = add2(c, n) + if nc not in coords: + coords.append(nc) + to_visit.append(nc) + return len(set(coords)) + + +def calc(ins): + c = (0, 0) + corners = [] + for i, (count, d) in enumerate(ins): + c = (c[0] + count * d[0], c[1] + count * d[1]) + nd = ins[(i + 1) % len(ins)][1] + + if d == nd: + raise Exception("Dirs should not be equal!") + + # XXX: This mapping only works when going clockwise! + match d, nd: + case Grid2D.W, Grid2D.N: + c_log = (c[0] + 1, c[1]) + case Grid2D.W, Grid2D.S: + c_log = (c[0] + 1, c[1] + 1) + case Grid2D.E, Grid2D.N: + c_log = (c[0], c[1]) + case Grid2D.E, Grid2D.S: + c_log = (c[0], c[1] + 1) + case Grid2D.N, Grid2D.E: + c_log = (c[0], c[1]) + case Grid2D.N, Grid2D.W: + c_log = (c[0] + 1, c[1]) + case Grid2D.S, Grid2D.E: + c_log = (c[0], c[1] + 1) + case Grid2D.S, Grid2D.W: + c_log = (c[0] + 1, c[1] + 1) + case d, nd: + raise Exception(f"Uncoverred {d=} -> {nd=}") + corners.append(c_log) + return int(shoelace_area(corners)) + + +def solve2(i: Input, second=False): + lines = i.lines() + ins = [] + for line in lines: + if not line: + continue + _, _, c = line.split() + c = c.replace("(#", "").replace(")", "") + d = int(c[:5], 16) + m = {"0": Grid2D.E, "1": Grid2D.S, "2": Grid2D.W, "3": Grid2D.N}[c[5]] + ins.append((d, m)) + return calc(ins) + + +def debug(): + ins = [ + ( 3, Grid2D.S,), + ( 2, Grid2D.E,), + ( 1, Grid2D.N,), + ( 1, Grid2D.E,), + ( 1, Grid2D.N,), + ( 1, Grid2D.W,), + ( 1, Grid2D.N,), + ( 2, Grid2D.W,), + ] + # Should be 14 but is 2 because it's going counter-clockwise, but mapping + # only works for clockwise. + print(calc(ins)) + + +def main(): + DAY_INPUT = "i18.txt" + print("Example 1:", solve(Input(EXAMPLE))) + print("Solution 1:", solve(Input(DAY_INPUT))) + print("Example 2:", solve2(Input(EXAMPLE), True)) + print("Solution 2:", solve2(Input(DAY_INPUT), True)) + return + + +if __name__ == "__main__": + main() diff --git a/lib.py b/lib.py index acb5c49..19644de 100644 --- a/lib.py +++ b/lib.py @@ -31,6 +31,8 @@ class Grid2D: NE = (-1, 1) SE = (1, 1) SW = (1, -1) + COORDS_ORTH = (N, E, S, W) + COORDS_DIAG = (NW, NE, SE, SW) def __init__(self, text: str): lines = [line for line in text.splitlines() if line.strip() != ""] @@ -222,3 +224,12 @@ class A_Star(object): g_score[neighbor] = tentative_g_score f_score = g_score[neighbor] + h(neighbor) heapq.heappush(open_set, (f_score, neighbor)) + +def shoelace_area(corners): + n = len(corners) + area = 0 + for i in range(n): + x1, y1 = corners[i] + x2, y2 = corners[(i + 1) % n] + area += (x1 * y2) - (x2 * y1) + return abs(area) / 2.0