← Back to course
Python 11-12 / Week 05 / Wednesday
3/6
Week 05 · Grids

Wednesday

Sim Lab: grid.py
// The test field as rows and columns
⏱ about 30 min

Wednesday: Sim Lab: grid.py and field_data.py

Comet pins the field sketch to the wall. "Pip needs a real map," she says. "One the Rover can ask questions, like where are the rocks?"

Wren reads his notes from week 3. "What does the evidence say about Rover?" he asks. "We hid its state behind methods, and it helped. Let's do the same for the field."

Nova projects two file icons, field_data.py and grid.py, with a line between them. "How can I help?" she asks. "Keep the data in one file and the class in another."

"Lead programmer," Comet says, "two files today. Then we put Pip on the map."

Two new files

field_data.py holds made-up data from the FieldSim story. It has the field map, the four field samples and a catalog for later weeks.

grid.py holds class FieldGrid. It keeps the field as a list of lists in _cells, hidden behind methods, like Rover keeps its state.

MethodWhat it gives back or does
height(), width()The number of rows, and the number of columns.
in_bounds(row, col)True if the cell is inside the field.
cell(row, col)The mark in that cell.
set_cell(row, col, mark)Puts a new mark in that cell.
is_open(row, col)True if the cell is in bounds and not a rock.
count(mark)How many cells hold that mark.
show(rover=None)Prints the field, with R where a rover is.
  1. In your fieldsim folder, make field_data.py. Type it exactly, save it and run it. Nothing prints, and that is right.
  2. Make grid.py and type class FieldGrid. Save it and run it. Again, nothing prints.
  3. Make grid_demo.py. Predict its output, then run it.
  4. Make grid_rover.py, which puts Pip on the map. Predict, then run.
  5. Make one change and one deliberate error, below.
# field_data.py
# Made-up data for the Larkspur test field (FieldSim story).
# "." open ground, "#" rock, "*" a sample spot.

FIELD = [
    "..*..#",
    ".#....",
    "...#*.",
    "*.....",
    "..#..*",
]

# label, kind, grams, row, col (the four spots marked * above)
FIELD_SAMPLES = [
    ("S01", "basalt", 42, 0, 2),
    ("S02", "quartz", 18, 2, 4),
    ("S03", "clay", 65, 3, 0),
    ("S04", "mica", 7, 4, 5),
]

# The whole catalog of samples the crew has logged (weeks 8 to 12).
CATALOG = [
    ("S01", "basalt", 42),
    ("S02", "quartz", 18),
    ("S03", "clay", 65),
    ("S04", "mica", 7),
    ("S05", "granite", 30),
    ("S06", "shale", 51),
    ("S07", "slate", 12),
    ("S08", "flint", 88),
    ("S09", "chalk", 25),
    ("S10", "sandstone", 39),
]
# grid.py
# A FieldGrid is the test field as a 2D list of one-letter cells.


class FieldGrid:
    """The test field: rows of cells, each ".", "#" or "*"."""

    def __init__(self, rows):
        self._cells = []
        for row in rows:
            self._cells.append(list(row))

    def height(self):
        return len(self._cells)

    def width(self):
        return len(self._cells[0])

    def in_bounds(self, row, col):
        return 0 <= row < self.height() and 0 <= col < self.width()

    def cell(self, row, col):
        return self._cells[row][col]

    def set_cell(self, row, col, mark):
        self._cells[row][col] = mark

    def is_open(self, row, col):
        return self.in_bounds(row, col) and self.cell(row, col) != "#"

    def count(self, mark):
        total = 0
        for row in self._cells:
            for c in row:
                if c == mark:
                    total += 1
        return total

    def show(self, rover=None):
        for r in range(self.height()):
            line = ""
            for c in range(self.width()):
                if rover is not None and rover.get_row() == r and rover.get_col() == c:
                    line += "R"
                else:
                    line += self._cells[r][c]
            print(line)
# grid_demo.py
from field_data import FIELD
from grid import FieldGrid

grid = FieldGrid(FIELD)
grid.show()
print(grid.height(), "rows,", grid.width(), "columns")
print("Rocks:", grid.count("#"))
print("Sample spots:", grid.count("*"))
print("Open ground:", grid.count("."))
PREDICT BEFORE RUN 1: GRID_DEMO.PY
  • Read the question.
  • Tap your answer.
What does the Rocks line show?
What does the Open ground line show?
..*..#
.#....
...#*.
*.....
..#..*
5 rows, 6 columns
Rocks: 4
Sample spots: 4
Open ground: 22
# grid_rover.py
from field_data import FIELD
from grid import FieldGrid
from rover import Rover

grid = FieldGrid(FIELD)
pip = Rover("Pip", 2, 1)
grid.show(pip)
print(grid.cell(2, 1))
PREDICT BEFORE RUN 2: GRID_ROVER.PY
  • Read the question.
  • Tap your answer.
Which line of the map shows R?
What does print(grid.cell(2, 1)) show?
..*..#
.#....
.R.#*.
*.....
..#..*
.

Change it once

Pip has collected the sample at (0, 2), so mark that cell open. Make grid_change.py:

# grid_change.py
from field_data import FIELD
from grid import FieldGrid

grid = FieldGrid(FIELD)
grid.set_cell(0, 2, ".")
print("Sample spots:", grid.count("*"))
print(FIELD[0])
PREDICT GRID_CHANGE.PY
  • Read the question.
  • Tap your answer.
What does the Sample spots line show?
What does print(FIELD[0]) show?

Break it once

In grid_demo.py, take the quotes away from the rock mark: grid.count(#). Save and run it.

  File "grid_demo.py", line 8
    print("Rocks:", grid.count(#))
                              ^
SyntaxError: '(' was never closed
What kind of error is this?
At your computer
1. Put the quotes back around the #, save and run grid_demo.py again.
2. Check the map and the counts: Rocks: 4, Sample spots: 4, Open ground: 22.
3. Your fieldsim folder now has field_data.py and grid.py for the rest of the course.

A real map for Pip. Tomorrow you will find out what happens at the edges.

← Tuesday