Learning Scientific Programming with Python (2nd edition)
E2.23: A simple "turtle"
A simple "turtle'' virtual robot lives on an infinite two-dimensional plane on which its location is always an integer pair of $(x,y)$ co-ordinates. It can face only in directions parallel to the $x$ and $y$ axes (i.e. 'North', 'East', 'South' or 'West') and it understands four commands:
F
: move forward one unit;L
: turn left (anti-clockwise) by $90^\circ$;R
: turn right (clockwise) by $90^\circ$;S
: stop and exit.
The following Python program takes a list of such commands as a string and tracks the turtle's location. The turtle starts at $(0,0)$, facing in the direction $(1,0)$ ('East'). The program ignores (but warns about) invalid commands, and reports when the turtle crosses its own path.
commands = 'FFFFFLFFFLFFFFRRRFXFFFFFFS'
# Current location, current facing direction
x, y = 0, 0
dx, dy = 1, 0
# Keep track of the turtle's location in the list of tuples, locs
locs = [(0, 0)]
for cmd in commands:
if cmd == 'S':
# Stop command
break
if cmd == 'F':
# Move forward in the current direction
x += dx
y += dy
if (x, y) in locs:
print('Path crosses itself at: ({}, {})'.format(x,y))
locs.append((x,y))
continue
if cmd == 'L':
# Turn to the left (counterclockwise).
# L => (dx, dy): (1,0) -> (0, 1) -> (-1,0) -> (0,-1) -> (1,0)
dx, dy = -dy, dx
continue
if cmd == 'R':
# Turn to the right (clockwise).
# R => (dx, dy): (1,0) -> (0,-1) -> (-1,0) -> (0, 1) -> (1,0)
dx, dy = dy, -dx
continue
# if we're here it's because we don't recognise the command: warn
print('Unknown command:', cmd)
else:
# We exhausted the commands without encountering an S for STOP
print('Instructions ended without a STOP')
# Plot a path of asterisks
# First find the total range of x and y values encountered
x, y = zip(*locs)
xmin, xmax = min(x), max(x)
ymin, ymax = min(y), max(y)
# The grid size needed for the plot is (nx, ny)
nx = xmax - xmin + 1
ny = ymax - ymin + 1
# Reverse the y-axis so that it decreases *down* the screen
for iy in reversed(range(ny)):
for ix in range(nx):
if (ix+xmin, iy+ymin) in locs:
print('*', end='')
else:
print(' ', end='')
print()
Some notes:
-
We can iterate over the string
commands
to take its characters one at a time. -
The
else:
clause to thefor
loop is only executed if we do notbreak
out of it on encountering a STOP command. -
We unzip the list of tuples,
locs
, into separate sequences of the $x$ and $y$ coordinates withzip(*locs)
.
The output produced from the commands given is:
Unknown command: X
Path crosses itself at: (1, 0)
*****
* *
* *
******
*
*
*
*