You may work with a partner on this assignment
Run update21, if you haven't already, to create the
cs21/labs/11 directory. Then cd into your cs21/labs/11
directory and create the python programs for lab 11 in this directory
(handin21 looks for your lab 11 assignments in your cs21/labs/11 directory):
$ update21
$ cd cs21/labs/11
$ vim grid.py
Introduction
For this assignment, you will write a two classes, Cell and Grid, and
then write a program that uses these classes to draw a picture to the
graphics window. The Cell and Grid classes are designed to be used by
programs that implement time-step simulations of some phenomena on a 2-D world.
Simulating heat dissipation over a sheet of metal or an insect infestation
on a crop of wheat, or even climate modeling, are examples of such
discrete time-step simulations.
In these type of simulations, the world is broken up into a grid of cells,
each cell summarizes the information for its part of the world. At
each discrete time step, a cell's value changes based on some function
that involves its neighboring cell's values. So, in the insect model,
if a neighboring cell is infested, it is likely that in a few time steps
the cell will become infested too, and after some more time when the
insects have eaten all the wheat in that cell, they will move on.
In the next lab assignment, you will use the Cell and Grid classes to
implement a simulation program such as these.
You will use the graphics library to draw the world represented by
a Grid object that contains a list of lists of Cell objects.
For our implementation a Cell can have one of two values (ON and OFF) and
you will color cells differently based on their current value. In this
assignment you will implement the Cell and Grid classes and then use them
to draw a pixelated picture to the graphics window, by setting cells values
to ON or OFF.
Cell class
The cell class represents 1 square unit in a 2-D grid in the Graphics window.
A Cell object has some some state including:
- its row and column position in the 2-D grid,
- it current value (ON or OFF)
- its value for the next time step (ON or OFF)
- its Rectangle object
In addition, all Cell objects share the same definitions for ON and OFF
and their associated colors (the Cell should be colored one color if
its value is ON, another if its value is OFF).
A Cell has the following methods:
- constructor that takes a row, column, and value and create a new
Rectangle object at that position
- getRow: returns the value of the row field
- getColumn: returns the value of the column field
- getValue: returns the value of the value field
- getRectangle: returns a reference to its rectangle field
- setNextValue: that takes a value
- update: change a Cell's value to its next value and update its color
Grid class
The Grid class is used to represent the world. It has some state:
- number of rows and columns in the world
- a GraphWin object
- a list of lists of Cell objects, one for each (row, column) element
in the world. The outer list is index by row number to get an
inner list that contains all Cells in the row (one for each column
in the grid)
The Grid has the following methods:
- constructor: takes the number of rows and columns and creates a
new graphics window, it creates a list of lists of Cell objects
and draws each Cell to the graphics window (use the Cell's getRectangle
method to get a reference to the Rectangle object associated with the
cell so that you can invoke its draw method)
- numRows(): returns the number of rows in the grid
- numCols(): returns the number of columns in the grid
- getValue(row, col): returns the value of the cell at (row, col)
- setNextValue(row, col, val): sets the next value of the cell at (row,col)
- update(): simulates a time step (each Cell's value should be updated
to its next value)
- close(): prompt the user to click to close the graphics window
Using these classes
At the bottom of the grid.py file that contains the Cell and Grid class definitions,
add a main function that will create a new Grid object and then call its methods
to draw a pixelated picture to the graphics window by setting Cell to ON or OFF.
You could draw a smiley face, your initials, a house, anything you'd like.
For example, here is a run of my program that creates a 15 by 15 grid and
draws my initials to it (notice that I have an option to "click to close"
so that the program doesn't exit until the user is done viewing it):
$ python grid
click to close
You do not have to use these classes to draw your initials, but use them to
draw something that is a bit more complicated than a single line.
You can hard-code in the row and column dimensions for the grid for
the drawing something part.
Hints/Getting Started
- The syntax for starting a class definition and its constructor (remember
that data member fields need to be prefixed with self. so that python
can distinguish between them and local variables):
class Cell(object):
# any class-wide definitions go go here
# these are things that can be accessed using Cell.name_of_field
ON = 1
# This is the constructor for a Cell object. Remember that all
# method functions have a 1st hidden parameter, self, that is
# a reference to the object on which the method is invoked.
# A call to this method might look like: c = Cell(3,4,Cell.ON)
def __init__(self, row, col, val):
# here is an example of what other method definitions might look like
# a call to this might look like: c.foo(Cell.OFF)
def foo(self, val)
row = self.row # row is a local variable inside this method
# self.row is a data member associated with this object
- Use the setCoords method of the GraphWin class to set the coordinate
system to correspond to the number of rows and columns. setCoords(x1,y1,x2,y2)
changes the coordinates in the Graphics window from individual pixel values, to
a larger coordinate system where (x1,y1) is in the lower-left corner and (x2,y2)
is in the upper right corner. So, in the constructor of the Grid
class, I might set the coordinate system by doing the following (win is the
Grid data member that refers to the GraphWin object):
self.win.setCoords(0, 0, cols, rows)
By doing this I can position Cell objects using their (row, column) index, rather
than having to perform a conversion from row number to pixel row value and
column number to pixel column value.
- Cell objects should be stored by the Grid class as a list of lists of Cells.
Here is how I could add the first two rows of 3 cells each to the cell list of
lists (of course you should generalize this and put the code in loops so that
it will work for any number of rows and columns):
self.cell_list = []
self.cell_list.append( [Cell(0,0,Cell.OFF), Cell(0,1,Cell.OFF), Cell(0,2,Cell.OFF)])
self.cell_list.append([Cell(1,0,Cell.OFF), Cell(1,1,Cell.OFF), Cell(1,2,Cell.OFF)])
- You can access the Cell object in row r and column c by indexing cell_list with
its row number, r, to get the correct row list, and then indexing into that
list with its col number, c, to get the correct Cell object in the correct
column position:
cell = self.cell_list[r][c]
Optional Extensions
These are not required, so do not try these until you have the basic assignment
complete and correct. Here are some extensions you can try:
- Animate your drawing in some way. You can do this by repeatedly
- setting some Cells' next value to ON or OFF
- calling update
- calling the sleep function to sleep some number of tenths of
a second
from time import sleep
- Generalize your drawing into a function, and implement it so
that what is drawn shows up as roughly the same size and at the
same position in the graphics window no matter the dimensions of
the Grid. For example, if the Grid has 20x20 cells then you will
have to color more cells to draw your picture than if it has
10x10 cells.
Submit
Once you are satisfied with your program, hand it in by typing
handin21 in a terminal window.