Goal
The goal is to build a "Maze-runner special edition" of your Toy Robot. You must basically be
able to select from a list of available obstacle modules, and then get your robot to navigate
through this maze of obstacles. The various obstacle modules will be provided by your team
members.
What you will program
You will work from your Toy Robot 4 codebase and add to that.
Create a new obstacles.py module that generates obstacles.
It must keep the same functions (i.e. interface) as now, but you can change the implementation to
create a designed maze (i.e. you determine the positions of each obstacle) or you can bring in
some other way of creating a randomized maze.
Get other obstacle modules from your team members, and share yours with them.
Find a manual way to navigate through the various obstacle modules.
Implement a maze-runner module that must be able to solve all the obstacle modules provided by
the various team members
What you will learn
Working in a team and sharing code.
Figuring out how somebody elses code works.
Working with multiple modules providing the same interface.
TipDo not throw away the unit tests you have for your solution from Toy Robot 3; these tests are
still valid and must still succeed (allowing for updated tests in the various test .py files). Rather
extend these tests to cover new functionality.
Instructions
ImportantThis exercise must be completed and submitted for review.
STEP 1 Implement new Obstacles module
Please use your solution for the Toy Robot 4 exercise as starting point for this problem.
First you need to implement your very own module, based on the obstacles.py module from the
previous exercise, but with a better approach to generating obstacles so that it creates a kind of
maze that is more difficult to navigate through.
First create a new package maze by creating it as a sub-directory.
Move your existing obstacles.py to that package, and update your existing code so that it imports
obstacles from the maze package.
Now add a new module to the maze package with the same functions as in obstacles.py.
Use a descriptive filename for your maze code, e.g. the_worlds_most_crazy_maze.py
Change the implementation of the functions in your new module to create the obstacles in an
interesting way.
First just create a few obstacles by hand and try that in your robot.py to see how it renders.
Evolve using random obstacles, hard coded obstacles (for a hand-designed maze), or even look at
interesting approaches like procedural generation
Use your new maze in your robots world, and see how it works.
You are welcome to try out different maze implementations.
For now, just change the necessary import statements to choose which one to use.
Run your robot using the turtle world, so that you can visually see the maze and move through it
manually.
STEP 2 Share a maze
Next you must share your maze implementation(s) with your team members. This is where a
tool like git is handy, using a repository everyone can access, such .
Goal The goal is to build a Maze-runner special edition of your .pdf
1. Goal
The goal is to build a "Maze-runner special edition" of your Toy Robot. You must basically be
able to select from a list of available obstacle modules, and then get your robot to navigate
through this maze of obstacles. The various obstacle modules will be provided by your team
members.
What you will program
You will work from your Toy Robot 4 codebase and add to that.
Create a new obstacles.py module that generates obstacles.
It must keep the same functions (i.e. interface) as now, but you can change the implementation to
create a designed maze (i.e. you determine the positions of each obstacle) or you can bring in
some other way of creating a randomized maze.
Get other obstacle modules from your team members, and share yours with them.
Find a manual way to navigate through the various obstacle modules.
Implement a maze-runner module that must be able to solve all the obstacle modules provided by
the various team members
What you will learn
Working in a team and sharing code.
Figuring out how somebody elses code works.
Working with multiple modules providing the same interface.
TipDo not throw away the unit tests you have for your solution from Toy Robot 3; these tests are
still valid and must still succeed (allowing for updated tests in the various test .py files). Rather
extend these tests to cover new functionality.
Instructions
ImportantThis exercise must be completed and submitted for review.
STEP 1 Implement new Obstacles module
Please use your solution for the Toy Robot 4 exercise as starting point for this problem.
First you need to implement your very own module, based on the obstacles.py module from the
previous exercise, but with a better approach to generating obstacles so that it creates a kind of
maze that is more difficult to navigate through.
First create a new package maze by creating it as a sub-directory.
Move your existing obstacles.py to that package, and update your existing code so that it imports
obstacles from the maze package.
Now add a new module to the maze package with the same functions as in obstacles.py.
Use a descriptive filename for your maze code, e.g. the_worlds_most_crazy_maze.py
Change the implementation of the functions in your new module to create the obstacles in an
2. interesting way.
First just create a few obstacles by hand and try that in your robot.py to see how it renders.
Evolve using random obstacles, hard coded obstacles (for a hand-designed maze), or even look at
interesting approaches like procedural generation
Use your new maze in your robots world, and see how it works.
You are welcome to try out different maze implementations.
For now, just change the necessary import statements to choose which one to use.
Run your robot using the turtle world, so that you can visually see the maze and move through it
manually.
STEP 2 Share a maze
Next you must share your maze implementation(s) with your team members. This is where a
tool like git is handy, using a repository everyone can access, such as one hosted the WTC_
Gitlab.
Create a shared repository on GitLab, and make sure all team members have access to it
Clone that repository to your local machine, using a different working directory than this
exercise directory.
Copy your maze python file(s) to that directory, and then use git to add it to the repository and
push it to the remote repository.
Use git to pull the maze files of your team members from the repository.
Copy your team member maze files into this exercise working directory, in the maze package.
Now try out your robot program with each of your team member obstacles - it should be able to
run with each of those.
use them by changing the necessary import statement.
ensure that test_obstacles.py from Toy Robot 4 should succeed against each team member
module as well.
See if you can navigate your robot manually through each team member maze.
NoteWe have prepared some material to help you learn about version control. Download and
work through Introduction to Version Control with Git. Leverage your peers and mentor groups
to help you learn.
STEP 3 Select a Maze
Changing import statement to load the desired maze is not very elegant. Let us change that so
that the player can select a maze from the available ones.
Update robot.py to allow the player to specify the maze to load as a command line argument,
3. similar to how the world to load can be specified.
If the player only provides one command line argument, e.g. python robot.py turtle, then the
world to use is specified, and you can assume it must use obstacles.py in the maze package as the
maze module to load.
If the player specifies a second argument, then that indicates the name of the maze module to
load: e.g. python robot.py turtle simple_maze means it must use the world/turtle.py module for
the world, and the maze/simple_maze.py module for the maze.
Print out what maze module is loaded before printing the list of obstacles, e.g. HAL: Loaded
simple_maze.
TipWe supply a module import_helper.py with a function dynamic_import(module_name) that
you can use to dynamically load the maze module. Instead of doing an import statement at the
top of your world.py file, you can call this function to import the module while your program is
running. If the module is in the maze package, then you will need to supply that as part of your
module name to load, e.g. obstacles = import_helper.dynamic_import('maze.simple_maze').
The output should look something like this:
$ python3 robot.py text simple_maze
What do you want to name your robot? HAL
HAL: Hello kiddo!
HAL: Loaded simple_maze.
There are some obstacles:
- At position 0,15 (to 4,19)
- At position 50,60 (to 54,64)
- At position -20,-30 (to -16, -26)
HAL: What must I do next?
Or, if you do not specify the second argument, it should use obstacles by default:
$ python3 robot.py text
What do you want to name your robot? HAL
HAL: Hello kiddo!
HAL: Loaded obstacles.
There are some obstacles:
- At position 0,15 (to 4,19)
- At position 50,60 (to 54,64)
- At position -20,-30 (to -16, -26)
HAL: What must I do next?
STEP 4 Maze-runner
4. The ability to choose a maze, and then navigate the robot from the center of the screen to one of
the edges is already more fun that we possibly had in the whole of lockdown, but let us take it up
a level.
We want to enable our robot to automatically traverse the maze.
Add the ability to robot.py to understand the command mazerunthis command will let the robot
figure out what commands to use to get to the top of the screen.
Remember to add help support for this command.
First output a message to indicate it is starting to run the maze, e.g. > starting maze run..
Implement code that gets called when the player activates the mazerun command that will use
existing commands (e.g. forward, back, left, right) to go from the starting position of the robot in
the center of its world, to the top edge of the world.
You might need to refactor the information returned internally when calling other command
functions to provide enough information so that your maze-runner code can figure out what to do
next.
E.g. each command function might need to be enhanced to return whether it could complete, or
was obstructed, so you can use the information to determine if a next command should happen.
You might also need to add functionality to your world modules to indicate whether the current
robot position is at a particular edge of the world.
For example, when calling forward, it might tell you there is an obstacle in the way, in which
case your robot needs to rather go left or right.
TipTry and put your maze-runner code in a separate module, and call that from robot.py to solve
the maze, using the returns from its functions to determine what commands to execute next.
For example, given a world with no obstacles (i.e. an empty maze), the output can look like:
$ python3 robot.py text empty_maze
What do you want to name your robot? HAL
HAL: Hello kiddo!
HAL: Loaded empty_maze.
HAL: What must I do next? mazerun
> HAL starting maze run..
> HAL moved forward by 50 steps.
> HAL now at position (0,50).
> HAL moved forward by 50 steps.
> HAL now at position (0,100).
> HAL moved forward by 50 steps.
> HAL now at position (0,150).
> HAL moved forward by 50 steps.
5. > HAL now at position (0,200).
HAL: Sorry, I cannot go outside my safe zone.
HAL: I am at the top edge.
> HAL now at position (0,200).
HAL: What must I do next?
Given a world with an obstacle at position (0,50), it might look like this:
$ python3 robot.py text single_obstacle_maze
What do you want to name your robot? HAL
HAL: Hello kiddo!
HAL: Loaded single_obstacle_maze.
There are some obstacles:
- At position 0,51 (to 4,55)
HAL: What must I do next? mazerun
> HAL starting maze run..
> HAL moved forward by 50 steps.
> HAL now at position (0,50).
> HAL moved forward by 50 steps.
HAL: Sorry, there is an obstacle in the way.
> HAL now at position (0,50).
> HAL turned right
> HAL moved forward by 50 steps.
> HAL now at position (50,50).
> HAL turned left
> HAL moved forward by 50 steps.
> HAL now at position (50,100).
> HAL moved forward by 50 steps.
> HAL now at position (50,150).
> HAL moved forward by 50 steps.
> HAL now at position (50,200).
HAL: Sorry, I cannot go outside my safe zone.
HAL: I am at the top edge.
> HAL now at position (0,200).
HAL: What must I do next?
STEP 5 Choose an edge
Well done, your robot is now a maze-runner - it can automatically find a path around obstacles
6. to the top edge of its world. In this step we want to specify where it must head: either top,
bottom, left or right edge.
Update the mazerun command to take a parameter, one of top, bottom, left or right.
E.g. mazerun bottom commands your robot to move past obstacles to the bottom of its world.
If no parameter is provided, it defaults to top
For example, given a world with no obstacles (i.e. an empty maze), the output can look like:
$ python3 robot.py text empty_maze
What do you want to name your robot? HAL
HAL: Hello kiddo!
HAL: Loaded empty_maze.
HAL: What must I do next? mazerun bottom
> HAL starting maze run..
> HAL turned right
> HAL turned right
> HAL moved forward by 50 steps.
> HAL now at position (0,-50).
> HAL moved forward by 50 steps.
> HAL now at position (0,-100).
> HAL moved forward by 50 steps.
> HAL now at position (0,-150).
> HAL moved forward by 50 steps.
> HAL now at position (0,-200).
HAL: Sorry, I cannot go outside my safe zone.
HAL: I am at the bottom edge.
HAL: What must I do next?
STEP 6 Run the mazes
Now try out your mazerun command on the different mazes supplied by your team members.
Can it solve all the mazes?
Can it solve it to all 4 edges?
Can you build a maze that you can solve manually, but that none of your teams maze-runners
can?
See if you can count the number of moves involved to solve a maze and determine who it the
teams maze-runner is the most efficient.
7. Fobot_py import sys import world.obstacles as obstacles 6 import world.text.world as my_world
7 is_text_world = True 8 if len(sys.argy) >1: if sys.argv[1] =" 'turtle': import world.turtle.world
as my_world is_text_world = False if sys.argv[1]== "text': import world.text.world as my_world
" list of valid command names 17 valid_commands = ['off', 'help', 'replay', 'forward', 'back',
'right', 'Left', 'sprint'] 18 move_commands = va 19 20 thcommands history 21 history 20
Hcommands hi. 22 24 def get_robot_name(): name = input("What do you want to name your
robot? ") while len(name) =0: name = input("What do you want to name your robot? ") return
name def get_command(robot_name): Asks the user for a command, and validate it as well Only
return a valid command prompt = '+robot_name +: What must I do next? command =
input(prompt) while len(command) =0 or not valid_command(command): output(robot_name,
"Sorry, I did not understand "+ command +"," ) command = input(prompt) return command.
Lower() def split_command_input(command): Splits the string at the first space character, to get
the actual command, as well as the argument(s) for the command : return: (command, argument)
args = command. split ( ' , , 1) if 1 en(args) >1 i
args = command.split ( ' , , 1) if len(args) > 1: return args [0], args [1] return args [0],
8. Do a 90 degree turn to the left :param robot_name: :return: (True, left turn output text) global
current_direction_index my_world.current_direction_index =1 if my_world.current
direction_index if my_world.current_direction_index <0: my_world.current_direction_index =3
if is_text_world =- False: if is_text_world =- False: my_world.my_robot. left (90) return True, ',
return True, '> '+robot_name+' turned left.' do sprint(robot_name, command, steps): Sprints the
robot, i.e. Let it go forward steps + (steps-1) +( steps-2) ++1 number of steps, in iterations
:param robot_name: :param steps: :return: (True, forward output) else: (do_next,
command_output) = do_forward(robot_name, command, steps) print(command_output)
print(command_output) return do_sprint(robot_name, command, steps - 1) def
get_commands_history(reverse, relativestart, relativeEnd): Retrieve the commands from history
list, already breaking them up into (command_name, arguments) tuples ;param reverse: if True,
then reverse the list :param relativestart: the start index relative to the end position of command,
e.g. -5 means from index len(comands)-5; None means from beginning :param relativeEnd: the
start index relative to the end position of command, e.g. -1 means from index len(commands)-1;
None means to the end :return: return list of (command_name, arguments) tuples
commands_to_replay =[( name, args) if reverse: commands_to_replay.reverse() range_start =
Len(commands_to_replay) + relativestar range_end - Len(commands_to_replay) + relativeEnd
return commands_to_replay[range_start:range_end]
do =n teft_turn(robot_name ): Do a 90 degree turn to the left :param robot_name: :return: (True,
left turn output text) global current_direction_index my_world.current_direction_index - =1 if
my_world. current direction_index <0: if is_world.current_direction_index =3
my_world.my_robot. left (90) return True,,> '+robot_namet' turned left.' def do
sprint(robot_name, command, steps): Sprints the robot, i.e. let it go forward steps + (steps-1) +(
steps-2) ++1 number of steps, in iterations :param robot_name: :param steps: :return: (True,
forward output) if s steps =1: return do_forward(robot_name, command, 1) (do_next,
command_output) = do_forward(robot_name, command, steps) rint(command_output) return
do_sprint(robot_name, command, steps - 1) def get_commands_history(reverse, relativestart,
relativeEnd): Retrieve the commands from history list, already breaking them up into
(command_name, arguments) tuples :param relativeEnd: the start index relative to the end posi :
return: return tist of (command_name, arguments) tuples ireturn: return list of (command name,
arguments) rommands_to_replay,reverse() range_end = len(commands_to_replay) + relativeEnd
return commands_to_replay[range_start:range_end]
9. args = command.split ( ' , , 1) if len(args) > 1: return args [0], args [1] return args [0],
Do a 90 degree turn to the left :param robot_name: :return: (True, left turn output text) global
current_direction_index my_world.current_direction_index =1 if my_world.current
direction_index if my_world.current_direction_index <0: my_world.current_direction_index =3
if is_text_world =- False: if is_text_world =- False: my_world.my_robot. left (90) return True, ',
return True, '> '+robot_name+' turned left.' do sprint(robot_name, command, steps): Sprints the
robot, i.e. Let it go forward steps + (steps-1) +( steps-2) ++1 number of steps, in iterations
:param robot_name: :param steps: :return: (True, forward output) else: (do_next,
command_output) = do_forward(robot_name, command, steps) print(command_output)
print(command_output) return do_sprint(robot_name, command, steps - 1) def
get_commands_history(reverse, relativestart, relativeEnd): Retrieve the commands from history
list, already breaking them up into (command_name, arguments) tuples ;param reverse: if True,
then reverse the list :param relativestart: the start index relative to the end position of command,
e.g. -5 means from index len(comands)-5; None means from beginning :param relativeEnd: the
start index relative to the end position of command, e.g. -1 means from index len(commands)-1;
None means to the end :return: return list of (command_name, arguments) tuples
commands_to_replay =[( name, args) if reverse: commands_to_replay.reverse() range_start =
Len(commands_to_replay) + relativestar range_end - Len(commands_to_replay) + relativeEnd
return commands_to_replay[range_start:range_end]