|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
from collections import defaultdict as dd
|
|
|
|
from aoc import get_input # AoC
|
|
|
|
import re # regex
|
|
|
|
import numpy as np
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
print(pics["3079"])
|
|
|
|
|
|
|
|
fits = dict()
|
|
|
|
|
|
|
|
# fits struc
|
|
|
|
#
|
|
|
|
# fits[id] = { "L": (otherid, rotation) }
|
|
|
|
# where key = "L", "R", "U", "D", "UL", "UR", "DL", "DR"
|
|
|
|
|
|
|
|
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[0] = compileBorder(pic[0])
|
|
|
|
borders[1] = compileSideBorder(pic, -1)
|
|
|
|
borders[2] = compileBorder(pic[-1]) # up: 0, right: 1, down: 2, left: 3
|
|
|
|
borders[3] = compileSideBorder(pic, 0)
|
|
|
|
|
|
|
|
return borders
|
|
|
|
|
|
|
|
for pid, pic in pics.items():
|
|
|
|
picBorders[pid] = genBorders(pid)
|
|
|
|
|
|
|
|
|
|
|
|
# picmap = dict() # map of the arranged pics
|
|
|
|
|
|
|
|
def rotateTile(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
|
|
|
|
0: 2,
|
|
|
|
1: 3,
|
|
|
|
2: 0,
|
|
|
|
3: 1
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
def getFits(picid, ignoreid):
|
|
|
|
borders = picBorders[picid]
|
|
|
|
|
|
|
|
seenborders = []
|
|
|
|
attachedTiles = []
|
|
|
|
|
|
|
|
# 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
|
|
|
|
matchID = None
|
|
|
|
selfSide = None
|
|
|
|
matchSide = None
|
|
|
|
matchFlipped = False
|
|
|
|
|
|
|
|
prevTile = None
|
|
|
|
|
|
|
|
for side, border in borders.items(): # TODO: make recursive instead and flip (np.fliplr) (and for up-down)
|
|
|
|
# check other borders
|
|
|
|
selfSide = side
|
|
|
|
|
|
|
|
for pid, bor in picBorders.items():
|
|
|
|
if(pid == picid or pid == ignoreid):
|
|
|
|
continue
|
|
|
|
|
|
|
|
# print("Checking", pid, f"{attachedTiles=}")
|
|
|
|
|
|
|
|
|
|
|
|
for pos, line in bor.items(): # top and bottom matching
|
|
|
|
if(not pos in [0, len(bor)-1]):
|
|
|
|
continue
|
|
|
|
|
|
|
|
if(line in seenborders):
|
|
|
|
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
|
|
|
|
attachedTiles.append(matchID)
|
|
|
|
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 ):
|
|
|
|
picBorders[pid][pos] = newborder_right # 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
|
|
|
|
if(match):
|
|
|
|
return [selfSide, matchID, matchSide, matchFlipped]
|
|
|
|
|
|
|
|
|
|
|
|
mapWidth = int(len(pics) ** (1/2))
|
|
|
|
|
|
|
|
tilemap = np.empty([mapWidth, mapWidth], dtype=str)
|
|
|
|
print("")
|
|
|
|
print(tilemap)
|
|
|
|
|
|
|
|
aligns = dict()
|
|
|
|
|
|
|
|
#for pid, pic in pics.items():
|
|
|
|
nextTile = None
|
|
|
|
prevTile = None
|
|
|
|
|
|
|
|
for key in pics.keys():
|
|
|
|
nextTile = key
|
|
|
|
prevTile = key
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
|
|
seenTiles = []
|
|
|
|
loop = False
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
selfside = align[0]
|
|
|
|
matchside = align[2]
|
|
|
|
|
|
|
|
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)
|