AoC 2024 day 20

 avatar
unknown
python
2 months ago
3.4 kB
6
Indexable
P=puzzle.splitlines()
D=[-1j,1,1j,-1]                                                 # move directions
F=set()                                                         # set of free positions
for j,r in enumerate(P):                                        # for each row
    for i,c in enumerate(r):                                    # for each column
        p=i+1j*j                                                # position in the maze
        if c=='S': S=p                                          # start position
        if c=='E': E=p                                          # end position
        if c!='#': F.add(p)                                     # memorize free positions

def maze():                                                     # find the shortest path from start to end
    q=[(0,[S])]; bc=1e9; v=set()                                # queue the start cost/path, best cost, visited positions
    while(q):                                                   # while unexplored positions remain
        c,pp=q.pop(0);p=pp[-1]                                  # unqueue the 1st cost/path for exploration, last position
        if c<bc:                                                # no exploration needed if cost is already higher than best
            if p==E: return(pp)                                 # if we reached the end, return the path solution
            for np in [p+d for d in D]:                         # else check the neighbour positions
                if np in F and np not in v:                     # for the free ones not already visited
                    v.add(np);q.append((c+1,pp+[np]))           # mark them as visited and queue them for later exploration

def cheat(cd,sv):                                               # how many ways to save time (sv) if collision disabled steps (cd) are allowed
    pl=maze(); ch=0                                             # resolve the path list of the maze
    pi=dict({p:i for i,p in enumerate(pl)})                     # position index for each path step
    for p,n in pi.items():                                      # for each path position from start
        for dj in range(-cd,cd+1):                              # for each next position within a Manhattan distance equal to cd
            adj=abs(dj)                                         # such positions are in a diamond shape around the current position
            for di in range(adj-cd,cd+1-adj):                   # typically, if  -d<= dy <= d , then  |y|-d <= dx <= d-|y| 
                adi=abs(di); np=p+di+1j*dj; md=adi+adj          # next position and its Manhattan distance from current position
                if md>1 and np in pi:                           # need to move at least 2 steps to cross a wall, else there would be no cheating
                    if pi[np]-n-md>=sv: ch+=1                   # count the nbr of cheating path that saves us enough time, from this position
    return(ch)                                                  # display the total number

def part1():
    print(cheat(2,1 if _F==1 else 100))                         # nbr of cheats saving min 100 picosec, if collision is disabled during max 2 steps

def part2():
    print(cheat(20,50 if _F==1 else 100))                       # nbr of cheats saving min 100 picosec, if collision is disabled during max 20 steps
Leave a Comment