diff --git a/challenges/2024/08-resonantCollinearity/README.md b/challenges/2024/08-resonantCollinearity/README.md new file mode 100644 index 0000000..9414e3c --- /dev/null +++ b/challenges/2024/08-resonantCollinearity/README.md @@ -0,0 +1,6 @@ +# [Day 8: Resonant Collinearity](https://adventofcode.com/2024/day/8) + +Part 1 is: +* less than 298 + +This is not a fully generic solution as, in a situation where the differences between x and y dimensions of pairs of coordinates are not coprime, this implementation would skip steps. \ No newline at end of file diff --git a/challenges/2024/08-resonantCollinearity/main.py b/challenges/2024/08-resonantCollinearity/main.py new file mode 100644 index 0000000..baf2a4c --- /dev/null +++ b/challenges/2024/08-resonantCollinearity/main.py @@ -0,0 +1,74 @@ +import sys +from gridutil import grid, coord +from collections import defaultdict +import itertools +from fractions import Fraction + + +def parse(instr: str) -> tuple[dict[str, list[coord.Coordinate]], tuple[int, int]]: + g = grid.parse(instr) + antenna_by_type = defaultdict(list) + for key in g: + if g[key] == ".": + continue + antenna_by_type[g[key]].append(key) + return antenna_by_type, (grid.get_max_x(g), grid.get_max_y(g)) + + +def one(instr: str): + (antenna_by_type, (max_x, max_y)) = parse(instr) + + pos = set() + for antenna_type in antenna_by_type: + for (a, b) in itertools.permutations(antenna_by_type[antenna_type], 2): + diff = coord.sub(b, a) + c = coord.add(a, coord.mult(diff, 2)) + if 0 <= c.x <= max_x and 0 <= c.y <= max_y: + pos.add(c) + + return len(pos) + + +def two(instr: str): + (antenna_by_type, (max_x, max_y)) = parse(instr) + + pos = set() + for antenna_type in antenna_by_type: + for (a, b) in itertools.permutations(antenna_by_type[antenna_type], 2): + if ( + a.x > b.x + ): # filter out (most) duplicate pairs eg ((1, 2), (2, 1)) will only be calculated as ((2, 1), (1, 2)) will be filtered. This also prevents diff.x from being negative (useful for the mod operation) + continue + + diff = coord.sub(b, a) + + m = Fraction( + diff.y, diff.x + ) # equiv of diff.y / diff.x but without the 26.9999999999996 issue + c = a.y - (m * a.x) + + x_cursor = a.x % diff.x + y_cursor = int((x_cursor * m) + c) + while x_cursor <= max_x: + if 0 <= y_cursor <= max_y: + pos.add((x_cursor, y_cursor)) + x_cursor += diff.x + y_cursor += diff.y + + return len(pos) + + +def _debug(*args, **kwargs): + kwargs["file"] = sys.stderr + print(*args, **kwargs) + + +if __name__ == "__main__": + if len(sys.argv) < 2 or sys.argv[1] not in ["1", "2"]: + print("Missing day argument", file=sys.stderr) + sys.exit(1) + inp = sys.stdin.read().strip() + if sys.argv[1] == "1": + print(one(inp)) + else: + print(two(inp)) diff --git a/challenges/2024/08-resonantCollinearity/tests.json b/challenges/2024/08-resonantCollinearity/tests.json new file mode 100644 index 0000000..3aaf3a8 --- /dev/null +++ b/challenges/2024/08-resonantCollinearity/tests.json @@ -0,0 +1,26 @@ +{ + "1": [ + { + "is": "14", + "input": "............\n........0...\n.....0......\n.......0....\n....0.......\n......A.....\n............\n............\n........A...\n.........A..\n............\n............\n\n" + }, + { + "is": "2", + "input": "..........\n..........\n..........\n....a.....\n..........\n.....a....\n..........\n..........\n..........\n..........\n\n" + }, + { + "is": "4", + "input": "..........\n..........\n..........\n....a.....\n........a.\n.....a....\n..........\n..........\n..........\n..........\n" + } + ], + "2": [ + { + "is": "34", + "input": "............\n........0...\n.....0......\n.......0....\n....0.......\n......A.....\n............\n............\n........A...\n.........A..\n............\n............\n\n" + }, + { + "is": "9", + "input": "T.........\n...T......\n.T........\n..........\n..........\n..........\n..........\n..........\n..........\n..........\n" + } + ] +} \ No newline at end of file diff --git a/challenges/2024/README.md b/challenges/2024/README.md index 19665c2..554a496 100644 --- a/challenges/2024/README.md +++ b/challenges/2024/README.md @@ -4,7 +4,7 @@ Solutions to the [2024 Advent of Code](https://adventofcode.com/2024)! --- -Total stars: **14 ★** +Total stars: **16 ★** ![Benchmark graph](./benchmark-graph.png) @@ -20,4 +20,5 @@ A day denoted with an asterisk means it has a visualisation. | 04* - Ceres Search | ★ ★ | Python | When it says a cross, it does not mean a plus. | | 05 - Print Queue | ★ ★ | Python | Before you dismiss and idea as being "too simple", make sure you check that it doesn't work. | | 06 - Guard Gallivant | ★ ★ | Python | oh dear runtime (also I knew what I wanted to do for so long it just took me 3 hours to implement it properly) | -| 07 - Bridge Repair | ★ ★ | Python | Maths? Backwards?? | \ No newline at end of file +| 07 - Bridge Repair | ★ ★ | Python | Maths? Backwards?? | +| 08 - Resonant Collinearity | ★ ★ | Python | `Fraction` saving us all from the curse of a computer's inability to do floating point arithmetic | \ No newline at end of file diff --git a/challenges/2024/benchmark-graph.png b/challenges/2024/benchmark-graph.png index be12f4a..aded0dd 100644 Binary files a/challenges/2024/benchmark-graph.png and b/challenges/2024/benchmark-graph.png differ diff --git a/challenges/2024/benchmarks.jsonl b/challenges/2024/benchmarks.jsonl index d54d779..af991e8 100644 --- a/challenges/2024/benchmarks.jsonl +++ b/challenges/2024/benchmarks.jsonl @@ -12,3 +12,5 @@ {"day": 6, "part": 2, "runner": "py", "min": 15.881408452987671, "max": 17.086341857910156, "avg": 16.64130985736847, "n": 6} {"day": 7, "part": 1, "runner": "py", "min": 0.025098562240600586, "max": 0.04218649864196777, "avg": 0.027579346656799317, "n": 500} {"day": 7, "part": 2, "runner": "py", "min": 0.03137779235839844, "max": 0.04896378517150879, "avg": 0.03503713369369507, "n": 500} +{"day": 8, "part": 1, "runner": "py", "min": 0.025803089141845703, "max": 0.036757469177246094, "avg": 0.02743640899658203, "n": 200} +{"day": 8, "part": 2, "runner": "py", "min": 0.027710437774658203, "max": 0.035851240158081055, "avg": 0.029560294151306152, "n": 200}