Where am I? Coordinates and moving on a grid
Before a robot can go anywhere, it needs a way to say where it is. Two numbers do the job. Moving becomes adding, walls become a check, and "how far?" becomes a tiny formula.
Start here
Give every square a pair of numbers (x, y), and moving one step is just adding a small change to them.
The problem
You tell Robo-Helper: "the red block is near the top, a bit to the right of you." The robot has no idea what that means. "Near" how near? "Top" of what? It needs an exact address for every square, and exact rules for what "go east" does to that address. It also needs to not drive through walls.
The idea in one picture
Think of a game of Battleships, or seats in a cinema: "row F, seat 12". One number says how far across, one says which row. Once every square has an address, "move one seat right" just means "seat number + 1". The robot's room works the same way.
Words to know
- Coordinates
- A pair of numbers (x, y) that gives the address of a square. x = across, y = down.
- Tuple
- Python's way of keeping a few values together, like
(2, 3). You can't change it, only make a new one. - Dictionary
- A lookup table.
DIRECTIONS["E"]looks up "E" and gives back(1, 0). - Path
- The list of every square the robot stood on, in order.
- Manhattan distance
- How many steps apart two squares are if you can only move along the grid, never diagonally.
Going deeper: everything below is the full lesson. Keep squared paper next to you: you'll draw on it.
1. The problem
Here is the room again. The robot R is in the top-left area, the red block r is right next to it, and the bin B is bottom right.
##########
#Rr......#
#..##....#
#..#..b..#
#....##..#
#g.......#
#.#....B.#
##########
To drive to the bin, the robot needs answers to four questions:
- Where am I, exactly?
- If I take one step east, where will I be?
- Is that square a wall? If so, don't go.
- How far am I from the thing I want?
Each question becomes one function in today's homework.
2. First principles
Step 1: give every square an address
Number the columns from the left, starting at 0. That's x. Number the rows from the top, starting at 0. That's y. Now every square has an address (x, y):
x: 0 1 2 3 4 5 6 7 8 9
y = 0 # # # # # # # # # #
y = 1 # R r . . . . . . #
y = 2 # . . # # . . . . #
y = 3 # . . # . . b . . #
y = 4 # . . . . # # . . #
y = 5 # g . . . . . . . #
y = 6 # . # . . . . B . #
y = 7 # # # # # # # # # #
Read it like this: go across x, then down y. The robot is at (1, 1). The red block is at (2, 1). The bin is at (7, 6).
Wait, y goes down? In maths class y usually goes up. On screens and in text files, rows are counted from the top, so y grows downwards. It's just a choice, but you must stick to it. Getting it backwards is the most common bug in this lesson.
Predict first: what are the coordinates of the blue block b? And what's at (5, 4)?
The blue block is in column 6, row 3, so (6, 3). At (5, 4), go across 5 and down 4: it's a wall #, part of the little wall in the middle.
Step 2: a direction is a small change
What does "go east" do to the address? East means one column to the right: x goes up by 1, y stays the same. So "east" is the change (+1, 0). Do the same for all four:
| Direction | Change (dx, dy) | Why |
|---|---|---|
| N (north, up) | (0, −1) | up a row, and y grows downwards, so y gets smaller |
| S (south, down) | (0, +1) | down a row |
| E (east, right) | (+1, 0) | one column right |
| W (west, left) | (−1, 0) | one column left |
The homework file already has this table, as a Python dictionary called DIRECTIONS. ("dx" just means "change in x".)
Now moving is adding. New x = old x + dx. New y = old y + dy. By hand:
start (2, 3), go E: change (1, 0) → (2 + 1, 3 + 0) = (3, 3)
start (2, 3), go N: change (0, -1) → (2 + 0, 3 - 1) = (2, 2)
start (2, 3), go W: change (-1, 0) → (2 - 1, 3 + 0) = (1, 3)
Why is this better than writing four separate if statements? Because it's one rule for every direction. Later you could add diagonals, like "NE": (1, -1), without changing the moving code at all.
Step 3: don't drive into walls
The adding rule doesn't know about walls. From (1, 1), going N gives (1, 0), which is a #. A real robot would crash. So we add one rule: work out the new square, then check it. If it's free, go there. If not, stay put.
You don't need to write the check yourself. world.py gives you is_free(room, pos), which returns True for floor and False for walls or anywhere outside the room.
Step 4: follow a list of commands, and remember the path
A command string like "EES" means: east, east, south. Do them one at a time, and after each one, write down where you are. Start (1, 1):
start (1, 1)
E → (2, 1) free (2, 1)
E → (3, 1) free (3, 1)
S → (3, 2) WALL (3, 1) stays put
path = [(1, 1), (2, 1), (3, 1), (3, 1)]
Notice the path has 4 entries for 3 commands: the start plus one per command. And a bump into a wall still counts as an entry. The robot spent that turn standing still.
Predict first: the robot starts at (1, 1) and runs "EEEESSS". Where does it end up? Trace it on paper, then open.
E four times: (2, 1), (3, 1), (4, 1), (5, 1). S: (5, 2) free. S: (5, 3) free. S: (5, 4) is a wall, so it stays at (5, 3). That's exactly what the homework program prints at the end.
Step 5: how far away is something?
The robot is at (5, 3). The red block is at (2, 1). How far is that? You can't fly diagonally, you walk along the grid. So count the steps across and the steps down separately:
across: from x = 5 to x = 2 → 5 - 2 = 3 steps
down: from y = 3 to y = 1 → 3 - 1 = 2 steps
total: 3 + 2 = 5
This is called the Manhattan distance, after the city where the streets form a grid and taxis can't cut through buildings. In general: |x1 − x2| + |y1 − y2|. The bars | | mean "absolute value": drop the minus sign. Why? Because if you subtract the other way round, 2 − 5 = −3, and a distance can't be negative. Python's abs(-3) gives 3.
One important thing: Manhattan distance ignores walls. It's the shortest possible distance in a perfect world. The real route can only be the same or longer. In Lesson 6 that "never too big" property is exactly what makes A* search work.
3. Play with it
Interactive: drive the robot
The real room from world.py, with the same moving rules as your Python. Click a square next to the robot, use the buttons, or type a command string and press Run. Bumps into walls leave the robot where it is.
Try: drive to the bin at (7, 6) in as few moves as you can. Manhattan says at least 11. Can you do it in 11? Then type NNNWWW and count the bumps.
4. Homework
Open homework/lesson01_moving.py. It imports the room from world.py and already has the DIRECTIONS dictionary. Write the four functions in order. Each one uses the one before.
Function 1: move(pos, direction)
What it does: returns the new (x, y) after one step. Pure maths, no wall check.
Example: move((2, 3), "E") → (3, 3), and move((2, 3), "N") → (2, 2).
Hint 1
Look up the change for this direction in DIRECTIONS, then add it to the position.
Hint 2
dx, dy = DIRECTIONS[direction] unpacks the tuple into two variables in one go. The position's parts are pos[0] (x) and pos[1] (y).
Hint 3
Return a new tuple: ( x plus dx , y plus dy ). Don't forget the brackets: you're returning one tuple, not two numbers.
Function 2: safe_move(room, pos, direction)
What it does: like move, but if the new square isn't free, return the old position.
Example: safe_move(ROOM, (1, 1), "N") → (1, 1) (wall above). safe_move(ROOM, (1, 1), "E") → (2, 1).
Hint 1
Don't redo the maths. Call your own move first.
Hint 2
Then ask is_free(room, new_pos). It's already imported at the top of the file.
Hint 3
new_pos = (the result of move)
if the new square is free: return new_pos
otherwise: return posFunction 3: follow(room, start, commands)
What it does: runs every letter of the command string through safe_move, and returns the list of all positions, including the start.
Example: follow(ROOM, (1, 1), "EE") → [(1, 1), (2, 1), (3, 1)]. An empty string gives [(1, 1)].
Hint 1
A for loop over a string gives you one letter at a time: for c in "EES": runs with c = "E", then "E", then "S".
Hint 2
Keep two things: the list so far (it starts with the start in it) and the current position. Each loop, update the position, then add it to the list with .append.
Hint 3
path = a list containing just start
pos = start
for each letter in commands:
pos = safe_move(room, pos, letter)
append pos to path
return pathFunction 4: manhattan(a, b)
What it does: the grid distance |x1 − x2| + |y1 − y2|.
Example: manhattan((1, 1), (4, 5)) → 3 + 4 = 7, and the other way round, manhattan((4, 5), (1, 1)), is also 7.
Hint 1
Work out the difference in x and the difference in y separately, then add them.
Hint 2
Each difference could come out negative. Python's abs() removes the minus sign.
Hint 3
One line: return abs(difference of the x parts) + abs(difference of the y parts). Use a[0], b[0], a[1], b[1].
Run it and see
cd homework
python3 lesson01_moving.py # Windows: py lesson01_moving.py
cd ..
The program follows "EEEESSS" from the start and draws the path with *. Real output:
##########
#*r***...#
#..##*...#
#..#.Rb..#
#....##..#
#g.......#
#.#....B.#
##########
Ended at (5, 3) | distance to red block: 5
The red block's r covers the * at (2, 1): the robot rolled past it, since blocks aren't walls.
Check it
python3 check.py 1 # Windows: py check.py 1
All done looks like this (real output):
Lesson 1: lesson01_moving.py
✓ move
✓ safe_move
✓ follow
✓ manhattan
4/4 passed 🎉 lesson complete!
A ✗ comes with a hint. If you swap N and S, you'll see:
✗ move: move((2, 3), "N") should be (2, 2): remember y grows DOWN
And if you forget the start in follow:
✗ follow: follow(ROOM, (1, 1), "EE") should list 3 positions
Notice how one bug can cause several ✗: a wrong move also breaks safe_move and follow, because they use it. Always fix the first ✗ first.
Stretch
Write commands_to(start, goal) that returns a command string that walks straight to the goal, ignoring walls: first all the E or W steps, then all the N or S steps. Check that len(commands_to(a, b)) == manhattan(a, b) for a few pairs. Then run it through follow from (1, 1) to the bin at (7, 6) and count how many bumps it gets. (Not checked by check.py.)
5. Real-world challenge: map your room
- Pick a room at home, like your bedroom or the kitchen. Choose a step size: floor tiles if you have them, or one foot-length (heel to toe), or one big step.
- Draw it on squared paper, one paper square per step. Mark walls and furniture you can't walk through with
#. Put (0, 0) in the top-left corner of your drawing, x to the right, y downwards, exactly like the robot's room. - Label three things with their coordinates, for example the door, the bed, a lamp.
- Write a command string (only N, S, E, W) to get from the door to one of the things. Write down the Manhattan distance too.
- Be the robot. Stand at the door, then follow your string one step per letter without looking where you're going: only at the paper. Or have a friend follow it. If a step would hit furniture, stay put (and count a bump).
- Record: where you meant to end up, where you actually ended up, number of bumps, number of commands, and the Manhattan distance.
What it teaches: you'll find that the map and the real room don't quite agree (your steps aren't all the same size, the bed sticks out further than you drew). That gap between the map and reality is a real robotics problem: robots also drift, and that's why Lesson 2 adds sensors. You'll also see that your number of commands is never smaller than the Manhattan distance, and is bigger whenever furniture is in the way.
6. Your Robo-Helper so far
Lesson 0 ✓ the robot greets you and counts steps to a wall
Lesson 1 ✓ move, safe_move, follow, manhattan: the robot can move around the room
These four functions are the robot's legs, and later lessons import them directly:
- Lesson 2:
distance_to_wallcalls yourmoveto look further and further ahead. - Lesson 6:
astar_pathusesmoveto find neighbours andmanhattanas its guess of the distance left. - Lesson 7: the Q-learning robot practises in a maze using your
safe_move, bumps and all.
7. Common mistakes
- y going the wrong way. North is y − 1, because y grows downwards. The checker says
move((2, 3), "N") should be (2, 2): remember y grows DOWN. - Mixing up x and y when reading the grid.
ROOM[y][x]: the row (y) comes first, then the column (x).is_freealready handles this for you, so use it. - Forgetting the start in the path. The path for
"EE"has 3 positions, not 2. - Forgetting
abs(). Thenmanhattan((4, 5), (1, 1))gives −7, and the checker saysdistance shouldn't depend on order: use abs(). - Returning two numbers instead of a tuple.
return x, yactually does make a tuple in Python, butreturn [x, y]makes a list, and[3, 3] == (3, 3)isFalse. Use round brackets.
8. Check yourself
What change (dx, dy) does "S" mean, and why is it +1 not −1?
(0, 1). South is down the screen, and y counts rows from the top, so going down makes y bigger.
Predict: what does follow(ROOM, (2, 1), "SS") return? Use the grid in section 2.
[(2, 1), (2, 2), (2, 3)]. Both squares below (2, 1) are floor, so there are no bumps.
Predict: what does follow(ROOM, (1, 1), "NW") return?
[(1, 1), (1, 1), (1, 1)]. Both moves hit walls, so the robot stays put twice, but each turn still adds an entry to the path.
What is the Manhattan distance from the robot's start (1, 1) to the bin (7, 6)? Can the real route ever be shorter?
6 + 5 = 11. No: Manhattan distance ignores walls, so it's the best case. Walls can only make the real route the same or longer. (In Lesson 6 you'll find the real shortest route is exactly 11.)
Why is it useful that safe_move calls move instead of doing its own maths?
The maths lives in one place. If you change or fix move (or add diagonal directions), safe_move and follow get the fix for free.
9. Go deeper (optional)
- Homogeneous transforms: how real robot arms keep track of coordinates in 3D, when every joint has its own (x, y, z).
- Human–AI help levels: a robot moving towards goals on a 2D plane, with a human steering.
- Free robotics courses: look for "mobile robots" and "kinematics".