There’s a security guard and we want to track where they are walking.
Predict the path of the guard. How many distinct positions will the guard visit before leaving the mapped area?
import numpy as npimport pandas as pdfrom copy import deepcopywithopen('data-2024-06.txt', 'r') as f: inp = f.read().splitlines()grid = [list(x) for x in inp]walking = np.array(grid)np.where(walking =='^')len(walking)len(walking[0])
130
Iteration, I guess. Make sure we keep track of the direction the guard is walking.
# make some functions to move and check def move_me(dir, a, b):ifdir=='up': a -=1elifdir=='right': b +=1elifdir=='down': a +=1elifdir=='left': b -=1return a,bdef turn_me(dir):ifdir=='up': return'right'elifdir=='right':return'down'elifdir=='down':return'left'elifdir=='left':return'up'def able_to_move(walking, dir, a, b):ifdir=='up'and walking[a-1,b] =='#':returnFalseelifdir=='right'and walking[a,b+1] =='#':returnFalseelifdir=='down'and walking[a+1,b] =='#':returnFalseelifdir=='left'and walking[a,b-1] =='#':returnFalseelse: returnTruedef check_edges(dir, a, b):ifdir=='up'and a ==0:returnTrueelifdir=='right'and b ==129:returnTrueelifdir=='down'and a ==129:returnTrueelifdir=='left'and b ==0:returnTruedir='up'a,b =60,60counter = np.zeros([130,130], dtype =int)counter[a,b] =1stopper =0whileTrue: # safety so we don't get in an infinite loop stopper +=1if stopper ==100000: break# if we can move then do itif able_to_move(walking, dir, a, b):# print('moving') a,b = move_me(dir, a, b)# if we cannot then we should turnelse: # print('turning')dir= turn_me(dir) a,b = move_me(dir, a, b)# iterate our counter counter[a,b] =1# also check if the guard is going off the mapif check_edges(dir, a, b):breaknp.sum(counter)# 4789 too high# 4662 too low# 4663 move the counter iterationdf = pd.DataFrame(counter)df.to_csv("guard-path.csv", header=False, index=False)
— Part Two —
We should be able to place an object to make the guard walk in a loop.
You need to get the guard stuck in a loop by adding a single new obstruction. How many different positions could you choose for this obstruction?
The obstructions could only be on the path that the guard walks on, otherwise it has no effect. Maximum possibilities is 4663. That is not too large to iterate, test each one. How do we test for a loop? It isn’t just the starting position, since the loop could begin after and not include the start. If the guard does not go off the map then it must be a loop.
# reuse the same setup but record the locations of the path# then use that list to place obstructionsdir='up'a,b =60,60counter = np.zeros([130,130], dtype =int)counter[a,b] =1stopper =0guard_path = []guard_path.append([a,b])whileTrue: # safety so we don't get in an infinite loop stopper +=1if stopper ==100000: break# if we can move then do itif able_to_move(walking, dir, a, b):# print('moving') a,b = move_me(dir, a, b)# if we cannot then we should turnelse: # print('turning')dir= turn_me(dir) a,b = move_me(dir, a, b)# iterate our counter counter[a,b] =1 guard_path.append([a,b])# also check if the guard is going off the mapif check_edges(dir, a, b):break# guard_pathlen(guard_path)guard_path = guard_path[1:]guard_tuple = [tuple(x) for x in guard_path]guard_set =set(guard_tuple)guard_path = [list(x) for x in guard_set]len(guard_path)
4662
Convert the list of positions into a set of positions, since we don’t want to consider the same location twice, and we can’t use the starting position.
I am also going to create a function for the walking of the guard since that makes the most sense.
def check_for_loop(obs):# variables for starting dir='up' a,b =60,60 stopper =0 loop =TruewhileTrue: # safety so we don't get in an infinite loop# I do not have well defined stopping criteria so this is it stopper +=1if stopper ==10000: break# if we can move then do itif able_to_move(obs, dir, a, b): a,b = move_me(dir, a, b)# if we cannot then we should turnelse: dir= turn_me(dir) a,b = move_me(dir, a, b)# check if the guard is going off the mapif check_edges(dir, a, b): loop =Falsebreak# print(stopper)# after we break return loop or noreturn loop
Now we can do our assessment
loops =0for g in guard_path[:]:# create a new obstruction map obs = deepcopy(walking)# add a new obstruction obs[g[0],g[1]] ='#'# do the walking assessmentif check_for_loop(obs): loops +=1loops# 4375 too high# 1430 too low