Skip to content

Commit 5230f55

Browse files
committed
day 20
1 parent 6f00171 commit 5230f55

File tree

2 files changed

+89
-272
lines changed

2 files changed

+89
-272
lines changed

day20/input2.txt

Lines changed: 0 additions & 108 deletions
This file was deleted.

day20/main.py

Lines changed: 89 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
from collections import namedtuple
2-
from itertools import product, permutations
2+
from itertools import product
33

44
import numpy as np
5-
from imgaug.augmenters.flip import fliplr, flipud
5+
from imgaug.augmenters.flip import fliplr
66
from imgaug.augmenters.geometric import Rot90
7-
from tqdm import tqdm
7+
from scipy.ndimage import generic_filter
88

9-
Tile = namedtuple("Tile", "idx ns ts e")
9+
OrientedTile = namedtuple("OrientedTile", "idx data")
1010

11-
with open("input2.txt") as f:
11+
ndim = 12
12+
with open("input.txt") as f:
1213
parts = f.read().split("\n\n")
1314

1415
tiles = {}
@@ -18,173 +19,97 @@
1819
a = (np.array(k) == "#").astype(np.uint8)
1920
tiles[int(p[0].split()[1][:-1])] = a
2021

21-
def transform(tile, rot, lr, ud):
22+
def transform(tile, rot, lr):
2223
t = Rot90(rot).augment_image(tile)
2324
if lr:
2425
t = fliplr(t)
25-
if ud:
26-
t = flipud(t)
2726
return t
2827

28+
def get_all_transforms(arr):
29+
return [transform(arr, r, f) for r, f in product([0,1,2,3], [False, True])]
30+
2931
def edge_has_fit(tiles, tile_idx, edge):
30-
k = tiles[tile_idx]
31-
32-
if edge == 0: e = k[0]
33-
elif edge == 1: e = k[:,-1]
34-
elif edge == 2: e = k[-1]
35-
elif edge == 3: e = k[:,0]
32+
tile_data = tiles[tile_idx]
33+
34+
if edge == 0: e = tile_data[0]
35+
elif edge == 1: e = tile_data[:,-1]
36+
elif edge == 2: e = tile_data[-1]
37+
elif edge == 3: e = tile_data[:,0]
3638

3739
for idx, tile in tiles.items():
3840
if idx == tile_idx: continue
3941

40-
for trans in product([0,1,2,3], [False, True], [False, True]):
41-
t = transform(tile, *trans)
42-
for f in [t[0], t[:,-1], t[-1], t[:,0]]:
43-
if np.array_equal(f, e):
44-
return idx, trans
45-
return None, None
46-
47-
res = []
48-
# for idx in tqdm(tiles.keys()):
49-
for idx in tiles.keys():
50-
ns, ts = zip(*[edge_has_fit(tiles, idx, i) for i in range(4)])
51-
tile = Tile(idx, ns, ts, len([e for e in ns if e is not None]))
52-
res.append(tile)
53-
54-
# print(int(np.prod(corners)))
55-
56-
corners = [t for t in res if t.e == 2]
57-
edges = [t for t in res if t.e == 3]
58-
middles = [t for t in res if t.e == 4]
59-
60-
print([(t.idx, t.ns) for t in corners])
61-
print([(t.idx, t.ns) for t in edges])
62-
print([(t.idx, t.ns) for t in middles])
63-
print("\n\n")
64-
65-
nrows = 3
66-
ncols = 3
67-
68-
def rotate(l, n):
69-
return list(l[n:] + l[:n])
70-
71-
def flip_lr(l):
72-
return [l[0], l[3], l[2], l[1]]
73-
74-
def flip_ud(l):
75-
return [l[2], l[1], l[0], l[3]]
76-
77-
def find_and_pop(elems, n_idx):
78-
res = None
79-
del_idx = None
80-
for i, t in enumerate(elems):
81-
if t.idx == n_idx:
82-
res = t
83-
del_idx = i
84-
break
85-
else:
86-
raise RuntimeError(f"{t.idx} was not found in {elems}.")
87-
88-
del elems[del_idx]
89-
return res
90-
91-
def match(ns, exp):
92-
assert len(ns) == len(exp)
93-
94-
for n, e in zip(ns, exp):
95-
if e is None:
96-
if n is not None:
97-
return False
98-
else:
99-
if e == 0:
100-
continue
101-
else:
102-
if n != e:
103-
return False
104-
return True
105-
106-
def place(tile, exp):
107-
if tile.idx == 1171:
108-
print("orig")
109-
print(tile.ns)
110-
111-
n = tile.ns
112-
t = tile.ts
113-
for trans in product([0,1,2,3], [False, True], [False, True]):
114-
n = rotate(n, trans[0])
115-
t = rotate(t, trans[0])
116-
if trans[1]:
117-
n = flip_lr(n)
118-
t = flip_lr(t)
119-
if trans[2]:
120-
n = flip_ud(n)
121-
t = flip_ud(t)
122-
123-
if tile.idx == 1171:
124-
print(trans[0], "lr?", trans[1], "ud?", trans[2], end=" ")
125-
print(n, exp)
126-
if match(n, exp):
127-
return Tile(tile.idx, n, t, tile.e)
128-
129-
raise RuntimeError(tile)
130-
131-
def print_short(l):
132-
print([(t.idx, t.ns) for t in l])
133-
134-
layout = []
135-
136-
for idx_row in range(nrows):
137-
if idx_row == 0:
138-
tile = corners.pop()
139-
tile = place(tile, [None, 0, 0, None])
140-
row = [tile]
141-
elif idx_row == nrows-1:
142-
tile = find_and_pop(corners, layout[idx_row-1][0].ns[2])
143-
upper = layout[idx_row-1][0].idx
144-
tile = place(tile, [upper, 0, None, None])
145-
row = [tile]
146-
else:
147-
row = [find_and_pop(edges, layout[idx_row-1][0])]
148-
149-
for idx_col in range(1, ncols):
150-
prev_tile = row[-1]
151-
n_idx = prev_tile.ns[1]
42+
for t in get_all_transforms(tile):
43+
if np.array_equal(t[0], e):
44+
return idx
45+
return None
46+
47+
corners = []
48+
for tile_idx in tiles:
49+
ns = [edge_has_fit(tiles, tile_idx, i) for i in range(4)]
50+
if len([e for e in ns if e is not None]) == 2:
51+
corners.append(tile_idx)
52+
if len(corners) == 4:
53+
break
54+
print(int(np.prod(corners)))
55+
56+
# Part 2
57+
def find_neighbor(tiles, tile, direction, seen):
58+
if direction == "right":
59+
arr = tile.data[:,-1]
60+
elif direction == "down":
61+
arr = tile.data[-1]
62+
63+
for t_idx, t_data in tiles.items():
64+
if t_idx == tile.idx: continue
65+
if t_idx in seen: continue
15266

