You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
6.8 KiB
298 lines
6.8 KiB
#!/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 = int(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=[], ignoreBorder=[]):
|
|
#print(fits)
|
|
#print("\n#", borders, "\n")
|
|
ignoreLen = len(ignoreBorder)
|
|
borLen = len(borders)
|
|
|
|
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 or border2 in ignoreBorder):
|
|
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( not check ):
|
|
continue
|
|
|
|
if(check): # if the two borders match:
|
|
fits[pid] = fits[pid] or dict()
|
|
fits[pid][pos] = [pid2, pos2]
|
|
|
|
#ignore.append(pid)
|
|
ignoreBorder.append(bor2)
|
|
|
|
fits[pid] = fits[pid] or {"E": None} # It has to end somewhere
|
|
|
|
return getFits(newborders, fits, ignore) # do the other borders
|
|
|
|
|
|
fits = getFits(picBorders, fitsDict)
|
|
|
|
|
|
fitsListKeys = list(fits.keys())
|
|
|
|
|
|
|
|
#picmap = [["#" for x in range(mapWidth)] for y in range(mapWidth)] # inp: coord [][] -> pid
|
|
picmap = dd(dict)
|
|
piccoords = dict() # inp: pid -> out: coord (tuple)
|
|
|
|
seenPics = []
|
|
|
|
def translatePos(pos, x, y):
|
|
if(pos == "U"):
|
|
return x, y-1
|
|
elif(pos == "D"):
|
|
return x, y+1
|
|
elif(pos == "L"):
|
|
return x-1, y
|
|
elif(pos == "R"):
|
|
return x+1, y
|
|
|
|
|
|
|
|
def setPos(pid, x, y, pmap, coords, seen=[]):
|
|
pmap[y] = pmap[y] or dict()
|
|
pmap[y][x] = pid
|
|
|
|
coords[pid] = (x, y)
|
|
seen.add(pid)
|
|
return seen
|
|
|
|
def getPos(x, y, pmap):
|
|
try:
|
|
pid = pmap[y][x]
|
|
if( pid == "####" ):
|
|
return None
|
|
else:
|
|
return pid
|
|
except:
|
|
return None
|
|
|
|
def printMap(pmap):
|
|
for y in range(mapWidth):
|
|
row = pmap[y]
|
|
|
|
for x in range(mapWidth):
|
|
try:
|
|
print( row[x], end=" " )
|
|
except:
|
|
print("####", end=" ")
|
|
|
|
print("\n")
|
|
|
|
def findContainer(sid, fits):
|
|
foundContainers = []
|
|
for pid, fit in fits.items():
|
|
for f in fit.values():
|
|
if(sid in f):
|
|
foundContainers.append(pid)
|
|
|
|
return foundContainers
|
|
|
|
|
|
corners = []
|
|
cornersPos = dict()
|
|
for pid, cont in fits.items():
|
|
print(pid, cont)
|
|
if(len(cont) == 2):
|
|
corners.append(pid)
|
|
|
|
|
|
prod = 1
|
|
for c in corners:
|
|
prod *= int(c)
|
|
|
|
print("Part1:", prod)
|
|
|
|
print("\n\n")
|
|
|
|
|
|
seenid = set()
|
|
|
|
# know the corners
|
|
# start from corner 1 then 2, then update 3 and 4
|
|
#
|
|
# 1---------2
|
|
# | |
|
|
# | stuff |
|
|
# | here |
|
|
# | |
|
|
# 3---------4
|
|
|
|
# append the first corner at 0, 0
|
|
|
|
mapW = mapWidth - 1
|
|
|
|
for y in range(mapWidth):
|
|
for x in range(mapWidth):
|
|
picmap[y][x] = "####"
|
|
|
|
seenid = setPos(corners[0], 0, 0, picmap, piccoords, seenid) # some corner
|
|
print("####", corners)
|
|
|
|
i = 0
|
|
|
|
def getRotIndex(rot):
|
|
return ["U", "R", "D", "L"].index(rot)
|
|
|
|
def rotateDirs(pid, rot, fits):
|
|
irot = getRotIndex(rot)
|
|
cont = fits[pid]
|
|
|
|
for pos, stuff in cont.items():
|
|
ipos = getRotIndex(pos)
|
|
diff = ipos - irot
|
|
|
|
|
|
def genPicMap(pmap=picmap, pcoords=piccoords, fits=fits, seenid=set()):
|
|
for y in range(mapWidth):
|
|
for x in range(mapWidth):
|
|
pidAtPos = getPos(x, y, pmap)
|
|
|
|
if(pidAtPos):
|
|
fit = fits[pidAtPos] # possible connections
|
|
print(f"\n{pidAtPos}: {fit}")
|
|
|
|
for pos, child in fit.items():
|
|
if(child[0] in seenid):
|
|
continue
|
|
|
|
nx, ny = translatePos(pos, x, y)
|
|
pidInSpace = getPos(nx, ny, pmap) # check if there is something there
|
|
print(f"{pos=} {child=} : {pidInSpace=} {nx,ny=}")
|
|
|
|
if(pidInSpace or nx < 0 or ny < 0):
|
|
continue
|
|
|
|
|
|
seenid = setPos(child[0], nx, ny, pmap, pcoords, seenid)
|
|
|
|
genPicMap()
|
|
|
|
|
|
print("")
|
|
printMap(picmap)
|
|
|