Skip to content
CCC Python Course

Grid basics

Module
M3.7
Lesson
1 of 1
Reading time
4 min

In this lesson

  • Read and store a rectangular grid of characters.
  • Use row and column indices to access and update grid cells.
  • Iterate over rows and columns with nested loops.
  • Apply grid basics to search and count features in a two-dimensional space.

Many problems give you data arranged in rows and columns. A map is a grid. A game board is a grid. A photo is a grid of pixels. You need a way to read, store, and navigate grids.

A grid is a list of lists. The outer list holds the rows. Each inner list holds the cells in one row. If the grid has 3 rows and 4 columns, you have a list of 3 lists, each with 4 elements.

Grids appear everywhere in competitive programming. Maps use grids to represent terrain. Mazes are grids. Game boards like chess or checkers are grids. Whenever your problem involves a 2D structure, you are working with a grid. Understanding how to store and navigate grids is essential.

Reading and storing a grid

Your input tells you the number of rows and columns first, then gives you the grid one row at a time.

Say you read:

3 4..###.......

The first line says 3 rows and 4 columns. The next 3 lines are the rows of the grid. You can store this in a list of strings:

Python
r, c = map(int, input().split())grid = []for i in range(r):    row = input()    grid.append(row)

Now grid[0] is the first row "..##", grid[1] is "#...", and grid[2] is "....". The row is a string, so grid[0][0] is "." and grid[0][2] is "#".

If the cells are numbers or need to change, store each row as a list of integers instead:

Python
r, c = map(int, input().split())grid = []for i in range(r):    row = list(map(int, input().split()))    grid.append(row)

Now grid[0] is a list [1, 2, 3, 4] and grid[0][0] is the integer 1.

Indexing rows and columns

Think of the grid as a table. The first index is the row (top to bottom, starting at 0). The second index is the column (left to right, starting at 0).

grid[0][0]   grid[0][1]   grid[0][2]   grid[0][3]grid[1][0]   grid[1][1]   grid[1][2]   grid[1][3]grid[2][0]   grid[2][1]   grid[2][2]   grid[2][3]

To access a cell, use two indices: grid[row][col]. To change a cell, assign to it: grid[row][col] = new_value.

Nested loops over a grid

To visit every cell, use two loops. The outer loop picks a row, the inner loop picks a column:

Python
grid = ["..#", ".#.", "#.."]r, c = len(grid), len(grid[0])for row in range(r):    for col in range(c):        cell = grid[row][col]        # Do something with this cell

This visits cells left to right, top to bottom. It processes row 0 columns 0–3, then row 1 columns 0–3, then row 2 columns 0–3.

examples/count_marks.py
r, c = map(int, input().split())grid = []for i in range(r):    row = input()    grid.append(row)
count = 0for row in range(r):    for col in range(c):        if grid[row][col] == '#':            count += 1
print(count)

Input

3 4
..##
#...
....

Output

3
Counting marked cells in a grid

The program counts how many cells hold '#'. It loops through every row and every column, and increments a counter each time it finds a '#'.

Finding a target in a grid

Here is another example. You have a grid and need to find the position of a specific value, say the character 'S'. You scan rows and columns, and when you find it, you return the row and column.

Python
r, c = map(int, input().split())grid = []for i in range(r):    row = input()    grid.append(row)
start_row, start_col = -1, -1for row in range(r):    for col in range(c):        if grid[row][col] == 'S':            start_row, start_col = row, col
print(start_row, start_col)

Once you find the target, you could immediately return, but this version scans the entire grid. This works when you need to process all cells or find all occurrences.

The nested loop is fundamental to grid problems. Counting, searching, and modifying a grid all rely on this same pattern of visiting every cell. The row-major order used here, rows first, then columns within each row, is the standard way to iterate.

Checking a cell's neighbors

Many grid problems ask about a cell's neighbors: the cells directly above, below, left, and right of it. Writing out all four by hand gets repetitive, so it helps to list the four moves as pairs of row and column offsets and loop over them.

Python
grid = ["..#", ".#.", "#.."]r, c = len(grid), len(grid[0])moves = [(-1, 0), (1, 0), (0, -1), (0, 1)]
row, col = 1, 1for dr, dc in moves:    nr, nc = row + dr, col + dc    if 0 <= nr < r and 0 <= nc < c:        print(nr, nc, grid[nr][nc])

Each pair in moves is a direction: (-1, 0) is up a row, (1, 0) is down a row, (0, -1) is left a column, and (0, 1) is right a column. Adding a pair to the current row and column gives the neighbor's position. The bounds check 0 <= nr < r and 0 <= nc < c matters more here than anywhere else in this lesson. A neighbor of a cell on the edge of the grid can fall outside it. The moment your current cell sits on row 0, row r - 1, column 0, or column c - 1, reading grid[nr][nc] without checking first crashes the program with an index error.

Common mistakes

One mistake is mixing up rows and columns. grid[row][col] is correct. grid[col][row] is wrong, and reads from the wrong cells. Remember: first index is the row, second is the column. This is easy to get backwards under pressure during a contest.

Another mistake is forgetting that Python indices start at 0. The first row is row 0, not row 1. The first column is column 0. If you accidentally use grid[r] when r is the number of rows, Python crashes with an index out of range error. Always think in terms of "row 0, row 1, row 2" instead of "first row, second row, third row".

A third mistake is reading the grid wrong. Each row must be a separate input line. If you read the entire grid as one space-separated list, you have lost the row structure. Read row by row and store each row separately. The input format is strict: it gives the count, then the rows.

A fourth mistake is using the wrong loop variable. If you loop for i in range(r), your variable is i, not row. Use descriptive names to avoid confusion. for row in range(r) is clearer than for i in range(r), and when you write grid[row][col], the meaning is obvious.

When iterating, check your loop bounds. If the grid has 3 rows and 4 columns, rows range from 0 to 2 and columns range from 0 to 3. Use range(r) for rows and range(c) for columns. Off-by-one errors are common here.

Practice

Try these on the judge. Each link opens the problem on WMOJ.

  1. 2021 S1
    Crazy Fencing (opens on WMOJ in a new tab) WMOJ

    Sum a property across a 2D layout of shapes, row by row.

  2. 2023 J4
    Trianglane (opens on WMOJ in a new tab) WMOJ(same problem as 2023 S1)

    Count features across a triangular grid pattern.