|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
from collections import defaultdict as dd
|
|
|
|
from aoc import get_input # AoC
|
|
|
|
import re # regex
|
|
|
|
import numpy as np
|
|
|
|
from copy import deepcopy as copy
|
|
|
|
|
|
|
|
from sys import setrecursionlimit
|
|
|
|
setrecursionlimit(20000)
|
|
|
|
|
|
|
|
data = get_input(20).strip()
|
|
|
|
|
|
|
|
pics = dict()
|
|
|
|
for genpic in data.split("\n\n"):
|
|
|
|
foundTitle = None
|
|
|
|
|
|
|
|
for i, row in enumerate(genpic.splitlines()):
|
|
|
|
if(foundTitle == None):
|
|
|
|
titleCheck = re.match(r"^Tile ([0-9]+):", row)
|
|
|
|
|
|
|
|
if(titleCheck != None):
|
|
|
|
foundTitle = titleCheck[1]
|
|
|
|
pics[foundTitle] = []
|
|
|
|
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
pics[foundTitle].append( list(row) )
|
|
|
|
except:
|
|
|
|
pics[foundTitle] = []
|
|
|
|
pics[foundTitle].append( list(row) )
|
|
|
|
|
|
|
|
|
|
|
|
for pid, pic in pics.items():
|
|
|
|
pics[pid] = np.asarray(pic)
|
|
|
|
|
|
|
|
|
|
|
|
mapWidth = len(pics) ** (1/2)
|
|
|
|
|
|
|
|
def compileBorder(row):
|
|
|
|
out = ""
|
|
|
|
for char in row:
|
|
|
|
out += char
|
|
|
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
def compileSideBorder(pic, xOffset=0):
|
|
|
|
out = ""
|
|
|
|
for row in pic:
|
|
|
|
out += row[xOffset]
|
|
|
|
|
|
|
|
return out
|
|
|
|
|
|
|
|
|
|
|
|
# check 4 border locations, up, down, left, right
|
|
|
|
picBorders = dict()
|
|
|
|
def genBorders(picid):
|
|
|
|
pic = pics[picid]
|
|
|
|
|
|
|
|
## compile its borders
|
|
|
|
borders = dict()
|
|
|
|
borders["U"] = compileBorder(pic[0])
|
|
|
|
borders["R"] = compileSideBorder(pic, -1)
|
|
|
|
borders["D"] = compileBorder(pic[-1]) # up: 0, right: 1, down: 2, left: 3
|
|
|
|
borders["L"] = compileSideBorder(pic, 0)
|
|
|
|
|
|
|
|
return borders
|
|
|
|
|
|
|
|
for pid, pic in pics.items():
|
|
|
|
picBorders[pid] = genBorders(pid)
|
|
|
|
|
|
|
|
|
|
|
|
# picmap = dict() # map of the arranged pics
|
|
|
|
|
|
|
|
def flip(otherside):
|
|
|
|
# 0 up, 1 right, 2 down, 3 left
|
|
|
|
|
|
|
|
sidething = { # used to rotate as the rotation is allways the opposite relative to the whole system
|
|
|
|
"U": "D",
|
|
|
|
"R": "L",
|
|
|
|
"D": "U",
|
|
|
|
"L": "R"
|
|
|
|
}
|
|
|
|
|
|
|
|
newotherside = sidething[otherside]
|
|
|
|
|
|
|
|
# update the connected tiles rotation
|
|
|
|
|
|
|
|
return newotherside
|
|
|
|
|
|
|
|
def borderCheck(border, other):
|
|
|
|
other_rev = other[::-1]
|
|
|
|
|
|
|
|
if( border == other or border == other_rev ):
|
|
|
|
return True, border
|
|
|
|
else:
|
|
|
|
return False, other
|
|
|
|
|
|
|
|
fitsDict = dd(dict)
|
|
|
|
|
|
|
|
# fits struc
|
|
|
|
#
|
|
|
|
# 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
|
|
|
|
|
|
|
|
|
|
|
|
def getFits(borders, fits=fitsDict, ignore=[]):
|
|
|
|
#print(fits)
|
|
|
|
#print("\n#", borders, "\n")
|
|
|
|
ignoreLen = len(ignore)
|
|
|
|
borLen = len(borders)
|
|
|
|
|
|
|
|
print(ignoreLen, borLen)
|
|
|
|
|
|
|
|
if(ignoreLen >= borLen):
|
|
|
|
print("goodbye")
|
|
|
|
return fits # if there is nothing to do then return the result
|
|
|
|
|
|
|
|
newborders = copy(borders)
|
|
|
|
for pid, border in borders.items(): # borders[pid]
|
|
|
|
#print(f"\nChecking {pid=}")
|
|
|
|
if( pid in ignore ):
|
|
|
|
continue
|
|
|
|
|
|
|
|
for pos, bor in border.items(): # check each border : borders[pid][pos]
|
|
|
|
|
|
|
|
print(f"##Border {pid=} {pos=} {bor=}")
|
|
|
|
|
|
|
|
for pid2, border2 in borders.items(): # check for others borders ; borders[pid2]
|
|
|
|
if(pid2 == pid or pid2 in ignore):
|
|
|
|
continue
|
|
|
|
|
|
|
|
for pos2, bor2 in border2.items(): # check for other matching border ; borders[pid2][pos2]
|
|
|
|
|
|
|
|
check, newBor2 = borderCheck(bor, bor2)
|
|
|
|
print(f"####Border2 {pid2=} {pos2=} {bor2=} {newBor2=} {check=}")
|
|
|
|
|
|
|
|
if(check): # if the two borders match:
|
|
|
|
fits[pid] = dict()
|
|
|
|
fits[pid][pos] = (pid2, pos2)
|
|
|
|
|
|
|
|
ignore.append(pid)
|
|
|
|
|
|
|
|
fits[pid] = fits[pid] or {"E": None} # It has to end somewhere
|
|
|
|
|
|
|
|
return getFits(newborders, fits, ignore) # do the other borders
|
|
|
|
|
|
|
|
print(picBorders)
|
|
|
|
|
|
|
|
fits = getFits(picBorders, fitsDict)
|
|
|
|
print("------------")
|
|
|
|
print(fits)
|
|
|
|
|
|
|
|
picmap = dd(dict) # inp: coord [][] -> pid
|
|
|
|
coords = dict() # inp: pid -> out: coord (tuple)
|
|
|
|
|
|
|
|
pidList = fits.keys()
|
|
|
|
firstID = None
|
|
|
|
for pid in pidList:
|
|
|
|
firstID = pid
|
|
|
|
break
|
|
|
|
|
|
|
|
# y x
|
|
|
|
picmap[0][0] = firstID
|
|
|
|
print(firstID)
|
|
|
|
|
|
|
|
for pid, fit in fits.items():
|
|
|
|
if(pid == firstID):
|
|
|
|
continue
|
|
|
|
|
|
|
|
for pos, fitin in fit.items():
|
|
|
|
if(pos != "E"):
|
|
|
|
fitID, fitDIR = fitin[0], fitin[1]
|
|
|
|
|
|
|
|
print(f"{pid=} {pos=} : {fitID=} {fitDIR=}")
|
|
|
|
else:
|
|
|
|
continue
|