Initial commit

This commit is contained in:
Geoffrey Frogeye 2024-12-25 12:58:02 +01:00
commit 97a4330bc0
Signed by: geoffrey
GPG key ID: C72403E7F82E6AD8
110 changed files with 7006 additions and 0 deletions

4
2024/16/:w Normal file
View file

@ -0,0 +1,4 @@
####
#.E#
#S##
####

23
2024/16/README.md Normal file
View file

@ -0,0 +1,23 @@
Oh boy. My first time in a while doing something with mazes.
I decided to go with a filling thing.
I realized I need to put the direction as a dimension in the table of visited scores.
So basically it would go up to the very last tile, and then be like "op, what if I went back to the start?", although I did not know that at the time.
I did exhaust Python call stack, so I increased it, again and again.
But I thought that surely, even with this dumb of an algorithm it should be able to do, so I decided to look for a bug.
I used the depth variable (initally only used to indent print statements) as my own call stack limit that would still print the maze.
I realized that even at 1000 depth, the maze was already filled, which puzzled me.
I submitted the answer... and it was correct x)
For part 2 I first implemented something that would print only one best path.
Then I realized my mistake, and then did something that would work if not for the fact that I optimized out
2 paths arriving with the same score. A <= changed to < later and it was fixed.
The optimisation didn't cost much, the "allocating a list every recursion", very much so.
Since it was taking a long time to compute I realized maybe I could do something clever with only considering crossways,
since there's quite a lot of corridors.
But I decided to instead firsrt code two2.py, which would add the position to the set of best places only when the recursion hit the best score,
which was hardcoded from part 1 (it took 48s to run alone, sooo).
I tried it on demo, got None as answer to part 1 and 0 as answer to part 2, completly overlooked that, and let it cook on the real input.
In the end, the first iteration of two.py ended up being cooked first, 11 minutes after, with the right answer.
...a win is a win I guess

15
2024/16/demog Normal file
View file

@ -0,0 +1,15 @@
################
############..E#
###########..#.#
##########..##.#
#########..###.#
########..##...#
#######..###.###
######..####...#
#####..#######.#
####..##.......#
###..###.#######
##..####.......#
#..###########.#
#S.............#
################

4
2024/16/demog0 Normal file
View file

@ -0,0 +1,4 @@
####
#.E#
#S.#
####

86
2024/16/networkx_test.py Normal file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env python3
import sys
import typing
import matplotlib.pyplot as plt
import networkx as nx
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
# Parse input
g = nx.DiGraph()
for i in range(height):
for j in range(width):
char = lines[i][j]
if char == "#":
continue
for d, direction in enumerate(directions):
cur = (i, j, d)
# Start/end
if char == "E":
g.add_edge(cur, "end", weight=0)
elif char == "S" and d == 1:
g.add_edge("start", cur, weight=0)
# Rotate
g.add_edge(cur, (i, j, (d + 1) % len(directions)), weight=1000)
g.add_edge(cur, (i, j, (d - 1) % len(directions)), weight=1000)
# Advance
ii, jj = i + direction[0], j + direction[1]
if lines[ii][jj] == "#":
continue
g.add_edge(cur, (ii, jj, d), weight=1)
# Part 1
score = nx.shortest_path_length(g, "start", "end", weight="weight")
print(f"{score=}")
# Part 2
paths = nx.all_shortest_paths(g, "start", "end", weight="weight")
best_orientations = set()
for path in paths:
best_orientations |= set(path)
path_edges = list(zip(path, path[1:])) # Will be one random best path
best_places = set(bo[:2] for bo in best_orientations - {"start", "end"})
print(f"{len(best_places)=}")
# Draw graph
if len(g.nodes) > 1000:
sys.exit(0)
node_colors = ["blue" if node in best_orientations else "cyan" for node in g.nodes()]
edge_colors = ["red" if edge in path_edges else "black" for edge in g.edges()]
node_pos: dict[typing.Any, tuple[float, float]] = dict()
for node in g.nodes():
pos: tuple[float, float]
if node == "start":
pos = height - 1, 0
elif node == "end":
pos = 0, width - 1
else:
i, j, d = node
direction = directions[d]
pos = i + direction[0] / 3, j + direction[1] / 3
node_pos[node] = pos[1], pos[0] * -1
nx.draw_networkx_nodes(g, node_pos, node_color=node_colors)
nx.draw_networkx_edges(g, node_pos, edge_color=edge_colors)
# nx.draw_networkx_labels(g, node_pos)
# nx.draw_networkx_edge_labels(
# g, node_pos, edge_labels={(u, v): d["weight"] for u, v, d in g.edges(data=True)}
# )
plt.show()

