This is an archived version of the course. Please find the latest version of the course on the main webpage.

Chapter 9: Refactoring the robot

Implementing your algorithm

face Josiah Wang

Hopefully you have your modular algorithm ready. I can imagine your hierarchy of post-it notes in front of you!

Now, we can start thinking more at an implementation level now.

We can start at your ‘root’ post-it note. You can treat each ‘note’ as a function.

So your first function can be a big program function. You could name it something like run_simulation().

Now, think about whether this function needs any input? This will depend on your implementation. Will there be any configuration variables that you may want to allow the user to customise? Perhaps the grid size? Or the location of the Ribena? Or maybe you want to allow the user to decide on the robot’s favourite drink instead? These can potentially be the input arguments to the run_simulation() function.

Does the function need any output? Possibly not. Or perhaps you want your program to tell you whether your robot has failed to find its favourite drink, or that the robot has broken down? That will be up to you - it’s your robot project! 🤖

You are now ready to implement this function. Like how we did this earlier, write the definition for run_simulation(), and if you are expecting it to output anything, write a temporary return statement that at least hints at the return type before you start.

Then write a function call for run_simulation() at the top level. Perhaps you need to give the grid size as input? So run_simulation(grid_size=10) might be a function call.

You can also start writing docstrings while you are at it 📝

Then run your program to make sure there are no syntax errors at the moment. Remember - test-as-you-code, incremental development!

You might end up with something like this:

def run_simulation(grid_size=10, target_row=9, target_col=9):
    """ Start robot navigation simulation.

    Args:
        grid_size (int): The size of the grid. Defaults to 10.
        target_row (int): The target row coordinate. Defaults to 9.
        target_col (int): The target column coordinate. Defaults to 9.
    """
    pass


grid_size = 10
run_simulation(grid_size=grid_size)

Now, implement run_simulation() by including the three tasks (unless you have other better tasks), each as a function (one to set up robot, one to print the robot’s message, one to allow for navigation). Write a placeholder function definition for each. And write the function call. Think about what information the function might need as input to be able to run, and pass them in as input arguments. And predict what output(s) the function might return, and assign them to variables accordingly in your function call.

It might look something like this:

def setup_robot(grid_size):
    """ Initialise the robot name, ID, and initial position and direction.

    Args:
        grid_size (int): The size of the grid.

    Returns:
        str : Robot name
        int : Robot ID
        int : Robot's row coordinate
        int : Robot's column coordinate
        str : Robot's direction ("n", "s", "e", or "w")
    """
    name = None
    id = 0
    row = 0
    col = 0
    direction = "n"
    return (name, id, row, col, direction)


def print_robot_greeting(name, identifier):
    print(f"Hello. My name is {name}. My ID is {identifier}.")


def navigate(current_direction,
             current_row,
             current_col,
             target_row,
             target_col,
             grid_size):
    pass


def run_simulation(grid_size=10, target_row=9, target_col=9):
    """ Start robot navigation simulation.

    Args:
        grid_size (int): The size of the grid. Defaults to 10.
        target_row (int): The target row coordinate. Defaults to 9.
        target_col (int): The target column coordinate. Defaults to 9.
    """
    name, identifier, row, col, direction = setup_robot(grid_size)
    print_robot_greeting(name, identifier)
    navigate(direction, row, col, target_row, target_col, grid_size)


grid_size = 10
run_simulation(grid_size=grid_size)

Then repeat the process for each function 🔁

You can also combine this with the bottom-up approach. Identify any bits of your code that seem repetitive (you might have duplicated some bits of your code). Can these be turned into functions? Some good candidates include clipping the coordinates, generating a string for the direction, rotate 90 degrees clockwise, moving one step forward, printing robot position and direction, etc.

Of course, you can refine your thoughts and change things as you code - it is not set in stone. You might have missed something. Or you might discover that a function is not as specific as you initially thought. A good sign that your function might not be specific enough is when you still have a very deep set of nested indentations. For example, if you have a triple-nested while loops, then perhaps consider refactoring (the innermost loop might be worth being in its own function).

Do spend time working on this. Mastering this thinking process will really make your programming and problem solving skills go really far!

Also remember to test your program to make sure that it works correctly!