 Computer Science

# Grids

A grid is a list of lists. You can think of this as a two-dimensional structure. For example, if we have this grid:

``````numbers = [
[1, 2, 3],
[4, 5, 6]
]``````

Then we can visualize it like this: You can also write it like this:

``numbers = [[1, 2, 3],[4, 5, 6]]``

But it is easier to visualize the first way.

In some programming languages you will see these called an array. In math you might call this a matrix.

## Printing a grid

It helps to think of a grid as a list of rows. Look at the code in `print_grid.py`, which will print out a grid:

``````def print_grid(grid: list[list[int]]):
"""
Print all the items in the grid, so that it looks like a grid
"""
# go through all the rows
for row in grid:
for item in row:
# print the item, but with a space after it, not a newline
print(item, end=' ')
print()

if __name__ == '__main__':
numbers = [
[1, 2, 3],
[4, 5, 6]
]
print_grid(numbers)``````

If we run this code then we get:

``````1 2 3
4 5 6``````

There are a couple of new things going on with this code. Let’s explain them:

### Nested for loops

First, you will notice that we have nested for loops in this code:

``````def print_grid(grid: list[list[int]]):
"""
Print all the items in the grid, so that it looks like a grid
"""
# go through all the rows
for row in grid:
for item in row:
# print the item, but with a space after it, not a newline
print(item, end=' ')
print()``````

Since our grid is a set of nested lists, here is how this works:

Loop #1Loop #2rowitem
11[1, 2, 3]1
12[1, 2, 3]2
13[1, 2, 3]3
21[4, 5, 6]4
22[4, 5, 6]5
23[4, 5, 6]6

### Keyword arguments

Second, you will notice some different syntax with `print()`:

``print(item, end=' ')``

Normally, `print()` ends a line with a newline. But when we use `end=' '` we are telling `print()` to end the line with a space instead.

This will print one of the numbers, followed by a space instead of a newline. This ensures all of the numbers on the same row appear on the same line: `1 2 3`. After we done with all of the numbers in a row, we call `print()`. We don’t tell it to print anything, but print always ends with a newline, unless we override this with `end`, so `print()` by itself prints a newline to end the row.

So technically, the code above prints:

``````1<space>2<space>3<space><newline>
4<space>5<space>6<space><newline>``````

If we leave off `end=' '` in the code above, we would get:

``````1
2
3

4
5
6
``````

We could also have written this code to print a grid:

``````def print_grid(grid: list[list[int]]):
"""
Print all the items in the grid, so that it looks like a grid
"""
# go through all the rows
for row in grid:
for item in row:
# print the item, but with a hyphen after it, not a newline
print(item, end='-')
print()``````

This will print a hyphen after each item:

``````1-2-3-
4-5-6-``````

## Creating a grid

Sometimes you want to create a grid and fill it with whatever you want in each row and column. Here is a function that will do that:

``````def empty_grid(num_rows: int, num_columns: int, value=None) -> list[list]:
"""
Create an empty grid. <num_rows> is the number
of rows in the grid. <num_columns> is the number
of columns in the grid. Fill the grid with None or with
whatever value the caller supplies in <value>.
"""
# create an empty grid
new_grid = []
# keep going until we have created all the rows
while len(new_grid) < num_rows:
new_row = []
# keep going until we have all the columns we need
while len(new_row) < num_columns:
# append an item for this column
new_row.append(value)
# now that we have a full row, append the row
new_grid.append(new_row)
return new_grid``````

The function takes three parameters:

• `num_rows` is the number of rows
• `num_columns` is the number of columns
• `value` is the value to put in each place in the grid

### Keyword argument

When we use `value=None` in the `empty_grid()` function, this is a keyword argument. The first time we call this function:

``````grid1 = empty_grid(3, 2)
print(grid1)``````

we have left off the `value` argument. This tells `empty_grid()` that the first grid should be filled with `None`, since that is the default value we have specified.

The second time we call this function:

``````grid2 = empty_grid(3, 2, value='*')
print(grid2)``````

we have specified `value='*'`. This tells `empty_grid()` that the second grid should be filled with `*` characters.

Thus when we run this code, we get:

``````None None
None None
None None

* *
* *
* *``````

