def run_main(): args = get_args() prog = get_prog(args) icn = IntcodeComputerNode("ENIAC", prog, []) # I don't think there is a strategy that is guaranteed to win based on what we know # For example consider: # ###.#.#...# # ###.#...#.# # In the first case, you have to jump at step 3 or you lose # In the second case, you have to jump at step 1 or you lose # But it looks the same from the beginning. No way to tell them apart. # So no strategy is guaranteed to work. # A good solution would be something like: figure out rules which allow us to greedily go forward # until we have rules that are *good enough* to get us to the end. # Or, we can try a heuristic and go from there. Opting for this. # So, heuristic strategy: jump if ### 4 ahead is hull AND 3 ahead is not hull, OR 1 ahead is not hull # sure, we can refine from there as long as we take care to visualize the output. ss = ["NOT C J\n", "AND D J\n", "NOT A T\n", "OR T J\n", "WALK\n"] ss = [ord(v) for v in "".join(ss)] # print(ss) icn.inputs = ss icn.run_prog_from_current_state() outs = icn.outputs print(f"The outputs are: {outs}") ### Output a bunch of ascii-range integers and then a single giant integer which worked. ### Great but not the most satisfying I guess? return 0
def run_main(): args = get_args() prog = get_prog(args) inputs=[1] icn = IntcodeComputerNode("A", prog, inputs) icn.run_prog_from_current_state() print(icn.outputs) return 0
def run_main(): args = get_args() X_SIZE = 100 Y_SIZE = 60 X_COORD = int(X_SIZE / 2) Y_COORD = int(Y_SIZE / 2) base_grid_row = ['.'] * X_SIZE base_grid = np.asarray([base_grid_row for i in range(Y_SIZE)]) print(base_grid) prog = get_prog(args) icn = IntcodeComputerNode("A", prog, []) robot = HullPaintingRobot("JOE", X_COORD, Y_COORD, base_grid, icn) robot.paintGrid[Y_COORD, X_COORD] = robot.WHITE robot.paint() grid_out = np.asarray( [getchar(x, robot) for x in robot.paintGrid.flatten()]) grid_out = grid_out.reshape((Y_SIZE, X_SIZE)) print(grid_out.shape) print(grid_out[:5, :5]) np.savetxt("./out.txt", grid_out, fmt="%s") print(f"Painted at least once: {len(robot.paintedSet)}") return 0
def run_main(): args = get_args() prog = get_prog(args) icn = IntcodeComputerNode("A", prog, []) icn.run_prog_from_current_state() debug(''.join([chr(v) for v in icn.outputs])) s = ''.join([chr(v) for v in icn.outputs]) arr = [v for v in s.split('\n') if len(v.strip()) > 0] arr = np.asarray([list(s) for s in arr]) disp_board(arr) debug(arr.shape) XX = range(1, (arr.shape[1] - 1)) YY = range(1, (arr.shape[0] - 1)) intersections = [] for y in YY: for x in XX: if is_crossing(arr, y, x): intersections.append((x, y)) total = 0 params = [v[0] * v[1] for v in intersections] total = sum(params) print(f"intersections: {intersections}") print(f"{len(intersections)} intersections") print(f"parameters: {params}") print(f"{len(params)} parameters") print(f"The total is {total}") return 0
def get_board(args): f = open(args.infile, 'r') lines = f.readlines() f.close() lines = [line.strip() for line in lines] lines = [line for line in lines if len(line) > 0] prog = "".join(lines) prog = [int(v) for v in prog.split(",")] icn = IntcodeComputerNode("A", prog, []) icn.run_prog_from_current_state() outs = icn.outputs idx = 0 triples = [] while idx + 3 < len(outs): triples.append( (int(outs[idx]), int(outs[idx + 1]), int(outs[idx + 2]))) idx += 3 L = len(triples) board_spec = np.zeros(shape=(L, 3), dtype=int) for i in range(len(triples)): t = triples[i] x = t[0] # offset from left y = t[1] # offset from top b = t[2] # board element board_spec[i, 0] = x board_spec[i, 1] = y board_spec[i, 2] = b return (board_spec)
def run_main(): args = get_args() prog = get_prog(args) icn = IntcodeComputerNode("ENIAC", prog, []) XX = 50 YY = 50 arr = np.zeros(shape=(XX, YY)) out_ptr = 0 for y in range(YY): for x in range(XX): # icn.run_prog_from_current_state() # print(icn.outputs) icn.reset() icn.inputs = [y, x] # print(f"Running with inputs: {icn.inputs}") icn.run_prog_from_current_state() # print(f"All outputs: {icn.outputs}") # print(f"Exit status: {icn.has_exited}") arr[y, x] = icn.outputs[out_ptr] # exit() # out_ptr += 1 # print(arr) # exit() for y in range(YY): row = "".join([str(int(v)) for v in arr[y, :]]) # print(row) row = row.replace('1', '#').replace('0', '.') print(row) total = np.sum(arr.flatten()) print(f"Total number of affected spots: {total}") return 0
def run_main(): global ball_loc global paddle ball_loc["current"] = (-1, -1) # initialize args = get_args() prog = get_prog(args) icn = IntcodeComputerNode("A", prog, []) icn.run_prog_from_current_state() board_spec = output_to_board_spec(icn.outputs) L = len(icn.outputs) board = init_board(board_spec) ball_loc["previous"] = ball_loc["current"] # initialize "previous" while not icn.has_exited: # disp_board(board) # joystick_instr = get_process_user_input() # too slow! joystick_instr = get_auto_input() icn.inputs.append(joystick_instr) icn.run_prog_from_current_state() # print(f"BALL: {ball_loc['current']}, PADDLE: {paddle}") board_spec = output_to_board_spec(icn.outputs[L:]) # print(icn.outputs[L:]) # print(board_spec) L = len(icn.outputs) update_board(board_spec, board) return 0
def run_main(): global ROBOT_CHAR global UNEXPLORED_CHAR args = get_args() prog = get_prog(args) D = 50 board = np.asarray([[UNEXPLORED_CHAR] * D] * D) robot = dict() robot['x'] = int(D / 2) robot['y'] = int(D / 2) robot["path"] = [np.asarray([robot['x'], robot['y']])] start_x = robot['x'] start_y = robot['y'] start = np.asarray([start_x, start_y]).reshape((1, 2)) # start position board[robot['y'], robot['x']] = ROBOT_CHAR count = 0 debug(f"Board status move {count}:") disp_board(board) icn = IntcodeComputerNode("A", prog, []) debug(f"Getting user input:") # user_instr = get_process_user_input() user_instr = auto_get_input_explore_heuristic(board, robot, start) debug(f"Updating robot orientation") update_robot_orientation(robot, user_instr) debug(f"Updating intcodeComputerNode inputs with {user_instr}") icn.inputs.append(user_instr) L = len(icn.outputs) disp_board(board) debug(f"Beginning the loop!") while not icn.has_exited: count += 1 debug(f"Count incremented to {count}") debug(f"Running intcodeComputerNode") icn.run_prog_from_current_state() debug(f"Ran intcodeComputerNode, updating board") update_board(board, robot, icn.outputs[L:]) debug(f"Updated board.") L = len(icn.outputs) debug(f"Board status move {count}:") # disp_board(board) debug(f"Getting user input:") # user_instr = get_process_user_input() try: user_instr = auto_get_input_explore_heuristic(board, robot, start) except: # not the most elegent but whatever... break debug(f"Updating robot orientation") update_robot_orientation(robot, user_instr) debug(f"Updating intcodeComputerNode inputs with {user_instr}") icn.inputs.append(user_instr) # print(f"IntcodeComputerNode EXITED: {icn.has_exited}") board[start_y, start_x] = START_CHAR board[oxygen_y, oxygen_x] = OXYGEN_CHAR disp_board(board) print(f"Started at: {start_x}, {start_y}") print(f"Oxygen at: {oxygen_x}, {oxygen_y}") # Now: simulate the oxygen expansion with a sort of BFS target_count = 0 target_set = set([START_CHAR, OXYGEN_CHAR, ROBOT_CHAR, EMPTY_CHAR]) for char in board.flatten(): if char in target_set: target_count += 1 global OXYGEN_GAS_CHAR board[oxygen_y, oxygen_x] = OXYGEN_GAS_CHAR s = set([(oxygen_x, oxygen_y)]) minutes = 0 while True: new_s = set() for n in s: neighbors = get_neighbors(n, board) for neighbor in neighbors: x = neighbor[0] y = neighbor[1] if board[y, x] != OXYGEN_GAS_CHAR: board[y, x] = OXYGEN_GAS_CHAR new_s.add(neighbor) minutes += 1 ox_count = np.sum(board.flatten() == OXYGEN_GAS_CHAR) print(f"ox_count is {ox_count}") if ox_count >= target_count: break s = new_s print(f"Done. ox_count is {ox_count} and target_count is {target_count}") print(f"The full spread took {minutes} minutes") return 0
def run_main(): global ROBOT_CHAR global UNEXPLORED_CHAR args = get_args() prog = get_prog(args) D = 50 board = np.asarray([[UNEXPLORED_CHAR] * D] * D) robot = dict() robot['x'] = int(D / 2) robot['y'] = int(D / 2) robot["path"] = [np.asarray([robot['x'], robot['y']])] start_x = robot['x'] start_y = robot['y'] start = np.asarray([start_x, start_y]).reshape((1, 2)) # start position board[robot['y'], robot['x']] = ROBOT_CHAR count = 0 debug(f"Board status move {count}:") disp_board(board) icn = IntcodeComputerNode("A", prog, []) debug(f"Getting user input:") # user_instr = get_process_user_input() user_instr = auto_get_input_explore_heuristic(board, robot, start) debug(f"Updating robot orientation") update_robot_orientation(robot, user_instr) debug(f"Updating intcodeComputerNode inputs with {user_instr}") icn.inputs.append(user_instr) L = len(icn.outputs) disp_board(board) debug(f"Beginning the loop!") while not icn.has_exited: count += 1 debug(f"Count incremented to {count}") debug(f"Running intcodeComputerNode") icn.run_prog_from_current_state() debug(f"Ran intcodeComputerNode, updating board") update_board(board, robot, icn.outputs[L:]) debug(f"Updated board.") L = len(icn.outputs) debug(f"Board status move {count}:") # disp_board(board) debug(f"Getting user input:") # user_instr = get_process_user_input() try: user_instr = auto_get_input_explore_heuristic(board, robot, start) except: # not the most elegent but whatever... break debug(f"Updating robot orientation") update_robot_orientation(robot, user_instr) debug(f"Updating intcodeComputerNode inputs with {user_instr}") icn.inputs.append(user_instr) # print(f"IntcodeComputerNode EXITED: {icn.has_exited}") board[start_y, start_x] = START_CHAR board[oxygen_y, oxygen_x] = OXYGEN_CHAR disp_board(board) print(f"Started at: {start_x}, {start_y}") print(f"Oxygen at: {oxygen_x}, {oxygen_y}") print(f"Beginning A* search") best_path = A_Star((start_x, start_y), (oxygen_x, oxygen_y), board) if not best_path: print(f"Got: {best_path}") return 0 print(f"Beginning of best path: {best_path[:5]}...") print(f"End of best path: {best_path[-5:]}...") print(f"Length of best path: {len(best_path)}") print(f"The number of steps is one fewer: {len(best_path)-1}") # Now we have the graph, we don't need the intcode computer anymore at all. # Can just do straight A* return 0
def run_main(): args = get_args() prog = get_prog(args) icn = IntcodeComputerNode("ENIAC", prog, []) # Unlike in part 1 we might now have enough information to know what to do each time. # For example consider: # ###.#.#...# # ###.#...#.# # Now that we can see 9 tiles ahead, we know in part 1 we have to wait to jump, and in 2 we can't wait. # Now how can we translate this into spring code. Well, start with logic. # if 9 ahead is space, cant jump at 5 # if 8 ahead is space, cant jump at 4 # so, don't want to land at 4 unless there is a good jump after: # land at 4 if 4 hull AND: # 3 empty, AND # 8 hull, OR # 5 hull AND (9 hull OR (6 hull and 7 hull)) # OR 1 empty (no matter what) # eh still not perfect but better? # ((2 space and 5 space and 1 hull) OR...: NOT B T, NOT E J, AND J T, AND A T # always avoid a suicide jump next step at 1 # 6 hull and 7 hull: NOT F J, NOT J J, AND G J # result is in J # 9 hull OR that: OR I J # result is in J # 5 hull AND that: AND E J # result is in J # 8 hull OR that: OR H J # result in J # 4 hull AND that: AND D J # OR T J # finished OR from the very first step # 1 empty OR that: NOT A T, OR T J # always jump if we're about to die # that is 14. # ss = ["NOT F J\n","NOT J J\n", "AND G J\n", # "OR I J\n", # "AND E J\n", # "OR H J\n", # "NOT C T\n", "AND T J\n", # "AND D J\n", # "NOT A T\n", "OR T J\n", "RUN\n"] # ss = ["NOT B T\n","NOT E J\n", "AND J T\n", "AND A T\n", # "NOT F J\n", "NOT J J\n", "AND G J\n", # "OR I J\n", # "AND E J\n", # "OR H J\n", # "AND D J\n", # "OR T J\n", # "NOT A T\n", "OR T J\n", "RUN\n"] # ss = [ # "NOT F J\n", "NOT J J\n", # (((6 hull # "OR I J\n", # or 9 hull) # "AND E J\n", # and 5 hull) # "OR H J\n", # or 8 hull) # "AND D J\n", # and 4 hull # "NOT C T\n", "AND T J\n", # and 3 space # "NOT B T\n", # "AND E T\n", "NOT T T\n", # "AND A J\n", # "NOT A T\n", "OR T J\n", "RUN\n"] ############################################### #### ALRIGHT LOTS OF FALSE STARTS ABOVE but finally got something working: ss = [ "NOT B T\n", "NOT E J\n", "AND J T\n", "AND A T\n", # (if 2 space and 5 space and 1 hull, "NOT C J\n", "OR J T\n", # OR 3 space: TRUE -> T) "NOT E J\n", "NOT J J\n", # (5 hull "OR H J\n", # or 8 hull) "AND D J\n", # and 4 hull: TRUE -> J "AND T J\n" # T and J -> J "NOT A T\n", "OR T J\n", "RUN\n" ] # if 1 is space or J: J true # In english: # Jump if: # it would be a safe-ish jump (4 is hull AND 5 or 8 is hull), AND either # the next step leads to a suicide jump (2 space, 5 space, 1 hull) OR # 3 is a space # OR: 1 is a space (have to jump) ss = [ord(v) for v in "".join(ss)] # print(ss) icn.inputs = ss icn.run_prog_from_current_state() outs = icn.outputs print(f"The outputs are: {outs}") if outs[-1] <= 127: tumbled = "".join([chr(v) for v in outs]) print(tumbled) ### Output a bunch of ascii-range integers and then a single giant integer which worked. ### Cool. Not bad heuristic-finding exercise. Wonder if there was a more "code" way to do it. ### Some reddit solutions talk about SAT machines which could be entertaining. return 0
def run_main(): args = get_args() prog = get_prog(args) icn = IntcodeComputerNode("ENIAC", prog, []) # record_d = dict() SQ_SZ = 100 y = 15 # initialize while (True): print(f"y is {y}") # binary search for biggest column index for which the beam takes effect (output 1) min_x = y max_x = 10 * y x = y v = run_with_inputs_get_output(icn, [y, x]) while (True): if v == 1: min_x = x last_x = x x = int((x + max_x) / 2) elif v == 0: max_x = x last_x = x x = int((x + min_x) / 2) if abs(x - last_x) == 0: break # make sure while run_with_inputs_get_output(icn, [y, x]) == 0: x -= 1 # really got x now. now test: pros_y = y + (SQ_SZ - 1) pros_x = x - (SQ_SZ - 1) print(f"for y={y}, testing pros_x, pros_y = {pros_x}, {pros_y}") if pros_x < 0: pass else: v = run_with_inputs_get_output(icn, [pros_y, pros_x]) if v == 1: final_y = y final_x = pros_x verdict = int(10000 * final_x + final_y) print(f"final x, y: {final_x}, {final_y}") print(f"Verdict is {verdict}") exit() y += 1 #Ultimately got: # y is 948 # for y=948, testing pros_x, pros_y = 761, 1047 # final x, y: 761, 948 # Verdict is 7610948. WRONG, too low. # Just checking do we have x, y backwards: verdict would be: 9480761 # Yes! that was it. Not sure how I should have known that, but there we go. return 0
def run_main(): args = get_args() prog = get_prog(args) ### Path based on all that other mess below (see run_main_dev()): # main: A,B,A,C,A,B,C,C,A,B # A: R,8,L,10,R,8 # B: R,12,R,8,L,12 # C: L,12,L,10,L,8 main_routine = list("A,B,A,C,A,B,C,C,A,B\n") A = list("R,8,L,10,R,8\n") B = list("R,12,R,8,L,8,L,12\n") C = list("L,12,L,10,L,8\n") video = list("y\n") # print(f"main_routine: {main_routine}") # print(f"A: {A}") # print(f"B: {B}") # print(f"C: {C}") # print(f"video: {video}") main_routine = [ord(x) for x in main_routine] A = [ord(x) for x in A] B = [ord(x) for x in B] C = [ord(x) for x in C] video = [ord(x) for x in video] # print(f"main_routine: {main_routine}") # print(f"A: {A}") # print(f"B: {B}") # print(f"C: {C}") # print(f"video: {video}") prog[0] = 2 icn = IntcodeComputerNode("ENIAC", prog, []) out_ptr = 0 print("Running program:") icn.run_prog_from_current_state() # print(f"icn exited: {icn.has_exited}") print(''.join([chr(v) for v in icn.outputs[out_ptr:]])) out_ptr = len(icn.outputs) # s = ''.join([chr(v) for v in icn.outputs]) # arr = [v for v in s.split('\n') if len(v.strip())>0] # arr = np.asarray([list(s) for s in arr]) # disp_board(arr) print(f"Adding main to input: {main_routine}") for v in main_routine: icn.inputs.append(v) print("Running program:") icn.run_prog_from_current_state() print(''.join([chr(v) for v in icn.outputs[out_ptr:]])) out_ptr = len(icn.outputs) print(f"Adding A to input: {A}") for v in A: icn.inputs.append(v) print("Running program:") icn.run_prog_from_current_state() print(''.join([chr(v) for v in icn.outputs[out_ptr:]])) out_ptr = len(icn.outputs) print(f"Adding B to input: {B}") for v in B: icn.inputs.append(v) print("Running program:") icn.run_prog_from_current_state() print(''.join([chr(v) for v in icn.outputs[out_ptr:]])) out_ptr = len(icn.outputs) print(f"Adding C to input: {C}") for v in C: icn.inputs.append(v) print("Running program:") icn.run_prog_from_current_state() print(''.join([chr(v) for v in icn.outputs[out_ptr:]])) out_ptr = len(icn.outputs) print(f"Adding video to input: {video}") for v in video: icn.inputs.append(v) print("Running program:") icn.run_prog_from_current_state() print(''.join([chr(v) for v in icn.outputs[out_ptr:]])) out_ptr = len(icn.outputs) print(f"Final output: {icn.outputs[-1]}") return 0
def run_main_dev(): args = get_args() prog = get_prog(args) icn = IntcodeComputerNode("A", prog, []) icn.run_prog_from_current_state() debug(''.join([chr(v) for v in icn.outputs])) s = ''.join([chr(v) for v in icn.outputs]) arr = [v for v in s.split('\n') if len(v.strip()) > 0] arr = np.asarray([list(s) for s in arr]) disp_board(arr) debug(arr.shape) XX = range(1, (arr.shape[1] - 1)) YY = range(1, (arr.shape[0] - 1)) intersections = [] for y in YY: for x in XX: if is_crossing(arr, y, x): intersections.append((x, y)) total = 0 params = [v[0] * v[1] for v in intersections] total = sum(params) # print(f"intersections: {intersections}") # print(f"{len(intersections)} intersections") # print(f"parameters: {params}") # print(f"{len(params)} parameters") # print(f"The total is {total}") ### Convert the map to a graph. ### Nodes: are the WALKWAYS. Each walkway between a {dead end, intersection} and a {dead end, intersection} is a graph node ### Undirected edges of weight 1 connect walkways if they are adjacent ### Each node (walkway) includes a PATH 1 and PATH 2. PATH 1 might be U,8,R,8 and PATH 2 would be the reverse: L,8,D,8 ### You know, we could solve it that way ### But this map is small enough that we can possibly figure this out manually ### Seriously let's try that first. Sometimes the dumbest way is best... ### Ok eyeballing this: # R,8,L,10,R,8,R,2,R,6,R,0,R,12,R,8,R,6,R,8 [done top left and first box] # R,0,R,10,R,2,R,2,R,2,R,2,R,2,L,8,R,6,R,8,R,6,R,8 # done mid left small and big box, now starting horizontal box mid left # L,6,R,2,R,6,R,2,R,6,R,2 # done the horizontal box mid left # L,2,L,10,R,2,R,6,L,10,R,8,L,8,R,10,R,12,R,8,L,10,R,6,R,2,L,10,R,2 # done big bottom right loop, now at the top of it # L,2,R,6,R,2,R,6,R,2,R,6 # done small horizontal box just above the big bottom right loop # L,6,R,10 # done top right isolated path # R,12,R,8,R,10,R,10,R,14,R,8,R,8,L,6 # done everything top right, done everything period. ### Ok, based on above a prospective path is (with corrections made based on tests) # R='R' # L='L' # pp=[R,8,L,10,R,8,R,2,R,6,R,0,R,12,R,8,R,6,R,8] # pp += [R,0,R,10,R,2,R,2,R,2,R,2,R,2,L,8,R,6,R,8,R,6,R,8] # pp += [L,6,R,2,R,6,R,2,R,6,R,2] # pp += [L,2,L,10,R,2,R,6,L,10,R,8,L,8,R,10,R,12,R,8,L,10,R,6,R,2,L,10,R,2] # pp += [L,2,R,6,R,2,R,6,R,2,R,6] # pp += [L,6,R,10] # pp += [R,12,R,8,R,10,R,12,R,12,R,8,R,8,L,6] ### Ok, after playing with that, new approach based on heuristic: don't turn until we have to (hence, limit total path command length): # R,8,L,10,R,8,R,12,R,8,L,8,L,12,R,8,L,10,R,8,L,12,L,10,L,8,R,8,L,10,R,8,R,12,R,8,L,8,L,12,L,12,L,10,L,8,L,12,L,10,L,8,R,8,L,10,R,8,R,12,R,8,L,8,L,12 # That's a complete traversal ending at the other dead end. We'll run with it: ### Ok based on above a prospective path is (with corrections made based on tests) R = 'R' L = 'L' pp = [ R, 8, L, 10, R, 8, R, 12, R, 8, L, 8, L, 12, R, 8, L, 10, R, 8, L, 12, L, 10, L, 8, R, 8, L, 10, R, 8, R, 12, R, 8, L, 8, L, 12, L, 12, L, 10, L, 8, L, 12, L, 10, L, 8, R, 8, L, 10, R, 8, R, 12, R, 8, L, 8, L, 12 ] ### First thing to do is confirm that this is actually a correct path # L = len(pp) # i=0 # direction='N' # x=0 # y=10 # while i<L: # direction=get_direction(pp[i],direction) # (x,y) = update_map(arr, direction, pp[i+1], x, y) # disp_board_with_coords(arr) # print(pp[i:(i+2)]) # i+=2 # iii = input() # don't use it # if iii.strip()=='q': # exit() ## Above path is right! Well, it's one correct path. ## Now we just need to see if it's convertable to a movement program for the robot. If not, we need a new path. pp_str = ",".join([str(v) for v in pp ]) + "," # take last comma away later, useful for now print(pp_str) print(len(pp_str)) ### Let's get a map of repeated substrings up to length 20, since that's the longest a movement function can be ### Ok good but there is a lot of redundancy in there. Remove the cycle-duplicates. ### Actually wait, that may matter. Don't remove them yet. ### Ok good. Now we need to choose three movement functions ### It looks like this entire thing needs to compose into these movement functions. So, yikes. ### If that doesn't work for our current path, then we need a new path for which that's possible. ### Some hard constraints on the path, though. Has to start a certain way for the top left loop, and has to accomodate the big bottom right loop. print(f"main {len(pp_str)}: {pp_str}") print() # A=find_best_repeat(pp_str) A = 'R,8,L,10,R,8,' # Refinement based on first attempt pp_str = pp_str.replace(A, 'A,').replace(',,', ',') print(f"A {len(A)}: {A}") print(f"After A substitution: {pp_str}") print() B = find_best_repeat(pp_str) pp_str = pp_str.replace(B, 'B,').replace(',,', ',') print(f"B {len(B)}: {B}") print(f"After B substitution: {pp_str}") print() C = find_best_repeat(pp_str) pp_str = pp_str.replace(C, 'C,').replace(',,', ',') print(f"C {len(C)}: {C}") print(f"After C substitution: {pp_str}") print() print() print(f"main {len(pp_str)}: {pp_str}") print(f"A {len(A)}: {A}") print(f"B {len(B)}: {B}") print(f"C {len(C)}: {C}") ## Wowzers, that did it: # main: A,B,A,C,A,B,C,C,A,B # A: R,8,L,10,R,8 # B: R,12,R,8,L,8,L,12 # C: L,12,L,10,L,8 ##### WHAT FOLLOWS WAS NOT PART OF THE ULTIMATE SOLUTION. It was a failed extension of the first try above (before the second try, which worked). ### Ok what this has shown me is that we need a more economical path ### Probably needs to END at that only dead end so we don't turn around ### How many possible rules are there? ### Commands: 2, 3, 4 (one command: "R" "L" then number). 3 options. ### How many numbers? 2, 4, 6, 8, 10, 12, 14 that's it. 7 options. With rapid elimination. ### So that's 2*7 choices per command, 14^4 is not that bad plus we can eliminate. ### Alright, brute force it? That may not be enough though. May have more than one solution. ### But can't have that many. Well, keep all solutions then pick the valid ones (less than 20 chars etc) # p_cmds = [] # for chgdir in ['R','L']: # for nsteps in [2, 4, 6, 8, 10, 12, 14]: # p_cmds.append([chgdir, nsteps]) # p_routines = [] # for i in range(len(p_cmds)): # for j in range(len(p_cmds)): # p_routines.append(p_cmds[i]+p_cmds[j]) # for i in range(len(p_cmds)): # for j in range(len(p_cmds)): # for k in range(len(p_cmds)): # p_routines.append(p_cmds[i]+p_cmds[j]+p_cmds[k]) # for i in range(len(p_cmds)): # for j in range(len(p_cmds)): # for k in range(len(p_cmds)): # for l in range(len(p_cmds)): # p_routines.append(p_cmds[i]+p_cmds[j]+p_cmds[k]+p_cmds[l]) # for i in range(len(p_cmds)): # for j in range(len(p_cmds)): # for k in range(len(p_cmds)): # for l in range(len(p_cmds)): # for m in range(len(p_cmds)): # p_routines.append(p_cmds[i]+p_cmds[j]+p_cmds[k]+p_cmds[l]+p_cmds[m]) # print(len(p_routines)) ### 26390 possible routines. Cool, but we still don't want to test that^4 possibilities. ### Start by seeing what the first routine COULD be. Not many options. ### Based on that see what the second routine COULD be. Etc. ### Should eliminate pretty fast that way. # on_scaffold=set(['#','^','>','<']) # p_first = [] # for j in range(len(p_routines)): # pp = p_routines[j] # # pp = ['R',8,'L',10,'R',8] # L = len(pp) # i=0 # direction='N' # x=0 # y=10 # full_traversal=[] # while i<L: # direction=get_direction(pp[i],direction) # ((x,y), traversed) = update_position(direction, pp[i+1], x, y) # full_traversal += traversed # might not have the right order but ok # i+=2 # if all_scaffold(arr, full_traversal): # if ends_at_non_midpoint(arr,x,y): # p_first.append(pp) # # break # print(p_first) # print(len(p_first)) return 0