The Position Map
A cell = an address, a name on top: your world fits in a Hash — and a wall is just an occupied cell.
42 seconds · English captions included · 8.7 Mo
The 7 things you'll know how to do
Lesson 8: The Position Map
Regarde la leçon (42 s) : ton monde n'a pas de décor, il a une table. Qui est où — et qui ne peut pas passer.
A cell = an address
Écris `case_de(x, y)` : la clé de la map pour la case (3, 4) est `"3,4"` — x, une virgule, y. Toute la case tient dans une chaîne.
Store a sprite
Écris `pose(carte, x, y, nom)` qui renvoie une NOUVELLE map avec le sprite posé sur la case — l'original ne bouge jamais. Tu ranges, tu ne détruits.
Is the cell taken?
Écris `occupe?(carte, x, y)` : true si un sprite est sur cette case. On interroge l'ADRESSE — `key?` — pas la valeur : le vide n'a pas de nom.
Who meets who?
Écris `rencontre(carte, x, y)` : le nom du sprite sur la case — ou `nil` si elle est vide. Dans un vrai jeu, c'est ce retour qui déclenche tout : dégât, pièce, dialogue.
60 updates per second
Écris `tic(secondes)` : le nombre de fois où la map est relue et recalculée — 60 fois par seconde, pas une de plus. Une seconde de jeu, soixante calculs.
Lesson 8: OK
Sans aide : `avance(carte, x, y, dx, dy)` — un pas vers `[x + dx, y + dy]`. Si cette case est déjà occupée (un mur, un autre sprite), le pas est IMPOSSIBLE : tu restes à `[x, y]`.
📄 The cheat sheet
print it, stick it on your monitor, pretend it was your idea📄 Cheat sheet — The Position Map (the world is a Hash)
Your game has no world: it has a table. A cell = an address, a name on top — and a wall is just an occupied cell.
The map in 4 words
| Word | What it is | Example |
|---|---|---|
| cell | an address x,y |
"3,4" |
| map | all the cells, a Hash | carte = {} |
| tick | one update | 60 per second |
| wall | an occupied cell | carte["5,5"] = "mur" |
A cell = an address
def case_de(x, y)
"#{x},#{y}" # x first, the comma, y after
end
case_de(3, 4) # => "3,4"
The whole cell fits in a string: that's your Hash key. 4,3 is another cell.
Storing a sprite: we copy, we never destroy
def pose(carte, x, y, nom)
carte.merge("#{x},#{y}" => nom) # new Hash, original intact
end
pose({}, 1, 2, "joueur") # => { "1,2" => "joueur" }
merge copies, = destroys. Since lesson 6 the rule hasn't changed — applied to the whole world.
Occupied or empty: key? vs value
carte = { "3,4" => "joueur" }
carte.key?("3,4") # => true the ADDRESS exists
carte.key?("0,0") # => false nobody lives there
carte["0,0"] # => nil the empty has no name
key? tells you the cell is taken whatever the value. That's the question the game asks
sixty times a second.
Who meets who
def rencontre(carte, x, y)
carte["#{x},#{y}"] # the name… or nil
end
rencontre(carte, 3, 4) # => "joueur"
rencontre(carte, 0, 0) # => nil
A nil coming back: nothing on the road. A name coming back: damage, coin, dialogue — all the
gameplay starts from that return.
60 updates per second
def tic(secondes)
secondes * 60 # one second of game = sixty re-reads of the table
end
tic(1) # => 60
Each tick: re-read the map, move, compare again. The game doesn't move: it recalculates.
Face a wall, the move is impossible
def avance(carte, x, y, dx, dy)
cible = "#{x + dx},#{y + dy}"
carte.key?(cible) ? [x, y] : [x + dx, y + dy]
end
avance({}, 2, 2, 1, 0) # => [3, 2] free cell: I advance
avance({ "3,2" => "mur" }, 2, 2, 1, 0) # => [2, 2] taken cell: I stay
No physics, no engine "collision": an address already taken, a key?, a choice.
Never say again
"the sprite hits a wall"→ the target cell is already in the map — you return the old position"I need a collision engine"→ your Hash has done that job since lesson 3.
SlopCourse — from slop to clean code.