Two-dimensional arrays

Rows and columns: the depth grid as a list of lists.

F3.5Strings, lists and recordsGCSE15 min

Covers OCR J277 2.2.3 Additional programming techniques · AQA 8525 3.2.6 Data structures · Edexcel 1CP2 6.3.1 Primitive and structured data types

Do this lesson in the simulator

Some data comes in rows and columns: a spreadsheet, a game board, a seating plan. BugBot's depth sensor is one of these. It sees an 8 by 8 grid of distances, like a very low-resolution picture where every pixel is a distance instead of a colour. A list of lists stores rows and columns, and exams call it a two-dimensional array.

A list of lists

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

board = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
print(board[0])        # the first row
print(board[1][2])     # row 1, column 2
board[2][0] = 0        # change one item
print(board)
print(len(board), "rows of", len(board[0]))

Run this in the simulator

Each item of board is itself a list: a row. board[1] is the second row, and board[1][2] is the item in column 2 of that row, which is 6. Read the two indexes as row, then column.

A 3 by 3 grid with row indexes down the side and column indexes along the top, with row 1 shaded and board[1][2], which is 6, outlinedcolumnrow001122123456789board[1] is this rowboard[1][2] is 6first index: which row. second: which column
board[1] picks a whole row, shaded, and the second index picks one item from that row. So board[1][2] is row 1, column 2: the 6.

Visiting every item

Two indexes need two loops, one inside the other:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

board = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in range(len(board)):
    for col in range(len(board[row])):
        print(board[row][col], end=" ")
    print()

total = 0
for row in board:
    for item in row:
        total = total + item
print("total", total)

Run this in the simulator

The first pair of loops uses indexes, the second uses the items directly. Both visit all nine.

The nested loops visit the grid a row at a time, left to right along row 0, then row 1, then row 2123row 0456row 1789row 2print()print()print()inner loop: along a rowouter loop: down to the next row
The inner loop runs all the way along one row, then the outer loop moves down a row and the inner loop starts again from column 0. That is 9 visits in all, and print() ends each line of output.

The depth grid

tof_grid() gives the sensor's 64 distances as one flat list, row after row. Row 0 is the top of the view and row 7 the bottom; column 0 is the left and column 7 the right. Rows 2 and 3 look level, straight ahead. Turning the flat list into rows is a good use of what you know:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

flat = tof_grid()
grid = []
for r in range(8):
    grid.append(flat[r * 8 : r * 8 + 8])

for row in grid:
    print(row)
print("straight ahead, level:", grid[3][3], "and", grid[3][4])

Run this in the simulator

Look at the printout. The top rows see over everything, so they read the sensor's maximum, 400. The level rows see the box off to the right, 26 or 27 cm away in columns 6 and 7, and the far edge of the mat, about 81 cm, everywhere else. The bottom rows see the mat just in front of the robot, so they are small and steady: 15, 10 and 8 cm.

The 64 flat readings arranged as 8 rows of 8, each cell showing its index in the flat list, with the level rows 2 and 3 shadedcolumnleftright01234567row 0, top01234567flat[0:8]row 189101112131415flat[8:16]row 2, level1617181920212223flat[16:24]row 3, level2425262728293031flat[24:32]row 43233343536373839flat[32:40]row 54041424344454647flat[40:48]row 64849505152535455flat[48:56]row 7, bottom5657585960616263flat[56:64]grid[3][4] is flat[3 * 8 + 4], which is flat[28]
The numbers here are positions in the flat list, not distances. Each row takes the next 8, so row r is flat[r * 8 : r * 8 + 8]. The shaded level rows, 2 and 3, look straight ahead.

Finding something in the grid

To steer, you want the nearest reading in the level rows and which column it is in:

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

flat = tof_grid()
grid = []
for r in range(8):
    grid.append(flat[r * 8 : r * 8 + 8])

best = 999
best_col = 0
for r in [2, 3]:
    for c in range(8):
        if grid[r][c] < best:
            best = grid[r][c]
            best_col = c
print(f"nearest: {best} cm in column {best_col}")

Run this in the simulator

This is the largest-so-far pattern from lesson F2.7, turned round to find the smallest, over two dimensions. best starts impossibly large so the first reading replaces it.

Task: nearest column

There is a box off to one side. Without moving, find the smallest reading in the level rows (2 and 3) of the depth grid and print nearest: <cm> cm in column <n>.

# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()

flat = tof_grid()
best = min(flat)
print("nearest:", best)

Challenges

  1. Print the grid with every reading over 100 shown as --, so the shape of the box stands out.
  2. Work out the average of each column in the level rows, and say which side of the robot is more crowded.
  3. Make a 3 by 3 noughts and crosses board of ".", put an X in the middle, and print it as three lines.

Past paper questions on this

Real exam questions that test this, each worked through with a program you can run.