#!/usr/bin/env python3 import sys from functools import cache @cache def path(f1,f2): path = [] y, x = f1 while y != 0: y -= 1 path.append((y,x)) while x != f2[1]: x += 1 if f2[1] > x else -1 path.append((y,x)) while y != f2[0]: y += 1 path.append((y,x)) assert path[-1] == f2 return path def pathvalid(path,board): for p in path: if p in board: return False return True def cost(a,w): if a == 'A': return len(w) * 1 if a == 'B': return len(w) * 10 if a == 'C': return len(w) * 100 if a == 'D': return len(w) * 1000 def endstate(board): for f,a in board.items(): if f[1] != target[a]: return False return True def target_position(f,b,lowest): a = b[f] if f[1] != target[a]: return False for y in range(f[0],lowest+1): if (y,target[a]) not in b: return False if b[(y,target[a])] != a: return False return True def totarget(board,lowest): change = False costs = 0 nb = {k: v for k,v in board.items()} for f,a in board.items(): if target_position(f,nb,lowest): continue t = (lowest,target[a]) while t in nb and nb[t] == a: t = (t[0]-1,target[a]) if t == (0,target[a]): continue p = path(f,t) if pathvalid(p,nb): change = True nb.pop(f) nb[t] = a costs += cost(a,p) return nb,costs,change def dfs(board,lowest,costs=0,curmin=sys.maxsize): ch = True nb = {k: v for k,v in board.items()} while ch: nb,co,ch = totarget(nb,lowest) costs += co if endstate(nb): return costs if costs > curmin: return None board = {k: v for k,v in nb.items()} for f,a in board.items(): if f[0] != 0 and not(target_position(f,board,lowest)): for f2 in hallway: p = path(f,f2) if pathvalid(p, board): cb = {k: v for k,v in board.items()} cb.pop(f) cb[f2] = a nc = dfs(cb,lowest,costs+cost(a,p),curmin) if nc is not None and nc < curmin: curmin = nc if curmin < sys.maxsize: return curmin if __name__ == '__main__': # challenge 1 lines = open(sys.argv[1]).readlines() start = {} for y,l in enumerate(lines[1:]): for x,f in enumerate(l[1:]): if f in 'ABCD': start[(y,x)] = f hallway = [(0,0), (0,1), (0,3), (0,5), (0,7), (0,9), (0,10)] target = {'A': 2, 'B': 4, 'C': 6, 'D': 8} res1 = str(dfs(start,2)) print("challenge 1:" + "\n" + res1 + "\n") # challenge 2 lines = open(sys.argv[2]).readlines() start = {} for y,l in enumerate(lines[1:]): for x,f in enumerate(l[1:]): if f in 'ABCD': start[(y,x)] = f res2 = str(dfs(start,4)) print("challenge 2:" + "\n" + res2 + "\n")