Pale Machine CSE101
Objectives
What we will evaluate today: Everything!
Setup: Before you start
Launch Spyder (from the Applications/Programming menu). If you see a “Spyder update” message, just click OK and ignore it.
Before you start: create a new
project, called PracticalExam_2 (using “New
Project” from the “Projects” menu). This will ensure that files from
last week (and future weeks) do not get mixed up together.
Now download the following files located in the same
moodle folder as the exam and save them in your
PracticalExam_2 folder. You can use save as, and
choose your PracticalExam_2 folder.
- mower.py
- garden1.txt
- garden2.txt
- garden3.txt
- mower_example.svg
Check and make sure that they have all appeared in
your PracticalExam_2 project in Spyder.
Now open the file named mower.py. This
is the file in which you will be writing your code today. We already
provide a code basis, you have to fill in the missing parts.
Introduction
Before the winter really starts, you once more want to cut the grass in your garden. To save some time, you want to automatize the grass cutting (= yawn mowing) by using a yawn mower robot. The image below shows an example.
Your garden will be divided into square tiles with coordinates (x,y).
Your robot will start at its station (S). The station is currently
located at position (1,1,) but it can be anywhere in the garden. Then
the lawn mowing robot will go through the garden and cut the grass on
every tile it passes. The robot will have to go around the stones (#)
and make sure to not fall into the pool (P). The robot can only continue
as long as it has enough energy. The image below gives an example of the
garden where W shows the tiles with long grass and
_ the tiles with short grass. The arrow shows the robot
position and its orientation. The numbers show the x and y coordinates
of the garden tiles.
We will implement the robot as its own class (Robot) as well as the single tiles (Tile) and the garden in another class (Garden).
Exercises
Exercise 1: Directions
Look at the three lists in the beginning of the file
mower.py: Directions,
Direction_steps and Direction_chars. They will
be used to turn and move the robot to the right positions.
Directions = ["north", "east", "south", "west"]
Direction_chars = ["^", ">", "v", "<"]
Direction_steps = [(0,1), (1,0), (0,-1), (-1,0)]Run the following examples in order to see how these lists will be used.
In [1]: myorientation = "south"
In [2]: myindex = Directions.index(myorientation)
In [3]: myindex
Out[4]: 2
In [5]: mychar = Direction_chars[myindex]
In [6]: mychar
Out[7]: 'v'
In [8]: mystep = Direction_steps[myindex]
In [9]: mystep
Out[10]: (0, -1)
In [11]: myposition = (3,2)
In [12]: newposition = (myposition[0]+mystep[0],myposition[1]+mystep[1])
In [13]: newposition
Out[14]: (3, 1)
We will need these directions in the next exercises in order to change the robot’s orientation to the left or right and to move one step in the direction of the orientation.
Exercise 2: Robot
We first look at the class Robot to
represent the grass mowing robot. The robot is defined by its
position in the garden, its energy level, and
its orientation. Additionally, it stores the position of
its station which is also the starting point of the robot,
thus the __init__ function will set the robot’s
position to it’s station in the beginning.
Note that the coordinates of the garden tiles are always given as (x,y) where x is the column number and y the row number as shown in the image above.
Your class Robot begins as follows:
class Robot:
"""
Encapsulates the robot and its properties
Data Attributes:
position -- the current position of the robot in the garden (x,y) initialized with station
energy -- the energy level of the robot
orientation -- the orientation of the robot (north, east, south, west)
station -- position of the station
"""
def __init__(self, posx, posy, energy, orientation):
self.station = (posx,posy)
pass # remove this line and replace with your own code
def __str__(self):
pass # remove this line and replace with your own codeComplete the two methods __init__ and
__str__.
Testing
Test your class Robot in the console,
for example as follows:
In [15]: robo = Robot(1, 1, 100, "north")
In [16]: print(robo)
Lawn mowing robot:
Position (1, 1)
Energy 100
Orientation north
Station (1, 1)
In [17]: robo.position = (4,3)
In [18]: print(robo)
Lawn mowing robot:
Position (4, 3)
Energy 100
Orientation north
Station (1, 1)
In [19]: robo.energy = 42
In [20]: print(robo)
Lawn mowing robot:
Position (4, 3)
Energy 42
Orientation north
Station (1, 1)
Exercise 3: Robot Moves
Now complete the method move in the
Robot class: The method move(self) moves the
robot one step in the direction of its orientation by updating the
position and reduce energy by -1. If the energy is 0, the robot will
stay on its position.
Hint: use the examples from the first exercise
(e.g. Direction_steps) in order to get the steps right.
def move(self):
"""
Checks if energy > 0, then updates the position by 1 corresponding to the
robot's orientation and decreases the energy by 1.
"""
pass # remove this line and replace with your own codeTesting
Test your class Robot in the console,
for example as follows:
In [21]: robo = Robot(1, 1, 100, "north")
In [22]: print(robo)
Lawn mowing robot:
Position (1, 1)
Energy 100
Orientation north
Station (1, 1)
In [23]: robo.move()
In [24]: print(robo)
Lawn mowing robot:
Position (1, 2)
Energy 99
Orientation north
Station (1, 1)
In [25]: robo2 = Robot(3, 4, 100, "west")
In [26]: print(robo2)
Lawn mowing robot:
Position (3, 4)
Energy 100
Orientation west
Station (3, 4)
In [27]: robo2.move()
In [28]: print(robo2)
Lawn mowing robot:
Position (2, 4)
Energy 99
Orientation west
Station (3, 4)
In [29]: robo3 = Robot(2, 6, 1, "south")
In [30]: print(robo3)
Lawn mowing robot:
Position (2, 6)
Energy 1
Orientation south
Station (2, 6)
In [31]: robo3.move()
In [32]: print(robo3)
Lawn mowing robot:
Position (2, 5)
Energy 0
Orientation south
Station (2, 6)
In [33]: robo3.move()
In [34]: print(robo3)
Lawn mowing robot:
Position (2, 5)
Energy 0
Orientation south
Station (2, 6)
Note: It is important to test your function in the console as there is no grader feedback.
Exercise 4: Robot Turns
Now, complete the class Robot with two
other methods: 1. turn_left(self) and 2.
turn_right(self) that will update the orientation of the
robot in the corresponding direction.
Hint: use the examples from the first exercise in order to get the directions right.
def turn_left(self):
"""
Change orientation of the robot to the left
"""
pass # remove this line and replace with your own code
def turn_right(self):
"""
Change orientation of the robot to the right
"""
pass # remove this line and replace with your own codeTesting
Test your class Robot in the console,
for example as follows:
In [35]: robo = Robot(1, 1, 100, "north")
In [36]: print(robo)
Lawn mowing robot:
Position (1, 1)
Energy 100
Orientation north
Station (1, 1)
In [37]: robo.turn_right()
In [38]: print(robo)
Lawn mowing robot:
Position (1, 1)
Energy 100
Orientation east
Station (1, 1)
In [39]: robo2 = Robot(3, 4, 100, "west")
In [40]: print(robo2)
Lawn mowing robot:
Position (3, 4)
Energy 100
Orientation west
Station (3, 4)
In [41]: robo2.turn_left()
In [42]: print(robo2)
Lawn mowing robot:
Position (3, 4)
Energy 100
Orientation south
Station (3, 4)
Note: It is important to test your function in the console as there is no grader feedback.
Upload
Now take a moment to upload your mower.py file to the moodle
assignment corresponding to this exam.
(You can upload mower.py several times, but only
your final upload will be graded - don’t wait until the last moment to
upload a preliminary version, in case you miss the cut-off!)
Exercise 5: Tiles
We imagine our garden is organized by a grid of tiles each with a
unique position. We now implement the class Tile with the
following attributes:
coordinates: the x and y integer coordinates of the tile on the gridstate: a string that can be “short” or “long”kind: a string that can be “plain”, “station”, “stones” or “pool”
Our class Tile will therefore work as follows: Each
object will get coordinates (x,y) and a kind
can be plain, station, pool and
stones. As the grass in the garden is really long, each
plain tile’sstate is set to long
in the beginning. It will be set to short as soon as the
robot entered the tile. All the non-plain tiles will be
short from the beginning.
Look for the class in your python file and
complete the methods __init__ and
__str__.
class Tile:
"""
Data Attributes:
coordinates -- integer coordinates (x,y) of the tile
state -- a string: short or long
kind -- a string: plain, station, stones or pool
In the beginning, set all the plain tiles to long, all the others to short.
"""
def __init__(self, x, y, kind):
pass # remove this line and replace with your own code
def __str__(self):
pass # remove this line and replace with your own codeComplete the method called cut(self) to
the class Tile that changes the state of a tile from
long to short.
def cut(self):
pass # remove this line and replace with your own codeTesting
You should be able to reproduce the following behavior:
In [43]: t1 = Tile(2,3,"plain")
In [44]: print(t1)
Coordinates (2, 3)
Kind plain
State long
In [45]: t1.cut()
In [46]: print(t1)
Coordinates (2, 3)
Kind plain
State short
In [47]: t2 = Tile(5,6,"station")
In [48]: print(t2)
Coordinates (5, 6)
Kind station
State short
Upload
Now take a moment to upload your mower.py file to the moodle
assignment corresponding to this exam.
(Remember, you can upload mower.py several times,
but only your final upload will be graded - don’t wait until the last
moment to upload a preliminary version, in case you miss the
cut-off!)
Exercise 6: The Garden
Objects from the class Garden have the following data
attributes:
xsizewhich is the number of squares in x direction of the grid,ysizewhich is the number of squares in y direction of the grid,- a dictionary
tiles, whose keys are tuples of coordinates on the grid, with the corresponding values beingTileobjects at those positions, - the position of the tile with the robot
station - a list of coordinates that specify the
stones - the position of the
pool
Note: The coordinates of the grid are always between 1 and xsize or ysize!
Note 2 : To initialize a garden object, you need
xsize, ysize, the stones list,
the position of the pool and the station
position. If a robot, station or
pool position is set to (-1,-1) it means that they are
not in the garden! If the stones list is
empty, there are no stones in the garden!
Now, complete the __str__ method of
your Garden class and test your class
Garden and the already implemented method
draw.
Note 3: The
garden.draw(position,orientation) will get the position and
orientation of the robot as arguments in order to draw also the robot.
However, if the robot position is (-1,-1), there is
no robot in the garden!
Testing
In [49]: b = Garden(3,2,[],(-1,-1),(1,1))
In [50]: print(b)
Garden of size 3x2
In [51]: b.draw((-1,-1), "north")
WWW
SWW
In [52]: b2 = Garden(4, 5, [(4,4),(1,5),(3,4)], (2,3), (1,1))
In [53]: print(b2)
Garden of size 4x5
In [54]: b2.draw((-1,-1), "north")
#WWW
WW##
WPWW
WWWW
SWWW
In [55]: b3 = Garden(4, 5, [(4,4),(1,5),(3,4)], (2,3), (1,1))
In [56]: print(b3)
Garden of size 4x5
In [57]: robo = Robot(4,2,100,"east")
In [58]: print(robo)
Lawn mowing robot:
Position (4, 2)
Energy 100
Orientation east
Station (4, 2)
In [59]: b3.draw(robo.position,robo.orientation)
#WWW
WW##
WPWW
WWW>
SWWW
Exercise 7: The RoboRun class
In order to control the behaviour of the robot, we create the class
RoboRun that will combine all the classes so far.
Objects from the class RoboRun have the following data
attributes:
gardenwhich is an instance of the garden classrobotwhich is an instance of the robot class
The __init__ function of the class will initialize the
robot object and get the garden object as argument. For the robot, we
will calculate its energy rounded to the next integer as
int(xsize*ysize*1.5) and set the orientation as a default
to north.
Write the __init__ and
__str__ functions. Both should use the corresponding
functions from the Robot and Garden classes.
Note: The starting position of the robot will be the station.
Implement the __init__ and
__str__ methods using the corresponding methods from the
classes for Robot and Garden.
class RoboRun:
"""
The class RoboRun which organizes the course of the robot through the garden
Data Attributes:
garden -- Garden object
robot -- Robot object
"""
def __init__(self, garden):
pass # remove this line and replace with your own code
def __str__(self):
pass # remove this line and replace with your own codeTesting
In [60]: b3 = Garden(4, 5, [(4,4),(1,5),(3,4)], (2,3), (1,1))
In [61]: print(b3)
Garden of size 4x5
In [62]: rr = RoboRun(b3)
In [63]: print(rr)
RoboRun with
Lawn mowing robot:
Position (1, 1)
Energy 30
Orientation north
Station (1, 1)
and Garden of size 4x5
In [64]: rr.garden.draw(rr.robot.position, rr.robot.orientation)
#WWW
WW##
WPWW
WWWW
^WWW
Note If the robot is located on the station, only the robot is drawn.
Exercise 8: The allowed directions for the next step
Complete the method
get_allowed_directions in the RoboRun
class.
The method get_allowed_directions returns a list of
possible positions the robot can move to (as defined in the
Directions list in the first exercise). The robot can move
by one tile north, east, south or west but it cannot move outside of the
garden, hence the coordinates cannot go below 1 or above
xsize or ysize. The robot cannot move to a
tile occupied by stones but it can move into the pool which will result
in the robot drowning such that it won’t be able to continue. If the
robot can’t move, return an empty list.
Reminder Use the lists Directions and
Direction_steps at the beginning of the file and the
examples in exercise 1 to get the directions right.
def get_allowed_directions(self):
"""
Return a list of allowed Directions the robot could move to.
The output list should contain the directions as defined in the
Directions list above and only if
- the next tile in the direction is not occupied by stones
- and still inside the garden
"""
pass # remove this line and replace with your own codeTest your methods in the console, for example as follows.
In [65]: garden = Garden(9, 6, [(4,4),(1,5),(3,4)], (2,3), (1,1))
In [66]: rr = RoboRun(garden)
In [67]: print(rr)
RoboRun with
Lawn mowing robot:
Position (1, 1)
Energy 81
Orientation north
Station (1, 1)
and Garden of size 9x6
In [68]: rr.garden.draw(rr.robot.position,rr.robot.orientation)
WWWWWWWWW
#WWWWWWWW
WW##WWWWW
WPWWWWWWW
WWWWWWWWW
^WWWWWWWW
In [69]: npos = rr.get_allowed_directions()
In [70]: print(npos)
Out[71]: ['north', 'east']
In [72]: rr.robot.move()
In [73]: print(rr)
RoboRun with
Lawn mowing robot:
Position (1, 2)
Energy 80
Orientation north
Station (1, 1)
and Garden of size 9x6
In [74]: rr.garden.draw(rr.robot.position,rr.robot.orientation)
WWWWWWWWW
#WWWWWWWW
WW##WWWWW
WPWWWWWWW
^WWWWWWWW
SWWWWWWWW
In [75]: npos = rr.get_allowed_directions()
In [76]: print(npos)
Out[77]: ['north', 'east', 'south']
We allow printing the allowed directions in any order, so the output does not have to have exactly the same order.
Upload
Now take a moment to upload your mower.py file to the moodle
assignment corresponding to this exam.
(Remember, you can upload mower.py several times,
but only your final upload will be graded - don’t wait until the last
moment to upload a preliminary version, in case you miss the
cut-off!)
Exercise 9: One step
Our yawn mowing robot will run in rounds. It will be able to do one step per round which includes:
- getting the allowed directions,
- choosing one of the directions,
- cutting the grass on the tile the robot is on,
- setting the new direction,
- moving to the next position according to the direction.
As our robot doesn’t have a good strategy, we will use
random.choice to choose one of the next possible positions.
Hence random.choice(inputlist) will return an element of
the inputlist.
Hint: We recommend to use already implemented
functions (e.g. robot.move, tile.cut).
Note 1: As we choose the next position randomly, your output may look different!
Note 2: The robot is able to move into the pool currently, we will implement this in the next step!
Complete the method one_round(self) in
the RoboRun class.
def one_round(self):
"""
Get allowed directions,
choose one direction randomly,
cut the grass on the tile the robot is on,
set the new direction,
move to the next tile accordingly.
"""
pass # remove this line and replace with your own codeTest your methods in the console, for example as follows.
In [78]: garden = Garden(9, 6, [(4,4),(1,5),(3,4),(9,2),(9,3)], (-1,-1), (1,1))
In [79]: rr = RoboRun(garden)
In [80]: print(rr)
RoboRun with
Lawn mowing robot:
Position (1, 1)
Energy 81
Orientation north
Station (1, 1)
and Garden of size 9x6
In [81]: rr.one_round()
In [82]: rr.garden.draw(rr.robot.position,rr.robot.orientation)
WWWWWWWWW
#WWWWWWWW
WW##WWWWW
WWWWWWWW#
^WWWWWWW#
SWWWWWWWW
In [83]: rr.one_round()
In [84]: rr.garden.draw(rr.robot.position,rr.robot.orientation)
WWWWWWWWW
#WWWWWWWW
WW##WWWWW
WWWWWWWW#
_>WWWWWW#
SWWWWWWWW
In [85]: rr.one_round()
In [86]: rr.garden.draw(rr.robot.position,rr.robot.orientation)
WWWWWWWWW
#WWWWWWWW
WW##WWWWW
W^WWWWWW#
__WWWWWW#
SWWWWWWWW
Exercise 10: Robo run!
We now have everything to let the robot run automatically through the
garden as long as it has enough energy and doesn’t fall into the pool
(if any). Write a method run(self) into the class
RoboRun which lets the robot run as long as it did not hit
the pool and still has energy.
The method should:
- count the rounds
- print START as well as the garden and robot at the beginning
- print the round number every round
- print the garden every 10 rounds
- print END as well as the garden and robot after the run ended
Complete the method run(self) in the
class RoboRun.
def run(self):
"""
Let the robot run as long as the robot still has energy and did not fall into the pool
Print START as well as the garden and robot at the beginning
Print the round number every round
Print the garden every 10 rounds
Print END as well as the garden and robot after the run ended
"""
pass # remove this line and replace with your own codeTest your methods in the console, for example as follows.
In [87]: garden = Garden(2, 3, [], (-1,-1), (1,1))
In [88]: rr = RoboRun(garden)
In [89]: rr.run()
START
WW
WW
^W
Lawn mowing robot:
Position (1, 1)
Energy 9
Orientation north
Station (1, 1)
ROUND 1
ROUND 2
ROUND 3
ROUND 4
ROUND 5
ROUND 6
ROUND 7
ROUND 8
ROUND 9
END
__
__
Sv
Lawn mowing robot:
Position (2, 1)
Energy 0
Orientation south
Station (1, 1)
In [90]: garden2 = Garden(3, 4, [(2,2),(2,3)], (-1,-1), (1,1))
In [91]: rr2 = RoboRun(garden2)
In [92]: rr2.run()
START
WWW
W#W
W#W
^WW
Lawn mowing robot:
Position (1, 1)
Energy 18
Orientation north
Station (1, 1)
ROUND 1
ROUND 2
ROUND 3
ROUND 4
ROUND 5
ROUND 6
ROUND 7
ROUND 8
ROUND 9
ROUND 10
WWW
W#^
_#_
S__
ROUND 11
ROUND 12
ROUND 13
ROUND 14
ROUND 15
ROUND 16
ROUND 17
ROUND 18
END
_>_
W#_
_#_
S__
Lawn mowing robot:
Position (2, 4)
Energy 0
Orientation east
Station (1, 1)
In [93]: garden3 = Garden(3, 4, [(2,2),(2,3)], (2,4), (1,1))
In [94]: rr3 = RoboRun(garden3)
In [95]: rr3.run()
START
WPW
W#W
W#W
^WW
Lawn mowing robot:
Position (1, 1)
Energy 18
Orientation north
Station (1, 1)
ROUND 1
ROUND 2
ROUND 3
ROUND 4
ROUND 5
ROUND 6
ROUND 7
ROUND 8
END
_>W
_#W
_#W
SWW
Lawn mowing robot:
Position (2, 4)
Energy 10
Orientation east
Station (1, 1)
Upload
Now take a moment to upload your mower.py file to the moodle
assignment corresponding to this exam.
(Remember, you can upload mower.py several times,
but only your final upload will be graded - don’t wait until the last
moment to upload a preliminary version, in case you miss the
cut-off!)
Exercise 11: Reading from a file
To test your function, it will be convenient to use pre-defined
gardens. The three files (garden1.txt,
garden2.txt and garden3.txt) given at the top
of this page contain the gardens where we will have to cut the
grass.
We strongly encourage you to open the files garden1.txt,
garden2.txt and garden3.txt to see how they
look like.
Write the function
load_from_file(filename) (in mower.py,
outside the Robot, Tile,
Garden and RoboRun classes). The function
should return a garden object that can be used to initiate a
RoboRun.
Hints:
- The garden coordinates are between 1 and xsize and 1 and ysize!
- There are no spaces within the tuples!
- The elements (ysize, xsize, pool, station and atones) can be in any order
- station and pool do not have to be present, then
they should be initialized with
(-1,-1) - if there are not stones, they should be initialized with
[]
Test your function, for example as follows:
In [95]: garden1 = load_from_file('garden1.txt')
In [96]: garden1.draw((-1,-1),"north")
WWWSWWWW
W##WW##W
W##WW##W
W##WW##W
WWWWWWWW
WWWWPWWW
In [96]: garden2 = load_from_file('garden2.txt')
In [97]: garden2.draw((-1,-1),"north")
###
WWW
WWW
WW#
PWS
WW#
WWW
WWW
###
In [97]: garden3 = load_from_file('garden3.txt')
In [98]: garden3.draw((-1,-1),"north")
WWWWWWWW
WW#WW#WW
WW#WW#WW
WW####WW
WWWWSWWW
Upload
Now take a moment to upload your mower.py file to the moodle
assignment corresponding to this exam.
(Remember, you can upload mower.py several times,
but only your final upload will be graded - don’t wait until the last
moment to upload a preliminary version, in case you miss the
cut-off!)