コード例 #1
0
ファイル: 21a.py プロジェクト: bslaff/aoc2019
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
コード例 #2
0
ファイル: 09a.py プロジェクト: bslaff/aoc2019
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
コード例 #3
0
ファイル: 11b.py プロジェクト: bslaff/aoc2019
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
コード例 #4
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
コード例 #5
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)
コード例 #6
0
ファイル: 19a.py プロジェクト: bslaff/aoc2019
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
コード例 #7
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
コード例 #8
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
コード例 #9
0
ファイル: 15a.py プロジェクト: bslaff/aoc2019
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
コード例 #10
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
コード例 #11
0
ファイル: 19b.py プロジェクト: bslaff/aoc2019
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
コード例 #12
0
ファイル: 17b.py プロジェクト: bslaff/aoc2019
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
コード例 #13
0
ファイル: 17b.py プロジェクト: bslaff/aoc2019
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