### The rest of the function

Now let’s go back and look at the rest of the code in that function:

``````def empty_grid(num_rows: int, num_columns: int, value=None) -> list[list]:
"""
Create an empty grid. <num_rows> is the number
of rows in the grid. <num_columns> is the number
of columns in the grid. Fill the grid with None or with
whatever value the caller supplies in <value>.
"""
# create an empty grid
new_grid = []
# keep going until we have created all the rows
while len(new_grid) < num_rows:
new_row = []
# keep going until we have all the columns we need
while len(new_row) < num_columns:
# append an item for this column
new_row.append(value)
# now that we have a full row, append the row
new_grid.append(new_row)
return new_grid``````

We have two, nested while loops here. The first while loop creates the rows. The second while loop creates the columns in each row.

Remember, a grid is just a set of nested lists. So we first create `new_grid` to hold an empty list for the entire grid. Then we create a `new_row` to hold each row. We append the columns, then append `new_row` to `new_grid`.

Loop # 1Loop #2new_gridnew_row
11`[]``['*']`
12`[]``['*','*']`
13`[]``['*', '*', '*']`
1done`[['*', '*', '*']]``['*', '*', '*']`
21`[['*', '*', '*']]``['*']`
22`[['*', '*', '*']]``['*','*']`
23`[['*', '*', '*']]``['*', '*', '*']`
2done`[['*', '*', '*'],['*','*','*']]``['*', '*', '*']`

### A complete program

The file called `empty_grid.py` is a complete program showing how this works:

``````def empty_grid(num_rows: int, num_columns: int, value=None) -> list[list]:
"""
Create an empty grid. <num_rows> is the number
of rows in the grid. <num_columns> is the number
of columns in the grid. Fill the grid with None or with
whatever value the caller supplies in <value>.
"""
# create an empty grid
new_grid = []
# keep going until we have created all the rows
while len(new_grid) < num_rows:
new_row = []
# keep going until we have all the columns we need
while len(new_row) < num_columns:
# append an item for this column
new_row.append(value)
# now that we have a full row, append the row
new_grid.append(new_row)
return new_grid

def print_grid(grid: list[list]):
"""
Print all the items in the grid, so that it looks like a grid
"""
# go through all the rows
for row in grid:
for item in row:
# print the item, but with a space after it, not a newline
print(item, end=' ')
print()

if __name__ == '__main__':
grid1 = empty_grid(3, 2)
print_grid(grid1)
print()
grid2 = empty_grid(3, 2, value='*')
print_grid(grid2)``````

When you run this code, it should print:

``````None None
None None
None None

* *
* *
* *
``````

## Reading a grid from a file

You may need to read a grid from a file. We will give you files where the grid is stored with spaces between the values. For example, the file `grid_example.txt` contains this:

``````. * . ! . * .
* . ! . ! . *
. * . ! . * .``````

We have some code in `read_grid.py` that will read this grid and print it back out:

``````def readgrid(filename: str) -> list[list[str]]:
'''
Read a grid from the file given by <filename> and return
a grid object. The file should have all of the grid items
separated by a space in each row.
'''
# read all of the lines of the file given
# by <filename>
with open(filename) as file:

# create an empty grid
grid = []

# for each line
for line in lines:
# split the line into a list, based on spaces
# append that list as the next row in the grid
grid.append(line.split())

# return the grid
return grid

def print_grid(grid: list[list]):
"""
Print all the items in the grid, so that it looks like a grid
"""
# go through all the rows
for row in grid:
for item in row:
# print the item, but with a space after it, not a newline
print(item, end=' ')
print()

if __name__ == '__main__':
print_grid(grid)``````

Note that when we first read all of the lines of the file. Then as we look at each line, we use `split()` to split the line into a list of “words”. Each word is one of the items in the grid. And this list is exactly what we need for a row! This means we can directly append that list to the grid as one of its rows.

If you run this code, you should see:

``````python read_grid.py grid_example.txt
. * . ! . * .
* . ! . ! . *
. * . ! . * .``````

## Grid coordinates

We can think of a grid as a two-dimensional structure that can be accessed using coordinates `(row, column)`: The cell that is colored blue has coordinates `(1, 2)`, meaning it is in row 1, column 2.