135
2024/16/numpy_test.py Normal file
View file

@ -0,0 +1,135 @@
#!/usr/bin/env python3
import sys
import numpy as np
import scipy as sp
dtype = np.int32
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
start = height - 2, 1
stop = 1, width - 2
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
walls = np.zeros((height, width), dtype)
for i in range(height):
for j in range(width):
if lines[i][j] == "#":
walls[i, j] = 1
invwalls = 1 - walls
# print("Walls")
# print(walls)
# TEST 4
scores = np.zeros((height, width, len(directions)), dtype)
scores[start[0], start[1], 1] = 1
for i in range(1000):
print("Step", i)
oldscores = scores.copy()
for od, odir in enumerate(directions):
for nd, ndir in enumerate(directions):
score = scores[:, :, nd]
moved = sp.ndimage.shift(oldscores[:, :, od], ndir)
increment = 1 if nd == od else 1001
moved = (moved + increment) * (moved > 0)
moved = moved * invwalls
mask = (moved > 0) & ((score == 0) | (score > moved))
scores[:, :, nd] = (score * ~mask) + (moved * mask)
# for d, dir in enumerate(directions):
# print("Direction", directions_keys[d])
# print(scores[:, :, d])
if (scores == oldscores).all():
break
else:
print("Limit!")
end_score = min(filter(lambda d: d > 0, scores[stop])) - 1
print(f"{end_score=}")
# TEST 3
# scores = [np.zeros((height, width), dtype) for _ in directions]
# scores[1][start] = 1
#
# for i in range(100):
# print("Step", i)
# for od, odir in enumerate(directions):
# oldscore = scores[od].copy()
# for nd, ndir in enumerate(directions):
# score = scores[nd]
# moved = sp.ndimage.shift(oldscore, ndir)
# increment = 1 if nd == od else 1001
# moved = (moved + increment) * (moved > 0)
# moved = moved * invwalls
# mask = (moved > 0) & ((score == 0) | (score > moved))
# scores[nd] = (score * ~mask) + (moved * mask)
#
# final_score = None
# for d, dir in enumerate(directions):
# print("Direction", directions_keys[d])
# print(scores[d])
# end_score = scores[d][stop]
# if end_score > 0:
# if final_score is None or end_score < final_score:
# final_score = end_score
#
# if final_score:
# final_score -= 1
# print(f"{final_score=}")
# break
# else:
# print("Limit!")
# TEST 2
# score = np.zeros((height, width), dtype)
# score[start] = 1
#
# for i in range(10):
# print("Step", i)
# oldscore = score.copy()
# for nd, ndir in enumerate(directions):
# moved = sp.ndimage.shift(oldscore, ndir)
# moved = (moved + 1) * (moved > 0)
# moved = moved * invwalls
# mask = (moved > 0) & ((score == 0) | (score > moved))
# score = (score * ~mask) + (moved * mask)
# print(score)
# TEST 1
# directions = np.array([[0, 1, 0], [1, 1, 1], [0, 1, 0]], dtype)
#
# visited = np.zeros((height, width), dtype)
# visited[start] = 1
#
# for i in range(1000):
# print("Step", i)
# new = sp.signal.convolve2d(visited, directions, mode="same")
# visited = (((new > 0) - walls) > 0)
# print(visited * 1)
# if visited[stop]:
# break
# else:
# print("Limit!")
# print(i)

86
2024/16/one.py Normal file
View file

@ -0,0 +1,86 @@
#!/usr/bin/env python3
import pprint
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
min_scores: list[list[list[None | int]]] = list()
for _ in range(height):
line: list[list[None | int]] = list()
for _ in range(width):
cell: list[None | int] = [None] * len(directions)
line.append(cell)
min_scores.append(line)
start = height - 2, 1
def walk(pos: vec, dir: int, score: int, depth: int = 0) -> int | None:
i, j = pos
char = lines[i][j]
if depth > 1000:
return None
# print("-" * depth, 28, pos, char, directions_keys[dir], score)
if char == "E":
return score
elif char == "#":
return None
min_score = min_scores[i][j][dir]
if min_score is not None and score >= min_score:
# print("-" * depth, f" 32 already taken {score} >= {min_score}")
return None
min_scores[i][j][dir] = score
mscore = None
for ndir, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
price = 1 if dir == ndir else 1001
nscore = walk((ii, jj), ndir, score + price, depth+1)
if nscore is None:
continue
if mscore is None or nscore < mscore:
mscore = nscore
return mscore
sys.setrecursionlimit(9000)
tot_score = walk(start, 1, 0)
# Viz
for i in range(height):
cline = ""
for j in range(width):
char = lines[i][j]
min_score = None
for d in range(len(directions)):
score = min_scores[i][j][d]
if score is None:
continue
if min_score is None or score < min_score:
char = directions_keys[d]
min_score = score
cline += char
print(cline)
print()
print(tot_score)

