Skip to content

Commit

Permalink
Day 23: A Long Walk
Browse files Browse the repository at this point in the history
  • Loading branch information
ephemient committed Dec 26, 2023
1 parent 260e705 commit ce08d39
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ Development occurs in language-specific directories:
|[Day20.hs](hs/src/Day20.hs)|[Day20.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day20.kt)|[day20.py](py/aoc2023/day20.py)|[day20.rs](rs/src/day20.rs)|
|[Day21.hs](hs/src/Day21.hs)|[Day21.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day21.kt)|[day21.py](py/aoc2023/day21.py)|[day21.rs](rs/src/day21.rs)|
|[Day22.hs](hs/src/Day22.hs)|[Day22.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day22.kt)|[day22.py](py/aoc2023/day22.py)|[day22.rs](rs/src/day22.rs)|
|[Day23.hs](hs/src/Day23.hs)|[Day23.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day23.kt)||[day23.rs](rs/src/day23.rs)|
|[Day23.hs](hs/src/Day23.hs)|[Day23.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day23.kt)|[day23.py](py/aoc2023/day23.py)|[day23.rs](rs/src/day23.rs)|
|[Day24.hs](hs/src/Day24.hs)|[Day24.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day24.kt)|[day24.py](py/aoc2023/day24.py)|[day24.rs](rs/src/day24.rs) ½|
|[Day25.hs](hs/src/Day25.hs)|[Day25.kt](kt/aoc2023-lib/src/commonMain/kotlin/com/github/ephemient/aoc2023/Day25.kt)|[day25.py](py/aoc2023/day25.py)|[day25.rs](rs/src/day25.rs)|
136 changes: 136 additions & 0 deletions py/aoc2023/day23.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
"""
Day 23: A Long Walk
"""

SAMPLE_INPUT = """
#.#####################
#.......#########...###
#######.#########.#.###
###.....#.>.>.###.#.###
###v#####.#v#.###.#.###
###.>...#.#.#.....#...#
###v###.#.#.#########.#
###...#.#.#.......#...#
#####.#.#.#######.#.###
#.....#.#.#.......#...#
#.#####.#.#.#########v#
#.#...#...#...###...>.#
#.#.#v#######v###.###v#
#...#.>.#...>.>.#.###.#
#####v#.#.###v#.#.###.#
#.....#...#...#.#.#...#
#.#########.###.#.#.###
#...###...#...#...#.###
###.###.#.###v#####v###
#...#...#.#.>.>.#.>.###
#.###.###.#.###.#.#v###
#.....###...###...#...#
#####################.#
"""


# pylint: disable=too-many-locals
def _parse(data):
data = {
(y, x): c
for y, line in enumerate(data.splitlines())
for x, c in enumerate(line)
if c in ".<>^v"
}
start, end = min(data), max(data)
gr = {
(y, x): {
dst: (
c.strip(".") in f and d.strip(".") in f,
c.strip(".") in b and d.strip(".") in b,
1,
)
for dst, f, b in zip(
[(y, x - 1), (y, x + 1), (y - 1, x), (y + 1, x)], "<>^v", "><v^"
)
if (d := data.get(dst, "#")) in ".<>^v"
}
for (y, x), c in data.items()
}
done = False
while not done:
done = True
for key in list(gr):
if key in (start, end):
continue
edges = gr[key]
if not edges:
del gr[key]
elif len(edges) == 1:
(key2,) = edges
del gr[key]
del gr[key2][key]
elif len(edges) == 2:
(key1, (f01, b01, w01)), (key2, (f02, b02, w02)) = edges.items()
f1, b1, w1 = gr[key1][key]
f2, b2, w2 = gr[key2][key]
del gr[key]
del gr[key1][key]
del gr[key2][key]
gr[key1][key2] = (f1 & f02, b1 & b02, w1 + w02)
gr[key2][key1] = (f2 & f01, b2 & b01, w2 + w01)
else:
continue
done = False
return (
start,
end,
{
key: weights
for key, edges in gr.items()
if (weights := {dst: w for dst, (f, _, w) in edges.items() if f})
},
)


def part1(data):
"""
>>> part1(SAMPLE_INPUT)
94
"""
start, end, gr = _parse(data)

def go(pos, used, distance, best):
if pos == end:
return distance if best is None or distance > best else best

reachable = {pos}

def dfs(pos):
for dst in gr.get(pos, {}):
if dst not in used and dst not in reachable:
reachable.add(dst)
dfs(dst)

dfs(pos)
potential = distance + sum(
max((w for dst, w in gr.get(dst, {}).items() if dst not in used), default=0)
for dst in reachable
)
if best is not None and potential <= best or end not in reachable:
return best

used = used | {pos}
for dst, w in gr[pos].items():
if dst not in used:
best = go(dst, used, distance + w, best)

return best

return go(start, set(), 0, None)


def part2(data):
"""
>>> part2(SAMPLE_INPUT)
154
"""
return part1(data.translate(data.maketrans("<>^v", "....")))


parts = (part1, part2)
1 change: 1 addition & 0 deletions py/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ day19 = "aoc2023.day19:parts"
day20 = "aoc2023.day20:parts"
day21 = "aoc2023.day21:parts"
day22 = "aoc2023.day22:parts"
day23 = "aoc2023.day23:parts"
day24 = "aoc2023.day24:parts"
day25 = "aoc2023.day25:parts"

Expand Down

0 comments on commit ce08d39

Please sign in to comment.