Let’s imagine we have this grid:

``````grid = [
[1, 2, 3],
[4, 5, 6]
]``````

If you want to get the value that is in cell coordinate `(1, 2)`:

``value = grid``

This says first get row 1, which is `[4, 5, 6]`, and then get the item in column 2 of that row, which is `6`. So `value = 6`.

If you want to set the value that is in that same cell, then:

``grid = '*'``

This will change the grid so that it looks like this:

``````1 2 3
4 5 *``````

The file called `coordinates.py` contains a complete example:

``````def empty_grid(num_rows: int, num_columns: int, value=None) -> list[list]:
"""
Create an empty grid. <num_rows> is the number
of rows in the grid. <num_columns> is the number
of columns in the grid. Fill the grid with None or with
whatever value the caller supplies in <value>.
"""
# create an empty grid
new_grid = []
# keep going until we have created all the rows
while len(new_grid) < num_rows:
new_row = []
# keep going until we have all the columns we need
while len(new_row) < num_columns:
# append an item for this column
new_row.append(value)
# now that we have a full row, append the row
new_grid.append(new_row)
return new_grid

def print_grid(grid: list[list]):
"""
Print all the items in the grid, so that it looks like a grid
"""
# go through all the rows
for row in grid:
for item in row:
# print the item, but with a space after it, not a newline
print(item, end=' ')
print()

def main():
grid = empty_grid(3, 5, value='.')

print_grid(grid)
print()

grid = '*'
grid = '?'
grid = '!'

print_grid(grid)

if __name__ == '__main__':
main()``````

This code creates an empty grid that has `.` in every cell. Then it replaces a few of these items and prints the grid. You should see this if you run it:

``````. . . . .
. . . . .
. . . . .

. . . . *
. . ? . .
. ! . . .``````

## Checking values in a grid

Sometimes you will want to check if a grid has a certain value. Here is a function to do that:

``````def has_value(grid: list[list], row: int, col: int, value: str) -> bool:
"""
Return true if grid[row][col] == value
"""
# be sure the row is in the grid
# if it is not, return False
if row < 0 or row >= len(grid):
return False

# be sure the column is in the grid
# if it is not, return False
if col < 0 or col >= len(grid[row]):
return False

# return  True ifthe value at (row, column) is equal to value
return grid[row][col] == value``````

Notice that we can first check to be sure we have been asked for a row and column that is in the grid. In other words, if the grid is 3 rows and 2 columns, we can’t check for a value in row 10 or column 5.

The number of rows in the grid is `len(grid)`. The number of columns in a row is `len(grid[row])`.

To see this at work, here is a complete program, which you can find in `has_value.py`:

``````def empty_grid(num_rows: int, num_columns: int, value=None) -> list[list]:
"""
Create an empty grid. <num_rows> is the number
of rows in the grid. <num_columns> is the number
of columns in the grid. Fill the grid with None or with
whatever value the caller supplies in <value>.
"""
# create an empty grid
new_grid = []
# keep going until we have created all the rows
while len(new_grid) < num_rows:
new_row = []
# keep going until we have all the columns we need
while len(new_row) < num_columns:
# append an item for this column
new_row.append(value)
# now that we have a full row, append the row
new_grid.append(new_row)
return new_grid

def has_value(grid: list[list], row: int, col: int, value: str) -> bool:
"""
Return true if grid[row][col] == value
"""
# be sure the row is in the grid
# if it is not, return False
if row < 0 or row >= len(grid):
return False

# be sure the column is in the grid
# if it is not, return False
if col < 0 or col >= len(grid[row]):
return False

# return  True ifthe value at (row, column) is equal to value
return grid[row][col] == value

def create_grid() -> list[list]:
# create an empty grid
grid = empty_grid(3, 5, value='.')

# fill in a few places in the grid
# with some special characters
grid = '*'
grid = '?'
grid = '!'

return grid

def main():
grid = create_grid()
print(has_value(grid, 0, 0, '*'))
print(has_value(grid, -1, 0, '*'))
print(has_value(grid, 1, 2, '?'))

if __name__ == '__main__':
main()``````

If you run this code, it should print:

``````False
False
True``````