course n°8 · Découverte

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.

~11 min7 stations
▶ video🧩 exercises📄 cheat sheet

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]`.

Share the lesson

← all courses

📄 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 Microduck — from slop to clean code.