153-
print_short(row)
154-
print(idx_col, n_idx)
155-
156-
if idx_row in (0, nrows-1):
157-
if idx_col == ncols-1:
158-
elems = corners
159-
else:
160-
elems = edges
161-
else:
162-
if idx_col == ncols-1:
163-
elems = edges
164-
else:
165-
elem = middles
166-
167-
t = find_and_pop(elems, n_idx)
168-
169-
if idx_row == 0:
170-
upper = None
171-
else:
172-
upper = layout[idx_row-1][0].idx
173-
174-
left = prev_tile.idx
175-
176-
if idx_col == ncols-1:
177-
if idx_row == 0:
178-
row.append(place(t, [None, None, 0, left]))
179-
180-
elif idx_row == nrows-1:
181-
row.append(place(t, [0, None, None, left]))
182-
183-
else:
184-
row.append(place(t, [upper, None, 0, left]))
185-
else:
186-
row.append(place(t, [upper, 0, 0, left]))
187-
188-
layout.append(row)
189-
190-
print(layout)
67+
for data in get_all_transforms(t_data):
68+
if direction == "right" and np.array_equal(arr, data[:,0]):
69+
return OrientedTile(t_idx, data)
70+
elif direction == "down" and np.array_equal(arr, data[0]):
71+
return OrientedTile(t_idx, data)
72+
73+
raise Exception()
74+
75+
corner_idx = corners[0]
76+
for corner_data in get_all_transforms(tiles[corner_idx]):
77+
try:
78+
seen = set()
79+
display = [[] for _ in range(ndim)]
80+
display[0].append(OrientedTile(corner_idx, corner_data))
81+
seen.add(corners[0])
82+
83+
for idx_row in range(ndim):
84+
row = display[idx_row]
85+
while len(row) < ndim:
86+
n = find_neighbor(tiles, row[-1], "right", seen)
87+
row.append(n)
88+
seen.add(n.idx)
89+
90+
if idx_row < ndim-1:
91+
n = find_neighbor(tiles, row[0], "down", seen)
92+
display[idx_row+1].append(n)
93+
seen.add(n.idx)
94+
except:
95+
continue
96+
break
97+
98+
display2 = np.concatenate([np.concatenate([t.data[1:-1, 1:-1] for t in row], axis=1) for row in display])
99+
100+
nessi = np.array([list(" # "),
101+
list("# ## ## ###"),
102+
list(" # # # # # # ")])
103+
nessi = (nessi == "#").astype(np.uint8)
104+
105+
def f(part):
106+
part = np.reshape(part, nessi.shape)
107+
for y, row in enumerate(part):
108+
for x, pix in enumerate(row):
109+
if nessi[y][x] == 1 and pix == 0:
110+
return 0
111+
return 1
112+
113+
found = max([np.sum(generic_filter(img, f, nessi.shape, mode="constant", cval=0)) for img in get_all_transforms(display2)])
114+
roughness = int(np.count_nonzero(display2) - found*np.sum(nessi))
115+
print(roughness)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy