Lesson 4 of 8 in the Pre-COSMOS series, 37 slides, on the one NumPy idea that powers camp vision: a 2D array is a grid, and a grayscale image is a grid of brightness numbers running from 0 for dark to 255 for bright. Using numpy as np, you build a grid with np.zeros((rows, cols), dtype=int), read its shape as (rows, cols), index it ROW-FIRST as img[row, col], and "draw" by setting a cell bright, as in img[1,2] = 9. You then find the brightest cell with img.max() and np.unravel_index(img.argmax(), img.shape). The two traps are the classic grid mistakes: reading [x, y] or img[col, row] instead of img[row, col], and the off-by-one IndexError from img[0,5] in a five-column grid, where the columns are numbered 0 to 4. There are five checks and a scaffolded your-turn Maze Rover Simulator build in which students draw a shape into a grid and report the brightest cell. NumPy is a library you install once with pip install numpy, not a piece of hardware. Every snippet was run on CPython 3.12 with numpy 2.3, and the outputs were copied verbatim.
Subject: Python · 63 slides · code lesson
Open the interactive version of this deck · Homework for this lesson
Title
Pre-COSMOS · Lesson 4 of 8
A camera doesn't see a cat - it sees a grid of numbers. Today you build that grid, draw into it, and find its brightest spot, all in NumPy.
Objectives
Last time you met NumPy arrays as fast number-lists. Today they become grids - the shape every camp vision project starts from. By the end you can:
np.zeros((rows, cols), dtype=int).(rows, cols) and index it row first: img[row, col].img[1, 2] = 9.0 is dark, 255 is bright.img.max().np.unravel_index(img.argmax(), img.shape).Warm-up
Discussion prompt
Before we open NumPy as Grids: an Image is a Table of Numbers: without looking back, what was the main idea of The Library Mindset: import, Docs & Small Modules, and what could you do by the end of it that you could not do before?
Hint: One sentence for the idea, one for the skill. If the second one is blank, that is the part to revisit.
Answer:
Pre-COSMOS Lesson 6 of 8 (38 slides): the meta-skill that nobody memorizes a library - you read an example or a doc and adapt it. A module is a toolbox you import; you reach its tools with the SAME dot you've used all camp (module.function).
Concept
Figure (svg): A terminal box showing the command pip install numpy, with an arrow to a small grid of cells, showing that installing the library gives you grids to work with.
NumPy is a Python library - extra code you install once with pip install numpy. It is software, not a camera or a sensor.
We bring it in with one line and a short nickname, then use that nickname all day: import numpy as np.
Counterexample
Discussion prompt
We bring it in with one line and a short nickname, then use that nickname all day: import numpy as np.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Concept
Four stops, each one a piece of the same grid:
np.zeros((rows, cols)).img[row, col].img[1,2]=9.max() + unravel_index.Matching
Match the pairs
From Today's roadmap — match each one to what it actually does. The descriptions have been shuffled.
np.zeros((rows, cols)).img[row, col].img[1,2]=9.max() + unravel_index.Why: Make a grid, Index it, Draw, Find brightest are easy to tell apart while they are sitting next to their descriptions and much harder afterwards, which is what this checks.
Section
Section 1
Concept
A 2D array is just a table of numbers laid out in rows and columns - a grid. NumPy stores it as one tidy block so the whole grid is fast to work with.
grid (2D array) — A rectangle of numbers with rows (top to bottom) and columns (left to right). Every cell has an address: which row, which column.
Analogy
Discussion prompt
Explain What a 2D array is by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
A 2D array is just a table of numbers laid out in rows and columns - a grid. NumPy stores it as one tidy block so the whole grid is fast to work with.
Intuition
Figure (svg): A small grid where each cell shows a brightness number, with a dark corner near 0 and a bright corner near 255, illustrating that an image is a grid of brightness values.
A camera doesn't store a picture of a cat. It stores a grid of brightness numbers - one number per tiny square (a pixel).
In grayscale, 0 means dark (black) and 255 means bright (white). Everything in between is a shade of gray. The picture is the pattern of numbers.
Explain it
Discussion prompt
Explain A grayscale image IS a grid to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
A camera doesn't store a picture of a cat. It stores a grid of brightness numbers - one number per tiny square (a pixel).
Worked example
np.zeros((rows, cols), dtype=int) builds a grid filled with 0 - a totally dark image you can draw onto. Note the double parentheses: (4, 5) is one thing, the shape.
import numpy as np
img = np.zeros((4, 5), dtype=int)
print(img)You get 4 rows and 5 columns, every cell a 0. dtype=int keeps them whole numbers (without it you'd get 0. floats).
| line | what it does | result |
|---|---|---|
| import numpy as np | load the library, nickname np | (no output) |
| np.zeros((4, 5), dtype=int) | build a 4-row, 5-col grid of 0 | the grid below |
| print(img) | show the grid | [[0 0 0 0 0] / [0 0 0 0 0] / [0 0 0 0 0] / [0 0 0 0 0]] |
Comparison
Comparison matrix
From Make a grid of zeros: refill the what it does column from what you know. The rest of the table is as it appeared.
| line | what it does | result |
|---|---|---|
| import numpy as np | load the library, nickname np | (no output) |
| np.zeros((4, 5), dtype=int) | build a 4-row, 5-col grid of 0 | the grid below |
| print(img) | show the grid | [[0 0 0 0 0] / [0 0 0 0 0] / [0 0 0 0 0] / [0 0 0 0 0]] |
Concept
Every grid knows its own size. img.shape reports it as a pair (rows, cols) - rows first, always.
shape — A grid's size as (rows, cols). For our grid, shape is (4, 5): 4 rows, 5 columns. Rows come first in the pair.
Worked example
shape is data about the grid - read it with no parentheses, like an attribute.
import numpy as np
img = np.zeros((4, 5), dtype=int)
print(img.shape)It prints (4, 5) - 4 rows, then 5 columns. Same order you typed into zeros.
| expression | means | value |
|---|---|---|
| img.shape | (rows, cols) | (4, 5) |
| img.shape[0] | number of rows | 4 |
| img.shape[1] | number of columns | 5 |
Trade off
Comparison matrix
From Read the shape: every row here is a choice with a cost. Fill the means column, then say which row you would actually pick and what you give up for it.
| expression | means | value |
|---|---|---|
| img.shape | (rows, cols) | (4, 5) |
| img.shape[0] | number of rows | 4 |
| img.shape[1] | number of columns | 5 |
Ranking
Put in order
These are the steps of How to set up any grid, scrambled. Put them back in order before the next slide shows you.
import numpy as np.np.zeros((rows, cols), dtype=int).img.shape - it reads (rows, cols).zeros, in shape, and (next) in indexing.Why: This is the order the recipe itself gives. Recalling the sequence without the slide in front of you is the difference between recognising the method and being able to run it — most of what goes wrong in practice is a step done out of turn.
Pattern
Every camp vision project starts the exact same way:
import numpy as np.np.zeros((rows, cols), dtype=int).img.shape - it reads (rows, cols).zeros, in shape, and (next) in indexing.Edge cases
Discussion prompt
How to set up any grid works on the cases you have just seen. Push it to the edge: what is the most degenerate input it still handles — empty, zero, one item, everything equal — and what is the first case where it stops being true? Name the case, not just "it breaks".
Hint: Try the smallest legal input, then the largest, then the one where two things collide. Methods are specified at their edges; the middle takes care of itself.
Answer:
Every camp vision project starts the exact same way:
Elimination
Eliminate the wrong options
img = np.zeros((4, 5), dtype=int) What does img.shape return?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: shape echoes the size you built, in (rows, cols) order: (4, 5) means 4 rows and 5 columns. Rows always come first in the pair.
Check
Picture the grid np.zeros((4, 5)) - 4 rows, 5 columns.
Check your understanding
img = np.zeros((4, 5), dtype=int)
What does img.shape return?
Answer: A
Why: shape echoes the size you built, in (rows, cols) order: (4, 5) means 4 rows and 5 columns. Rows always come first in the pair.
Section
Section 2
Concept
To reach one cell you give its address: img[row, col]. The row comes first (which line down), then the column (which spot across).
Counting starts at 0. The top row is row 0; the leftmost column is column 0.
Intuition
Figure (svg): A grid with rows labeled 0 to 3 down the left side and columns labeled 0 to 4 across the top, with the cell at row 1 column 2 highlighted to show img[1,2] goes down to row 1 then across to column 2.
Think of it like a street address: first you find the floor (the row), then the apartment along that floor (the column).
img[1, 2] means: go down to row 1, then across to column 2. That lands on one single cell.
Explain it
Discussion prompt
Explain Row first: down, then across to a student a year behind you. No notation, no jargon they have not met — and it still has to be true.
Hint: If your explanation needs a symbol they have never seen, you are describing the notation rather than the idea.
Answer:
Think of it like a street address: first you find the floor (the row), then the apartment along that floor (the column).
Worked example
Drawing is just assigning to a cell. We set two cells bright and leave the rest dark.
import numpy as np
img = np.zeros((4, 5), dtype=int)
img[1, 2] = 9
img[2, 1] = 5
print(img)The 9 lands at row 1, col 2; the 5 lands at row 2, col 1. Notice they are NOT in the same place - order matters.
| after this line | the grid is |
|---|---|
| np.zeros((4, 5)) | [[0 0 0 0 0] / [0 0 0 0 0] / [0 0 0 0 0] / [0 0 0 0 0]] |
| img[1, 2] = 9 | [[0 0 0 0 0] / [0 0 9 0 0] / [0 0 0 0 0] / [0 0 0 0 0]] |
| img[2, 1] = 5 | [[0 0 0 0 0] / [0 0 9 0 0] / [0 5 0 0 0] / [0 0 0 0 0]] |
Worked example
Reading uses the same address. img[1, 2] gives back the 9 we stored; img[2, 1] gives the 5.
import numpy as np
img = np.zeros((4, 5), dtype=int)
img[1, 2] = 9
img[2, 1] = 5
print(img[1, 2])
print(img[2, 1])Same address writes AND reads. The row index always comes first.
| expression | row, col | value |
|---|---|---|
| img[1, 2] | row 1, col 2 | 9 |
| img[2, 1] | row 2, col 1 | 5 |
| img[0, 0] | row 0, col 0 (still dark) | 0 |
Anomaly
Predict first
A student writes this, and it looks reasonable:
You think in [x, y] (across, then down) like a math graph, so you write img[2, 1] to mean 'column 2, row 1'.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: You read the address as [col, row].
Always read the address as [row, col] - row first.
Why: You read the address as [col, row]. But NumPy reads it as [row, col] - so you actually land on row 2, col 1.
Trap
You think in [x, y] (across, then down) like a math graph, so you write img[2, 1] to mean 'column 2, row 1'.
Write img[2, 1] hoping for the cell at row 1, col 2
Why: You read the address as [col, row]. But NumPy reads it as [row, col] - so you actually land on row 2, col 1.
You touch the wrong cell - the 5, not the 9
Why: img[2, 1] is row 2, col 1 (value 5). The cell you wanted, row 1 col 2 (value 9), needed img[1, 2].
Always read the address as [row, col] - row first.
Write img[1, 2] for row 1, column 2
Why: Down to row 1, then across to column 2. That is the cell holding 9.
Say it out loud: 'row one, column two'
Why: Naming row first every time kills the x/y habit. Grids are [row, col], not [x, y].
Break the constraint
Discussion prompt
The rule this trap just fixed:
Down to row 1, then across to column 2. That is the cell holding 9.
Now break it on purpose. Build a case that violates it and follow the consequences until something visibly fails. Where does the failure first show up — and would you have noticed it if you had not been looking?
Hint: The dangerous rules are the ones whose violation still produces an answer. If yours fails loudly, try to find one that fails quietly.
Answer:
You read the address as [col, row]. But NumPy reads it as [row, col] - so you actually land on row 2, col 1.
Check
The grid has a 9 at row 1, col 2 and a 5 at row 2, col 1.
Check your understanding
img = np.zeros((4, 5), dtype=int)
img[1, 2] = 9
img[2, 1] = 5
What does img[1, 2] return?
Answer: A
Why: img[1, 2] is row 1, column 2 - exactly where we stored the 9. Reading uses the same row-first address as writing.
Anomaly
Predict first
A student writes this, and it looks reasonable:
Your grid has 5 columns, so you reach for img[0, 5] expecting the 5th column.
It is wrong. Say what breaks — and say it before you turn the page.
Correct: You counted columns as 1..5. But indexing starts at 0, so 5 columns are numbered 0, 1, 2, 3, 4 - there is no column 5.
The last valid index is count minus one.
Why: You counted columns as 1..5. But indexing starts at 0, so 5 columns are numbered 0, 1, 2, 3, 4 - there is no column 5.
Trap
Your grid has 5 columns, so you reach for img[0, 5] expecting the 5th column.
Write img[0, 5] on a 5-column grid
Why: You counted columns as 1..5. But indexing starts at 0, so 5 columns are numbered 0, 1, 2, 3, 4 - there is no column 5.
Crashes: IndexError: index 5 is out of bounds for axis 1 with size 5
Why: Axis 1 is the columns. 'size 5' means 5 columns (0..4); index 5 is one step past the last. Off-by-one.
The last valid index is count minus one.
Use img[0, 4] for the last column
Why: 5 columns -> valid indices 0, 1, 2, 3, 4. The rightmost is 4, not 5.
Check the range against img.shape
Why: shape is (4, 5): rows 0..3, columns 0..4. Stay inside count - 1 on each axis.
Two truths and a lie
Sort into buckets
Some of these hold up and some are the exact mistakes this lesson is built to prevent. Sort them.
import numpy as np.; Four stops, each one a piece of the same grid:; A 2D array is just a table of numbers laid out in rows and columns - a grid. NumPy stores it as one tidy block so the whole grid is fast to work with.[x, y] (across, then down) like a math graph, so you write img[2, 1] to mean 'column 2, row 1'.; Your grid has 5 columns, so you reach for img[0, 5] expecting the 5th column.Prediction
Predict first
img = np.zeros((4, 5), dtype=int) print(img[0, 5]) What happens?
Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.
Correct: IndexError: index 5 is out of bounds for axis 1 with size 5
Why: 5 columns are numbered 0..4, so column index 5 is one past the end. NumPy raises IndexError on axis 1 (the columns), which has size 5.
Check
The grid is 4 rows by 5 columns.
Check your understanding
img = np.zeros((4, 5), dtype=int)
print(img[0, 5])
What happens?
Answer: A
Why: 5 columns are numbered 0..4, so column index 5 is one past the end. NumPy raises IndexError on axis 1 (the columns), which has size 5.
Section
Section 3
Concept
In a grayscale image, each cell's number is how bright that pixel is. 0 is fully dark (black); 255 is fully bright (white). Bigger number = brighter pixel.
pixel value — The brightness stored in one cell, 0..255. 0 = black, 255 = white, in-between = a gray. A bright spot is just a big number.
Matching
Match the pairs
Match each term to the definition this lesson gave it — not the one you would guess from the word.
Why: These are the working definitions of grid (2D array), shape, pixel value as NumPy as Grids: an Image is a Table of Numbers uses them. Pairing them correctly is the test of whether you could state each one with the slide switched off.
Intuition
Start with an all-0 grid (a black image). To draw a shape, you set the cells along it to a high number - they 'light up' against the dark background.
So a white dot on black is one cell set to 255 while its neighbors stay 0. Pictures are made by choosing which numbers go where.
Analogy
Discussion prompt
Explain Drawing = turning cells bright by analogy to something with no Python in it at all — a queue, a recipe, a map, a bank balance, whatever fits. Then say where your analogy breaks.
Hint: An analogy that never breaks is not an analogy, it is the same idea wearing a hat. Find the seam — that is the part that is actually new.
Answer:
Start with an all-0 grid (a black image). To draw a shape, you set the cells along it to a high number - they 'light up' against the dark background.
Worked example
img.max() scans the whole grid and returns the single largest number - the brightest pixel's value.
import numpy as np
img = np.zeros((4, 5), dtype=int)
img[1, 2] = 9
img[2, 1] = 5
print(img.max())Of all the cells (mostly 0, plus a 9 and a 5), the biggest is 9.
| cells present | the largest is | img.max() |
|---|---|---|
| 0 (many) | not the max | - |
| 5 at (2, 1) | not the max | - |
| 9 at (1, 2) | the brightest | 9 |
Pattern
Step through it
Step through Find the brightest value: max() one row at a time. What is driving the change, and what would the row after the last one be?
Concept
img.max() tells you the brightest value (9) but not where it is. For the location we need one more tool.
img.argmax() gives the position - but as a single counted-from-the-top-left number (7 here). np.unravel_index turns that flat number into a (row, col) pair.
Counterexample
Discussion prompt
img.max() tells you the brightest value (9) but not where it is. For the location we need one more tool.
That is stated as though it always holds. Do one of two things: produce a case where it fails, or say precisely what rules such a case out. "It just does" is not on the menu.
Hint: Hunt at the extremes first — zero, one, negative, empty, equal. If every extreme survives, the reason they survive is the proof.
Answer:
img.argmax() gives the position - but as a single counted-from-the-top-left number (7 here). np.unravel_index turns that flat number into a (row, col) pair.
Worked example
np.unravel_index(img.argmax(), img.shape) returns the (row, col) of the brightest cell. Pass it the grid's shape so it knows the width.
import numpy as np
img = np.zeros((4, 5), dtype=int)
img[1, 2] = 9
img[2, 1] = 5
spot = np.unravel_index(img.argmax(), img.shape)
print(spot)It prints (np.int64(1), np.int64(2)). Don't let np.int64 scare you - read it as plain (1, 2): row 1, column 2. (Use .item() if you want clean Python ints.)
| step | value | meaning |
|---|---|---|
| img.argmax() | 7 | brightest cell, counted flat from top-left |
| np.unravel_index(7, (4, 5)) | (np.int64(1), np.int64(2)) | that flat spot as (row, col) |
| read as | (1, 2) | row 1, column 2 - matches where we put the 9 |
Comparison
Comparison matrix
From Find WHERE: unravel_index: refill the meaning column from what you know. The rest of the table is as it appeared.
| step | value | meaning |
|---|---|---|
| img.argmax() | 7 | brightest cell, counted flat from top-left |
| np.unravel_index(7, (4, 5)) | (np.int64(1), np.int64(2)) | that flat spot as (row, col) |
| read as | (1, 2) | row 1, column 2 - matches where we put the 9 |
Prediction
Predict first
To get BOTH the brightest value and its (row, col) location in a grid img, you use:
Answer it in your own words, now, with nothing to choose from. The options are on the next slide — and picking the right one off a list is an easier skill than producing it.
Correct: img.max() for the value, np.unravel_index(img.argmax(), img.shape) for the (row, col)
Why: img.max() gives the brightest value; img.argmax() gives a flat position, and np.unravel_index turns that plus img.shape into a (row, col) pair. Two tools, two jobs.
Check
max gives the value; unravel_index(argmax, shape) gives the place.
Check your understanding
To get BOTH the brightest value and its (row, col) location in a grid img, you use:
Answer: A
Why: img.max() gives the brightest value; img.argmax() gives a flat position, and np.unravel_index turns that plus img.shape into a (row, col) pair. Two tools, two jobs.
Elimination
Eliminate the wrong options
In a grayscale grid, one cell is 0 and another is 250. What do those mean?
3 of these 4 are wrong. Strike them one at a time, and say what rules each one out before you strike the next. The survivor is the answer.
Survives elimination: A
Why: Brightness goes from 0 (fully dark/black) up to 255 (fully bright/white). 0 is dark and 250 is almost white - a bright pixel.
Check
Grayscale runs 0..255.
Check your understanding
In a grayscale grid, one cell is 0 and another is 250. What do those mean?
Answer: A
Why: Brightness goes from 0 (fully dark/black) up to 255 (fully bright/white). 0 is dark and 250 is almost white - a bright pixel.
Section
Section 4 · build it yourself
Concept
Build a tiny Maze Rover map as a grid: dark floor (0) with a few bright cells marking the rover's trail. Then report the brightest cell. Type every line yourself, run after each line, and read the errors - don't erase them.
| # | do this | tool you'll use |
|---|---|---|
| 1 | make a 5x6 grid of zeros | np.zeros((rows, cols), dtype=int) |
| 2 | draw a trail: set a few cells bright | img[row, col] = 255 |
| 3 | print the grid and its shape | print(img), img.shape |
| 4 | report the brightest cell + location | img.max(), np.unravel_index(...) |
Worked example
Your turn: make a grid with 5 rows and 6 columns, all zeros, and print its shape. Say out loud what shape you expect before you run it.
Hint: np.zeros takes the shape as (rows, cols) in double parentheses; read the size back with .shape.
import numpy as np
grid = np.zeros((5, 6), dtype=int)
print(grid.shape)| line | prints |
|---|---|
| np.zeros((5, 6), dtype=int) | (a 5x6 grid of 0s) |
| print(grid.shape) | (5, 6) |
Worked example
Your turn: light up a few cells to 255 to draw the rover's path. Predict which cells will show 255 before you print.
Hint: each bright cell is an assignment grid[row, col] = 255 - row first, then column.
grid[1, 1] = 255
grid[1, 2] = 255
grid[2, 2] = 255
print(grid)| set | lands at row, col |
|---|---|
| grid[1, 1] = 255 | row 1, col 1 |
| grid[1, 2] = 255 | row 1, col 2 |
| grid[2, 2] = 255 | row 2, col 2 |
Worked example
Your turn: report the brightest value and where it is. Predict the value first - what is the biggest number you put in?
Hint: grid.max() gives the value; np.unravel_index(grid.argmax(), grid.shape) gives the (row, col).
print(grid.max())
spot = np.unravel_index(grid.argmax(), grid.shape)
print(spot)| line | prints | read it as |
|---|---|---|
| print(grid.max()) | 255 | brightest value |
| np.unravel_index(grid.argmax(), grid.shape) | (np.int64(1), np.int64(1)) | (1, 1) - the FIRST 255 we set |
| print(spot) | (np.int64(1), np.int64(1)) | row 1, column 1 |
Trade off
Comparison matrix
From Milestone 3 — find the brightest cell: every row here is a choice with a cost. Fill the prints column, then say which row you would actually pick and what you give up for it.
| line | prints | read it as |
|---|---|---|
| print(grid.max()) | 255 | brightest value |
| np.unravel_index(grid.argmax(), grid.shape) | (np.int64(1), np.int64(1)) | (1, 1) - the FIRST 255 we set |
| print(spot) | (np.int64(1), np.int64(1)) | row 1, column 1 |
Worked example
Your turn: put it together - make the map, draw the trail, print it, and report the brightest cell. Predict the final two lines before running.
import numpy as np
grid = np.zeros((5, 6), dtype=int)
grid[1, 1] = 255
grid[1, 2] = 255
grid[2, 2] = 255
print(grid)
print("shape:", grid.shape)
print("brightest:", grid.max())
print("at:", np.unravel_index(grid.argmax(), grid.shape))| output line | prints |
|---|---|
| print(grid) | 5 rows; 255s at (1,1) (1,2) (2,2), rest 0 |
| shape: | shape: (5, 6) |
| brightest: | brightest: 255 |
| at: | at: (np.int64(1), np.int64(1)) |
If yours prints brightest: 255 and at: (np.int64(1), np.int64(1)) - you just built and read an image as a grid of numbers.
Comparison
Comparison matrix
From Milestone 4 — full program: refill the prints column from what you know. The rest of the table is as it appeared.
| output line | prints |
|---|---|
| print(grid) | 5 rows; 255s at (1,1) (1,2) (2,2), rest 0 |
| shape: | shape: (5, 6) |
| brightest: | brightest: 255 |
| at: | at: (np.int64(1), np.int64(1)) |
Worked example
Explain your program out loud, line by line: which line makes the grid, which lines draw (set cells bright), and which line reads the size?
Point to the (np.int64(1), np.int64(1)) and say what it means: row 1, column 1 - the first cell you turned bright. Note it's the FIRST 255, since ties go to the earliest cell.
You can now beat both of today's traps: reading [row, col] (not [x, y]), and staying inside 0..count-1 on each axis.
Connect it up
Draw it
One page, no notation unless you need it: draw how these connect — A Grid is a Table of Numbers · Indexing: Row First · Brightness: 0 Dark .. 255 Bright · Your Turn: Maze Rover Simulator. Put an arrow wherever one of them is what makes another possible, and label the arrow with why.
Recap
np.zeros((rows, cols), dtype=int) and read its size with .shape as (rows, cols).img[row, col] - down, then across, counting from 0.img[1, 2] = 255); read brightness as 0 dark .. 255 bright.img.max() for the value, np.unravel_index(img.argmax(), img.shape) for the (row, col).| you want to... | you write |
|---|---|
| make a dark grid | np.zeros((4, 5), dtype=int) |
| read the size | img.shape -> (4, 5) |
| reach one cell | img[row, col] (row first) |
| draw a bright cell | img[1, 2] = 255 |
| find brightest + where | img.max(), np.unravel_index(img.argmax(), img.shape) |
Next time (Lesson 5): you'll work a whole row or column at once with slicing - img[0, :], img[:, 2] - to brighten lines, not just single cells.
Want this taught 1-on-1? Alexander tutors Python — $55/session, free consultation.