Today: grid, grid testing, work on one function at a time

## Testing Thought For Today

• Divide and Conquer - our mantra
• Run movie.py
• Don't want to debug as one big piece
• Decompose into functions
• One function in isolation
• Huge time saver
• Today: techniques to drill down on one function

## Grid Utility Code

• Done lots of 2d algorithms on Image
Always with RGB input/output
Nice, but just one corner of 2d algorithms
• Grid - generic 2d facility
• In the grid.py file
• 2d storage of str, int, .. anything
• Reference: Grid Reference

## Grid Functions

• `grid = Grid(4, 3)` - create, all None initially
• Zero based x,y coordinates for every square in the grid:
origin at upper left
x: 0..grid.width - 1
y: 0..grid.height - 1
• `grid.set(0, 0, 'a')` - set at x,y
• `grid.get(0, 0)` - returns contents at x,y
• `grid.in_bounds(2, 2)` - returns True if in bounds

## set_edges()

Implement set_edges()

```def set_edges(grid):
"""
Set all the squares along the left edge (x=0) to 'a'.
Do the same for the right edge.
Return the changed grid.
"""
pass
```

Solution code:

```def set_edges(grid):
"""
Set all the squares along the left edge (x=0) to 'a'.
Do the same for the right edge.
Return the changed grid.
"""
for y in range(grid.height):
grid.set(0, y, 'a')
grid.set(grid.width - 1, y, 'a')
return grid

```

Q: How can we tell if that code works? With our image examples, at least you could look at the output, although that was not a perfect solution either. Really we want to be able to write test for a small case with visible data.

## Grid Literals

There's a syntax to write a grid out in Python code. It's a little funny looking, but it's fine for small grids. Suppose we have this 3 by 2 grid Here is the nested-list "literal" representation of that grid:

```[['a', None, 'a'], ['b', None, 'b']]
```

The grid is shown as a series of rows, row-0, row-1, .... Each row within square brackets. The special value `None` is in the empty squares. So the first thing you see is the top row, Then the next row and so on.

## Grid Literal Construction

The Grid code understands how to build a grid from a literal. This lets us throw together a grid in one line. This will be handy for testing!

```grid = Grid.build([['a', None, 'a'], ['b', None, 'b']])
```

## Recall: Write Test for set_edges()

• Now we can write a test for set_edges()
• Literal format used to create and check grid values
• Can set a variable within the >>> Doctest, use on later line
`>>> grid = Grid.build([['b', 'b', 'b'], ['x', 'x', 'x']])`
• Now by typing ctrl-shift-r .. we can test this function in isolation

Here are the key 3 lines added to set_edges() that make the Doctest: (1) create a grid, (2) call fn, (3) write out the expected result of the function call

```    >>> grid = Grid.build([['b', 'b', 'b'], ['x', 'x', 'x']])
>>> set_edges(grid)
[['a', 'b', 'a'], ['a', 'x', 'a']]
```

## Random Module

• Generate pseudo-random numbers
• A "module" we'll learn in detail later
• randrange(n): 0..n-1 at random
• choice('string'): select 1 char at random
```>>> import random   # starter code has this already
>>> random.randrange(10)
1
>>> random.randrange(10)
3
>>> random.randrange(10)
9
>>> random.randrange(10)
1
>>> random.randrange(10)
8
>>> random.choice('doofus')
'o'
>>> random.choice('doofus')
'u'
>>> random.choice('doofus')
'o'
>>> random.choice('doofus')
'o'
>>> random.choice('doofus')
's'
>>> random.choice('doofus')
's'
>>> random.choice('doofus')
'o'
>>> random.choice('doofus')
's'
```

## random_right() Function

Provided to fill in letters at right

```def random_right(grid):
"""
Set the right edge of the grid to some
random letters from 'doofus'.
(provided)
"""
for y in range(grid.height):
if random.randrange(10) == 0:
char = random.choice('doofus')
grid.set(grid.width - 1, y, char)
return grid
```

## scroll_left(grid)

• Real algorithmic code
• Kind of tricky
• The tests are going to save us here
• First work out the ideas without code

## Scroll Ideas

• For every x,y
• Want to "move" the 'd' or whatever one to the left
• 3 by 2 grid, move the 'd', 'N' represents None squares

Before:

```NNd
NNN
```

After:

```NdN
NNN
```
• Write some code in scroll_left()
access (x-1), so long as x > 0
I'll leave some bugs in there
• Run movie.py to see it animate
• Fun to watch, but not good for debugging

## scroll_left() v1 with bugs

```def scroll_left(grid):
"""
Implement scroll_left as in lecture notes.
"""
for y in range(grid.height):
for x in range(grid.width):
val = grid.get(x, y)
if x > 0 and val != None:
# x,y is source to copy from
grid.set(x - 1, y, val)
return grid
```
• Run this in the full GUI. It's bug, but at least it's funny.
• Lesson 1: small bugs can create big effects
• Running the whole program .. not a good way to debug
• Want: tiny, isolated, visible case to look at

```[['a', 'b', 'c'], ['d', None, None]]
```

What should result look like for that input case? You should be able to write out the "after" case for an algorithm you are building.

After:

```[['b', 'c', None], [None, None, None]]
```

## Here is the Doctest added in. Use this to get this algorithm perfect.

```def scroll_left(grid):
"""
Implement scroll_left as in lecture notes.
>>> grid = Grid.build([['a', 'b', 'c'], ['d', None, None]])
>>> scroll_left(grid)
[['b', 'c', None], [None, None, None]]
"""
```

## Work on scroll_left()

• Bug has to do with blanking out a square copying from
• Also dealing with x=0
• Use ctrl-shift-r .. fix this thing
• Notice how handy it is that it shows us the "got" .. think about our algo
• In lecture I have to go through this a bit fast

## scroll_left() Solution

```def scroll_left(grid):
"""
Implement scroll_left as in lecture notes.
>>> grid = Grid.build([['a', 'b', 'c'], ['d', None, None]])
>>> scroll_left(grid)
[['b', 'c', None], [None, None, None]]
"""
for y in range(grid.height):
for x in range(grid.width):
val = grid.get(x, y)
if x > 0 and val != None:
# x,y is source to copy from
val = grid.get(x, y)
grid.set(x - 1, y, val)
grid.set(x, y, None)
return grid
```

## Testing Big Picture

• Run whole program, see a bug
• Hard to debug with all the code at once
• Write a test per function
• Test case:
• 1. Tiny
• 2. Isolated
• 3. Visible input/output
• This is the easiest way to get code working
• Notice: we debugged on scroll_left() with its tiny case
• Once that worked, the big thing worked perfectly

## Demo: Sand Program

• Look at the whole program .. looks like a lot
• This program is very decomposable
• It's 4 functions
• Separate testing, Grid literals
• You can do this
• Something to show your parents