|
|
@ -4,6 +4,10 @@ from collections import defaultdict as dd |
|
|
|
from aoc import get_input # AoC |
|
|
|
from aoc import get_input # AoC |
|
|
|
import re # regex |
|
|
|
import re # regex |
|
|
|
import numpy as np |
|
|
|
import numpy as np |
|
|
|
|
|
|
|
from copy import deepcopy as copy |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
from sys import setrecursionlimit |
|
|
|
|
|
|
|
setrecursionlimit(20000) |
|
|
|
|
|
|
|
|
|
|
|
data = get_input(20).strip() |
|
|
|
data = get_input(20).strip() |
|
|
|
|
|
|
|
|
|
|
@ -30,14 +34,8 @@ for genpic in data.split("\n\n"): |
|
|
|
for pid, pic in pics.items(): |
|
|
|
for pid, pic in pics.items(): |
|
|
|
pics[pid] = np.asarray(pic) |
|
|
|
pics[pid] = np.asarray(pic) |
|
|
|
|
|
|
|
|
|
|
|
print(pics["3079"]) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fits = dict() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# fits struc |
|
|
|
mapWidth = len(pics) ** (1/2) |
|
|
|
# |
|
|
|
|
|
|
|
# fits[id] = { "L": (otherid, rotation) } |
|
|
|
|
|
|
|
# where key = "L", "R", "U", "D", "UL", "UR", "DL", "DR" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def compileBorder(row): |
|
|
|
def compileBorder(row): |
|
|
|
out = "" |
|
|
|
out = "" |
|
|
@ -46,7 +44,7 @@ def compileBorder(row): |
|
|
|
|
|
|
|
|
|
|
|
return out |
|
|
|
return out |
|
|
|
|
|
|
|
|
|
|
|
def compileSideBorder(pic, xOffset=0): |
|
|
|
def compileSideBorder(pic, xOffset=0): |
|
|
|
out = "" |
|
|
|
out = "" |
|
|
|
for row in pic: |
|
|
|
for row in pic: |
|
|
|
out += row[xOffset] |
|
|
|
out += row[xOffset] |
|
|
@ -61,10 +59,10 @@ def genBorders(picid): |
|
|
|
|
|
|
|
|
|
|
|
## compile its borders |
|
|
|
## compile its borders |
|
|
|
borders = dict() |
|
|
|
borders = dict() |
|
|
|
borders[0] = compileBorder(pic[0]) |
|
|
|
borders["U"] = compileBorder(pic[0]) |
|
|
|
borders[1] = compileSideBorder(pic, -1) |
|
|
|
borders["R"] = compileSideBorder(pic, -1) |
|
|
|
borders[2] = compileBorder(pic[-1]) # up: 0, right: 1, down: 2, left: 3 |
|
|
|
borders["D"] = compileBorder(pic[-1]) # up: 0, right: 1, down: 2, left: 3 |
|
|
|
borders[3] = compileSideBorder(pic, 0) |
|
|
|
borders["L"] = compileSideBorder(pic, 0) |
|
|
|
|
|
|
|
|
|
|
|
return borders |
|
|
|
return borders |
|
|
|
|
|
|
|
|
|
|
@ -74,14 +72,14 @@ for pid, pic in pics.items(): |
|
|
|
|
|
|
|
|
|
|
|
# picmap = dict() # map of the arranged pics |
|
|
|
# picmap = dict() # map of the arranged pics |
|
|
|
|
|
|
|
|
|
|
|
def rotateTile(otherside): |
|
|
|
def flip(otherside): |
|
|
|
# 0 up, 1 right, 2 down, 3 left |
|
|
|
# 0 up, 1 right, 2 down, 3 left |
|
|
|
|
|
|
|
|
|
|
|
sidething = { # used to rotate as the rotation is allways the opposite relative to the whole system |
|
|
|
sidething = { # used to rotate as the rotation is allways the opposite relative to the whole system |
|
|
|
0: 2, |
|
|
|
"U": "D", |
|
|
|
1: 3, |
|
|
|
"R": "L", |
|
|
|
2: 0, |
|
|
|
"D": "U", |
|
|
|
3: 1 |
|
|
|
"L": "R" |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
newotherside = sidething[otherside] |
|
|
|
newotherside = sidething[otherside] |
|
|
@ -98,301 +96,84 @@ def borderCheck(border, other): |
|
|
|
else: |
|
|
|
else: |
|
|
|
return False, other |
|
|
|
return False, other |
|
|
|
|
|
|
|
|
|
|
|
def getFits(picid, ignoreid): |
|
|
|
fitsDict = dd(dict) |
|
|
|
borders = picBorders[picid] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
seenborders = [] |
|
|
|
# fits struc |
|
|
|
attachedTiles = [] |
|
|
|
# |
|
|
|
|
|
|
|
# fits[id] = { "L": (otherid, rotation) } |
|
|
|
|
|
|
|
# where key = "L", "R", "U", "D", "UL", "UR", "DL", "DR" |
|
|
|
|
|
|
|
# otherid is the one that fits |
|
|
|
|
|
|
|
# rotation is the other that fits rotation to be able to fit |
|
|
|
|
|
|
|
|
|
|
|
# if some border match, i.e. this[2] match other[1] then rotate and/or flip other? |
|
|
|
|
|
|
|
# dont actually have to arrange the map, just get stuff that fits together and their IDs |
|
|
|
|
|
|
|
# |
|
|
|
|
|
|
|
# probably gonna be in part 2 idk |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match = False |
|
|
|
def getFits(borders, fits=fitsDict, ignore=[]): |
|
|
|
matchID = None |
|
|
|
#print(fits) |
|
|
|
selfSide = None |
|
|
|
#print("\n#", borders, "\n") |
|
|
|
matchSide = None |
|
|
|
ignoreLen = len(ignore) |
|
|
|
matchFlipped = False |
|
|
|
borLen = len(borders) |
|
|
|
|
|
|
|
|
|
|
|
prevTile = None |
|
|
|
print(ignoreLen, borLen) |
|
|
|
|
|
|
|
|
|
|
|
for side, border in borders.items(): # TODO: make recursive instead and flip (np.fliplr) (and for up-down) |
|
|
|
if(ignoreLen >= borLen): |
|
|
|
# check other borders |
|
|
|
print("goodbye") |
|
|
|
selfSide = side |
|
|
|
return fits # if there is nothing to do then return the result |
|
|
|
|
|
|
|
|
|
|
|
for pid, bor in picBorders.items(): |
|
|
|
newborders = copy(borders) |
|
|
|
if(pid == picid or pid == ignoreid): |
|
|
|
for pid, border in borders.items(): # borders[pid] |
|
|
|
continue |
|
|
|
#print(f"\nChecking {pid=}") |
|
|
|
|
|
|
|
if( pid in ignore ): |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
# print("Checking", pid, f"{attachedTiles=}") |
|
|
|
for pos, bor in border.items(): # check each border : borders[pid][pos] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print(f"##Border {pid=} {pos=} {bor=}") |
|
|
|
|
|
|
|
|
|
|
|
for pos, line in bor.items(): # top and bottom matching |
|
|
|
for pid2, border2 in borders.items(): # check for others borders ; borders[pid2] |
|
|
|
if(not pos in [0, len(bor)-1]): |
|
|
|
if(pid2 == pid or pid2 in ignore): |
|
|
|
continue |
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
if(line in seenborders): |
|
|
|
for pos2, bor2 in border2.items(): # check for other matching border ; borders[pid2][pos2] |
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
check, newborder = borderCheck(border, line) |
|
|
|
|
|
|
|
print("TB Checking border", pos, ":", line, f"{newborder=} {line=} : {border=}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( check ): |
|
|
|
|
|
|
|
matchID = pid |
|
|
|
|
|
|
|
matchSide = pos |
|
|
|
|
|
|
|
matchFlipped = line == newborder # flipped from matchSide axis |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( newborder != line ): |
|
|
|
|
|
|
|
picBorders[pid][pos] = newborder # flip that border if flipped |
|
|
|
|
|
|
|
otherside = rotateTile(pos) |
|
|
|
|
|
|
|
picBorders[pid][otherside] = picBorders[pid][otherside][::-1] # flip the other side too |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match = True |
|
|
|
|
|
|
|
attachedTiles.append(matchID) |
|
|
|
|
|
|
|
seenborders.append(line) |
|
|
|
|
|
|
|
prevTile = matchID |
|
|
|
|
|
|
|
print("MATCH") |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(not match): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
border_left, border_right = "", "" |
|
|
|
|
|
|
|
for y, line in bor.items(): |
|
|
|
|
|
|
|
border_left += line[0] |
|
|
|
|
|
|
|
border_right += line[-1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print(f"LR Checking border : {border_left=} {border_right=}") |
|
|
|
|
|
|
|
check_left, newborder_left = borderCheck(border_left, line) |
|
|
|
|
|
|
|
check_right, newborder_right = borderCheck(border_right, line) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(check_left): |
|
|
|
|
|
|
|
pos = 3 |
|
|
|
|
|
|
|
matchID = pid |
|
|
|
|
|
|
|
matchSide = pos |
|
|
|
|
|
|
|
matchFlipped = border_left == newborder_left # flipped from matchSide axis |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( newborder_left != line ): |
|
|
|
|
|
|
|
picBorders[pid][pos] = newborder_left # flip that border if flipped |
|
|
|
|
|
|
|
otherside = rotateTile(pos) |
|
|
|
|
|
|
|
picBorders[pid][otherside] = picBorders[pid][otherside][::-1] # flip the other side too |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match = True |
|
|
|
check, newBor2 = borderCheck(bor, bor2) |
|
|
|
attachedTiles.append(matchID) |
|
|
|
print(f"####Border2 {pid2=} {pos2=} {bor2=} {newBor2=} {check=}") |
|
|
|
seenborders.append(line) |
|
|
|
|
|
|
|
prevTile = matchID |
|
|
|
|
|
|
|
print("MATCH") |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
elif(check_right): |
|
|
|
|
|
|
|
pos = 1 |
|
|
|
|
|
|
|
matchID = pid |
|
|
|
|
|
|
|
matchSide = pos |
|
|
|
|
|
|
|
matchFlipped = border_right == newborder_right # flipped from matchSide axis |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( newborder_right != line ): |
|
|
|
if(check): # if the two borders match: |
|
|
|
picBorders[pid][pos] = newborder_right # flip that border if flipped |
|
|
|
fits[pid] = dict() |
|
|
|
otherside = rotateTile(pos) |
|
|
|
fits[pid][pos] = (pid2, pos2) |
|
|
|
picBorders[pid][otherside] = picBorders[pid][otherside][::-1] # flip the other side too |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
match = True |
|
|
|
ignore.append(pid) |
|
|
|
attachedTiles.append(matchID) |
|
|
|
|
|
|
|
seenborders.append(line) |
|
|
|
|
|
|
|
prevTile = matchID |
|
|
|
|
|
|
|
print("MATCH") |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fits[pid] = fits[pid] or {"E": None} # It has to end somewhere |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return getFits(newborders, fits, ignore) # do the other borders |
|
|
|
|
|
|
|
|
|
|
|
else: |
|
|
|
print(picBorders) |
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(match): |
|
|
|
fits = getFits(picBorders, fitsDict) |
|
|
|
return [selfSide, matchID, matchSide, matchFlipped] |
|
|
|
print("------------") |
|
|
|
|
|
|
|
print(fits) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
picmap = dd(dict) # inp: coord [][] -> pid |
|
|
|
|
|
|
|
coords = dict() # inp: pid -> out: coord (tuple) |
|
|
|
|
|
|
|
|
|
|
|
mapWidth = int(len(pics) ** (1/2)) |
|
|
|
pidList = fits.keys() |
|
|
|
|
|
|
|
firstID = None |
|
|
|
tilemap = np.empty([mapWidth, mapWidth], dtype=str) |
|
|
|
for pid in pidList: |
|
|
|
print("") |
|
|
|
firstID = pid |
|
|
|
print(tilemap) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
aligns = dict() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#for pid, pic in pics.items(): |
|
|
|
|
|
|
|
nextTile = None |
|
|
|
|
|
|
|
prevTile = None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for key in pics.keys(): |
|
|
|
|
|
|
|
nextTile = key |
|
|
|
|
|
|
|
prevTile = key |
|
|
|
|
|
|
|
break |
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# y x |
|
|
|
|
|
|
|
picmap[0][0] = firstID |
|
|
|
|
|
|
|
print(firstID) |
|
|
|
|
|
|
|
|
|
|
|
seenTiles = [] |
|
|
|
for pid, fit in fits.items(): |
|
|
|
loop = False |
|
|
|
if(pid == firstID): |
|
|
|
while(not loop): |
|
|
|
|
|
|
|
fits = getFits(nextTile, prevTile) |
|
|
|
|
|
|
|
aligns[nextTile] = fits |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print(fits) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# prevTile = nextTile |
|
|
|
|
|
|
|
# nextTile = fits[1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# if(nextTile in seenTiles): |
|
|
|
|
|
|
|
# loop = True |
|
|
|
|
|
|
|
# break |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# seenTiles.append(prevTile) |
|
|
|
|
|
|
|
# print(prevTile, nextTile, seenTiles) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print(aligns) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def copyList(ls): |
|
|
|
|
|
|
|
return [elem for elem in ls] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ################ |
|
|
|
|
|
|
|
# exit() |
|
|
|
|
|
|
|
# ################ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def rotateNumTo(numrot, tonum): |
|
|
|
|
|
|
|
tonum = rotateTile(tonum) |
|
|
|
|
|
|
|
# numrot -> tonum |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# get num of rotations clockwise |
|
|
|
|
|
|
|
rots = numrot - tonum |
|
|
|
|
|
|
|
newrot = numrot + rots |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while(newrot < 0): |
|
|
|
|
|
|
|
newrot += 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
newrot = newrot % 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return newrot |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ---------------------------------------------- |
|
|
|
|
|
|
|
# | 0 1 2 3 | |
|
|
|
|
|
|
|
# |[selfSide, matchID, matchSide, matchFlipped]| |
|
|
|
|
|
|
|
# ---------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# for pid, fit in aligns.items(): |
|
|
|
|
|
|
|
# fitid = fit[1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# conRot = fit[0] |
|
|
|
|
|
|
|
# myRot = fit[2] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# newrot = rotateNumTo(myRot, conRot) |
|
|
|
|
|
|
|
# #print(f"{pid=} {fitid=} : {conRot=} {myRot=} : {newrot=}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# aligns[fitid][0] = newrot # make others relative |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print("\n\n----") |
|
|
|
|
|
|
|
rotmap = dd(dict) # inp: coords |
|
|
|
|
|
|
|
rotcoords = dd(tuple) # inp: pid |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
seenpid = [] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i = 0 |
|
|
|
|
|
|
|
for pid, align in aligns.items(): |
|
|
|
|
|
|
|
print(pid, align) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if( i == 0 ): |
|
|
|
|
|
|
|
coords = (0, 0) |
|
|
|
|
|
|
|
rotmap[coords[1]][coords[0]] = pid |
|
|
|
|
|
|
|
rotcoords[pid] = coords |
|
|
|
|
|
|
|
seenpid.append(pid) |
|
|
|
|
|
|
|
continue |
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for pos, fitin in fit.items(): |
|
|
|
|
|
|
|
if(pos != "E"): |
|
|
|
|
|
|
|
fitID, fitDIR = fitin[0], fitin[1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print(f"{pid=} {pos=} : {fitID=} {fitDIR=}") |
|
|
|
selfside = align[0] |
|
|
|
else: |
|
|
|
matchside = align[2] |
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
matchid = align[1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# while(len(rots) < len(pics)): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# for pid, fit in aligns.items(): |
|
|
|
|
|
|
|
# if(len(seenpid) >= len(aligns)): |
|
|
|
|
|
|
|
# break |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# if(pid in seenpid): |
|
|
|
|
|
|
|
# continue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# seenpid.append(pid) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# if(len(rotmap) <= 0): |
|
|
|
|
|
|
|
# rotmap[0][0] = pid |
|
|
|
|
|
|
|
# rots[pid] = (0, 0) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# fitid = fit[1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# conRot = fit[0] |
|
|
|
|
|
|
|
# myRot = fit[2] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# coords = rots[pid] |
|
|
|
|
|
|
|
# print(f"{pid} : {coords} : {fitid} |", end=" ") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# if(len(coords) < 2): |
|
|
|
|
|
|
|
# print("no coords") |
|
|
|
|
|
|
|
# continue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# x, y = coords[0], coords[1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# if(conRot == 0): |
|
|
|
|
|
|
|
# # put the connected one above it |
|
|
|
|
|
|
|
# # x y is reversed because lists index and stuff |
|
|
|
|
|
|
|
# rots[fitid] = (x, y-1) |
|
|
|
|
|
|
|
# print(f"new coord: {rots[fitid]}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# elif(conRot == 1): |
|
|
|
|
|
|
|
# # right of |
|
|
|
|
|
|
|
# rots[fitid] = (x+1, y) |
|
|
|
|
|
|
|
# print(f"new coord: {rots[fitid]}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# elif(conRot == 2): |
|
|
|
|
|
|
|
# # down of |
|
|
|
|
|
|
|
# rots[fitid] = (x, y+1) |
|
|
|
|
|
|
|
# print(f"new coord: {rots[fitid]}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# elif(conRot == 3): |
|
|
|
|
|
|
|
# # left of |
|
|
|
|
|
|
|
# rots[fitid] = (x-1, y) |
|
|
|
|
|
|
|
# print(f"{fitid} new coord: {rots[fitid]}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# print(rots) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# def rotateClock( rots, picRot, face ): |
|
|
|
|
|
|
|
# clock = copyList(picRot) |
|
|
|
|
|
|
|
# clocklen = len(clock) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# facei = face |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# facei += rots |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# while(facei < 0): |
|
|
|
|
|
|
|
# facei += clocklen |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# facei = facei % clocklen |
|
|
|
|
|
|
|
# face = clock[facei] |
|
|
|
|
|
|
|
# return face |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# fitmap = dict() |
|
|
|
|
|
|
|
# for pid, fits in aligns.items(): |
|
|
|
|
|
|
|
# side, fitid, fitside, flipside = fits |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# newside = rotateTile(side, fitside) |
|
|
|
|
|
|
|