Comet flips through her sketch notebook, full of FieldSim drawings. "All our design ideas are in here," she says. "But nobody else can read my notebook."
Wren taps his chin. "What does the evidence say? A new programmer would only see the code."
Nova projects fieldsim.py with a blank space under its header. "How can I help?" she asks. "Comments help you and other programmers. A design note at the top can explain the classes and the choices."
Comet picks up her pencil. "And we should add tests for the algorithms, too."
Wren smiles. "Lead programmer, document it and test it."
Comments are written for you and for other programmers, so they can understand the code and what it does.
Before a class is written, it helps to design its attributes and behaviors in words or a diagram. A design note keeps that design with the code.
Add these comment lines to fieldsim.py, just below its two header lines:
# # Design note # Classes: a Sample is one sample token. A Rover drives, turns and # carries samples. A FieldGrid is the test field as a 2D list. # Why _battery is hidden: only move() changes it, one step at a time, # so outside code cannot set it to a value Pip never earned. # Why tests exist: test_fieldsim.py checks the grid, turns, moves, # pick_up and the algorithms after every change.
# fieldsim.py
# FieldSim: drive a rover across the test field and report what it found.
#
# Design note
# Classes: a Sample is one sample token. A Rover drives, turns and
# carries samples. A FieldGrid is the test field as a 2D list.
# Why _battery is hidden: only move() changes it, one step at a time,
# so outside code cannot set it to a value Pip never earned.
# Why tests exist: test_fieldsim.py checks the grid, turns, moves,
# pick_up and the algorithms after every change.
from field_data import FIELD, FIELD_SAMPLES
from grid import FieldGrid
from rover import Rover
from sample import Sample
from sorts import sort_by_grams
def collect_here(rover, grid, samples_by_spot):
"""Pick up the sample under the rover, if there is one."""
spot = (rover.get_row(), rover.get_col())
if grid.cell(spot[0], spot[1]) == "*" and spot in samples_by_spot:
if rover.pick_up(samples_by_spot[spot]):
grid.set_cell(spot[0], spot[1], ".")
def main():
grid = FieldGrid(FIELD)
samples_by_spot = {}
for label, kind, grams, row, col in FIELD_SAMPLES:
samples_by_spot[(row, col)] = Sample(label, kind, grams, row, col)
pip = Rover("Pip")
for c in "FFRFFFLFFLFRRFFLF":
pip.run(c, grid)
collect_here(pip, grid, samples_by_spot)
grid.show(pip)
print(pip.status())
found = pip.get_samples()
sort_by_grams(found)
for s in found:
print(s.describe())
print(f"Total: {pip.total_grams()} g")
main() Programmers test with many conditions to make a program reliable. Add these two tests to test_fieldsim.py, above the lines that call the tests.
Add the three new imports at the top, below the import of Sample. Then call test_algorithms() and test_reachable() before the final print.
from search import binary_search
from sorts import selection_sort
from recursion import total, reachable
def test_algorithms():
nums = [42, 18, 65, 7, 30]
selection_sort(nums)
assert nums == [7, 18, 30, 42, 65]
grams = [7, 12, 18, 25, 30, 39, 42, 51, 65, 88]
assert binary_search(grams, 39) == 5
assert binary_search(grams, 40) == -1
assert total(grams) == 377
def test_reachable():
grid = FieldGrid(FIELD)
assert reachable(grid, 0, 0, set()) == 26
grid.set_cell(0, 1, "#")
grid.set_cell(1, 0, "#")
assert reachable(grid, 0, 0, set()) == 1 # test_fieldsim.py
# Tests for FieldSim. Run this file: it prints one line when all pass.
from field_data import FIELD
from grid import FieldGrid
from rover import Rover
from sample import Sample
from search import binary_search
from sorts import selection_sort
from recursion import total, reachable
def test_grid():
grid = FieldGrid(FIELD)
assert grid.height() == 5
assert grid.width() == 6
assert grid.is_open(0, 0)
assert not grid.is_open(1, 1)
assert not grid.is_open(-1, 0)
assert not grid.is_open(0, 6)
def test_turns():
rover = Rover("Test")
rover.turn_right()
assert rover.get_heading() == "S"
rover.turn_left()
rover.turn_left()
assert rover.get_heading() == "N"
def test_move():
grid = FieldGrid(FIELD)
rover = Rover("Test")
assert rover.move(grid)
assert (rover.get_row(), rover.get_col()) == (0, 1)
assert rover.get_battery() == 95
rover.turn_left()
assert not rover.move(grid)
assert rover.get_row() == 0
def test_pick_up():
rover = Rover("Test")
for n in range(3):
assert rover.pick_up(Sample(f"T{n}", "clay", 10))
assert not rover.pick_up(Sample("T3", "clay", 10))
assert rover.total_grams() == 30
def test_algorithms():
nums = [42, 18, 65, 7, 30]
selection_sort(nums)
assert nums == [7, 18, 30, 42, 65]
grams = [7, 12, 18, 25, 30, 39, 42, 51, 65, 88]
assert binary_search(grams, 39) == 5
assert binary_search(grams, 40) == -1
assert total(grams) == 377
def test_reachable():
grid = FieldGrid(FIELD)
assert reachable(grid, 0, 0, set()) == 26
grid.set_cell(0, 1, "#")
grid.set_cell(1, 0, "#")
assert reachable(grid, 0, 0, set()) == 1
test_grid()
test_turns()
test_move()
test_pick_up()
test_algorithms()
test_reachable()
print("All FieldSim tests passed") The file still prints one line when every test passes:
All FieldSim tests passed
System reliability means a program does its job as expected without failing. Programs can have helpful and harmful effects, including effects nobody intended.
Imagine the crew had trusted a FieldSim that was never tested. Suppose is_open forgot the rocks. The simulation would say a route is clear when it is not.
| Statement | True or false? |
|---|---|
| A comment changes what a program prints. | ? |
| Testing with many conditions helps make a program reliable. | ? |
| A simulation that was never tested could give a team a wrong route. | ? |
| A program built to help can never have a harmful effect. | ? |
FieldSim is shipped, reviewed, documented and tested, lead programmer. Tomorrow you get to show it off.