← Back to course
Python 11-12 / Week 06 / Monday
1/6
Week 06 Β· Drive and Test

Monday

One step
// Step by step across the field, with tests at the edges
⏱ about 30 min

Monday: One Step

Comet has Pip on the workbench, wheels spinning in the air. "It is time Pip drove," she says. "One cell forward. How hard can it be?"

Wren holds up the field sketch. "What does the evidence say?" he asks. "Forward means a different direction for each heading. And some cells are rocks."

Nova projects four small arrows labeled N, E, S and W, each with a pair of numbers. "How can I help?" she asks. "Turn each heading into a row change and a column change."

Comet nods slowly. "So the rover adds the numbers, then asks the grid first. Lead programmer, trace one step that works and one that does not."

A heading becomes numbers

STEPS is a dict from each heading to a pair: the change in row and the change in column. North is one row up, so its row change is -1.

Wednesday these lines go into rover.py. Today you read them, trace them and try the idea in a small program of your own.

HEADINGS = ["N", "E", "S", "W"]
STEPS = {"N": (-1, 0), "E": (0, 1), "S": (1, 0), "W": (0, -1)}

The move() method

move() is a mutator: it changes Pip's row, column, battery and log. It also returns True or False so the caller knows what happened.

It checks the cell ahead first, then the battery. Only when both checks pass does Pip step.

    def move(self, grid):
        """Step forward one cell if it is open. Gives back True or False."""
        d_row, d_col = STEPS[self._heading]
        new_row = self._row + d_row
        new_col = self._col + d_col
        if not grid.is_open(new_row, new_col):
            self._log.append(f"blocked at ({new_row}, {new_col})")
            return False
        if self._battery < self.step_use():
            self._log.append("battery low")
            return False
        self._row = new_row
        self._col = new_col
        self._battery -= self.step_use()
        self._log.append(f"moved to ({new_row}, {new_col})")
        return True
LinePip at (0, 0) facing EPip at (0, 1) facing S
d_row, d_col(0, 1)(1, 0)
new_row, new_col(0, 1)(1, 1)
grid.is_open(...)True: open groundFalse: a rock
log line addedmoved to (0, 1)blocked at (1, 1)
returnsTrueFalse
# one_move.py
from field_data import FIELD
from grid import FieldGrid
from rover import Rover

grid = FieldGrid(FIELD)
pip = Rover("Pip")
print(pip.move(grid))
print(pip.status())
pip.turn_right()
print(pip.move(grid))
print(pip.get_log())
PREDICT ONE_MOVE.PY
  • Read the question.
  • Tap your answer.
What does print(pip.status()) show?
After turn_right(), what does the second move return?
True
Pip at (0, 1) facing E, battery 95
False
['moved to (0, 1)', 'blocked at (1, 1)']
PUT THE STEPS OF MOVE() IN ORDER
  • ?Add them to the row and column to get the new cell.
  • ?Look up the heading in STEPS to get d_row and d_col.
  • ?Check the battery has enough for one step.
  • ?Ask the grid whether the new cell is open.
  • ?Update the position, use battery and log the move.
WHY THIS EXERCISEEvery check happens before any state changes, so a blocked step leaves Pip exactly where it was.
At your computer
1. In your fieldsim folder, make step_test.py. It tries the same two steps without the Rover class. Type it, save it and run it.
2. Change ["E", "S"] to ["S", "S", "S", "E"]. Predict each line, then run it.
3. Change STEPS[heading] to STEPS[head] and run it. Read the NameError calmly, then change it back.
# step_test.py
from field_data import FIELD
from grid import FieldGrid

STEPS = {"N": (-1, 0), "E": (0, 1), "S": (1, 0), "W": (0, -1)}

grid = FieldGrid(FIELD)
row = 0
col = 0
for heading in ["E", "S"]:
    d_row, d_col = STEPS[heading]
    new_row = row + d_row
    new_col = col + d_col
    if grid.is_open(new_row, new_col):
        row = new_row
        col = new_col
        print(heading, "moved to", (row, col))
    else:
        print(heading, "blocked at", (new_row, new_col))
What does step_test.py print for the E step?
What does it print for the S step?
StatementTrue or false?
STEPS["N"] is (-1, 0) because north is one row up.?
move() changes Pip's position before it checks the grid.?
A blocked move adds a line to the log.?
move() returns True or False to its caller.?
WHY THIS EXERCISEmove() checks first and changes Pip only when both checks pass, then reports with True or False.

One step, traced both ways. Tomorrow Pip follows a whole string of commands.