93
2024/16/one_opti.py Normal file
View file

@ -0,0 +1,93 @@
#!/usr/bin/env python3
import pprint
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
min_scores: list[list[list[None | int]]] = list()
for _ in range(height):
line: list[list[None | int]] = list()
for _ in range(width):
cell: list[None | int] = [None] * len(directions)
line.append(cell)
min_scores.append(line)
start = height - 2, 1
def walk(pos: vec, dir: int, score: int, depth: int = 0) -> int | None:
i, j = pos
char = lines[i][j]
if depth > 1000:
return None
# print("-" * depth, 28, pos, char, directions_keys[dir], score)
if char == "E":
return score
elif char == "#":
return None
min_score = min_scores[i][j][dir]
for d, min_score in enumerate(min_scores[i][j]):
if min_score is None:
continue
if d == dir:
if score >= min_score:
return None
else:
if score >= min_score + 1000:
return None
min_scores[i][j][dir] = score
mscore = None
for ndir, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
price = 1 if dir == ndir else 1001
nscore = walk((ii, jj), ndir, score + price, depth + 1)
if nscore is None:
continue
if mscore is None or nscore < mscore:
mscore = nscore
return mscore
sys.setrecursionlimit(3000)
tot_score = walk(start, 1, 0)
# Viz
for i in range(height):
cline = ""
for j in range(width):
char = lines[i][j]
min_score = None
for d in range(len(directions)):
score = min_scores[i][j][d]
if score is None:
continue
if min_score is None or score < min_score:
char = directions_keys[d]
min_score = score
cline += char
print(cline)
print()
print(tot_score)

27
2024/16/reddit_edge_case Normal file
View file

@ -0,0 +1,27 @@
###########################
#######################..E#
######################..#.#
#####################..##.#
####################..###.#
###################..##...#
##################..###.###
#################..####...#
################..#######.#
###############..##.......#
##############..###.#######
#############..####.......#
############..###########.#
###########..##...........#
##########..###.###########
#########..####...........#
########..###############.#
#######..##...............#
######..###.###############
#####..####...............#
####..###################.#
###..##...................#
##..###.###################
#..####...................#
#.#######################.#
#S........................#
###########################

14
2024/16/reddit_open_maze Normal file
View file

@ -0,0 +1,14 @@
####################################################
#......................................#..........E#
#......................................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.................#...........#
#....................#.............................#
#S...................#.............................#
####################################################

105
2024/16/two.py Normal file
View file

@ -0,0 +1,105 @@
#!/usr/bin/env python3
import pprint
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
min_scores: list[list[list[None | int]]] = list()
for _ in range(height):
line: list[list[None | int]] = list()
for _ in range(width):
cell: list[None | int] = [None] * len(directions)
line.append(cell)
min_scores.append(line)
start = height - 2, 1
valid_paths: list[tuple[int, set[vec]]] = list()
def walk(pos: vec, dir: int, score: int, path: list[vec]) -> int | None:
i, j = pos
char = lines[i][j]
depth = len(path)
path = path + [pos]
if depth > 1000:
return None
# print("-" * depth, 28, pos, char, directions_keys[dir], score)
if char == "E":
valid_paths.append((score, set(path)))
return score
elif char == "#":
return None
min_score = min_scores[i][j][dir]
if min_score is not None and score > min_score:
# print("-" * depth, f" 32 already taken {score} >= {min_score}")
return None
min_scores[i][j][dir] = score
mscore = None
for ndir, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
price = 1 if dir == ndir else 1001
nscore = walk((ii, jj), ndir, score + price, path)
if nscore is None:
continue
if mscore is None or nscore < mscore:
mscore = nscore
return mscore
sys.setrecursionlimit(9000)
tot_score = walk(start, 1, 0, [])
print(76, len(valid_paths))
all_best: set[vec] = set()
for s, path in valid_paths:
if s != tot_score:
continue
print(81, "BEST")
all_best |= path
# Viz
for i in range(height):
cline = ""
for j in range(width):
char = lines[i][j]
pos = i, j
if pos in all_best:
min_score = None
for d in range(len(directions)):
score = min_scores[i][j][d]
if score is None:
continue
if min_score is None or score < min_score:
char = directions_keys[d]
min_score = score
cline += char
print(cline)
print()
print(tot_score)
print(len(all_best))

95
2024/16/two2.py Normal file
View file

@ -0,0 +1,95 @@
#!/usr/bin/env python3
import pprint
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
min_scores: list[list[list[None | int]]] = list()
for _ in range(height):
line: list[list[None | int]] = list()
for _ in range(width):
cell: list[None | int] = [None] * len(directions)
line.append(cell)
min_scores.append(line)
start = height - 2, 1
best: set[vec] = set()
def walk(pos: vec, dir: int, score: int, depth: int) -> int | None:
i, j = pos
char = lines[i][j]
if depth > 1000:
return None
# print("-" * depth, 28, pos, char, directions_keys[dir], score)
if char == "E":
if score == 90440:
return score
else:
return None
elif char == "#":
return None
min_score = min_scores[i][j][dir]
if min_score is not None and score > min_score:
# print("-" * depth, f" 32 already taken {score} >= {min_score}")
return None
min_scores[i][j][dir] = score
mscore = None
for ndir, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
price = 1 if dir == ndir else 1001
nscore = walk((ii, jj), ndir, score + price, depth + 1)
if nscore is None:
continue
best.add((ii, jj))
# if mscore is None or nscore < mscore:
# mscore = nscore
return mscore
sys.setrecursionlimit(9000)
tot_score = walk(start, 1, 0, 0)
# Viz
for i in range(height):
cline = ""
for j in range(width):
char = lines[i][j]
pos = i, j
min_score = None
for d in range(len(directions)):
score = min_scores[i][j][d]
if score is None:
continue
if min_score is None or score < min_score:
char = directions_keys[d]
min_score = score
cline += char
print(cline)
print()
print(tot_score)
print(len(best))

111
2024/16/two_opti.py Normal file
View file

@ -0,0 +1,111 @@
#!/usr/bin/env python3
import pprint
import sys
input_file = sys.argv[1]
with open(input_file) as fd:
lines = [line.rstrip() for line in fd.readlines()]
height = len(lines)
width = len(lines[0])
vec = tuple[int, int]
directions = [
(-1, 0), # ^ North
(0, 1), # > East
(1, 0), # v South
(0, -1), # < West
]
directions_keys = ("^", ">", "v", "<")
min_scores: list[list[list[None | int]]] = list()
for _ in range(height):
line: list[list[None | int]] = list()
for _ in range(width):
cell: list[None | int] = [None] * len(directions)
line.append(cell)
min_scores.append(line)
start = height - 2, 1
valid_paths: list[tuple[int, set[vec]]] = list()
def walk(pos: vec, dir: int, score: int, path: list[vec]) -> int | None:
i, j = pos
char = lines[i][j]
depth = len(path)
path = path + [pos]
if depth > 1000:
return None
# print("-" * depth, 28, pos, char, directions_keys[dir], score)
if char == "E":
valid_paths.append((score, set(path)))
return score
elif char == "#":
return None
min_score = min_scores[i][j][dir]
for d, min_score in enumerate(min_scores[i][j]):
if min_score is None:
continue
if d == dir:
if score > min_score:
return None
else:
if score > min_score + 1000:
return None
min_scores[i][j][dir] = score
mscore = None
for ndir, direction in enumerate(directions):
ii, jj = i + direction[0], j + direction[1]
price = 1 if dir == ndir else 1001
nscore = walk((ii, jj), ndir, score + price, path)
if nscore is None:
continue
if mscore is None or nscore < mscore:
mscore = nscore
return mscore
sys.setrecursionlimit(9000)
tot_score = walk(start, 1, 0, [])
print(76, len(valid_paths))
all_best: set[vec] = set()
for s, path in valid_paths:
if s != tot_score:
continue
print(81, "BEST")
all_best |= path
# Viz
for i in range(height):
cline = ""
for j in range(width):
char = lines[i][j]
pos = i, j
if pos in all_best:
min_score = None
for d in range(len(directions)):
score = min_scores[i][j][d]
if score is None:
continue
if min_score is None or score < min_score:
char = directions_keys[d]
min_score = score
cline += char
print(cline)
print()
print(tot_score)
print(len(all_best))