The Roguelike
Object, Hash, Set, YAML, AI, turn: six bricks, one dungeon — and a death that erases everything. Here's your recipe.
42 seconds · English captions included · 8.5 Mo
The 7 things you'll know how to do
Lesson 9: The Roguelike
Regarde la leçon (42 s) : l'histoire d'un genre né d'un détail — puis la recette complète, brique par brique. Tu as déjà tout ce qu'il faut.
Brick 1: the object — the hero
Écris la classe `Heros` : `initialize(pv)` qui range les points de vie, et une lecture `pv`. Un seul chiffre décide de ta vie — le reste est du confort.
Brick 2: the dungeon — array of arrays
Écris `donjon(lignes, colonnes)` : un tableau de tableaux rempli de `"."` — la case vide. Une grille qui dépend des dimensions reçues, jamais une taille en dur.
Brick 3: the loot — never the same sword twice
Écris `ramasse(objets)` qui renvoie un `Set` : les objets uniques du butin. `require "set"` en haut, et les doublons tombent tout seuls.
Brick 4: the save — a file, a YAML
Écris `sauvegarde(etat)` : l'état du héros en YAML (`pv: 20`…). Le roguelike sauvegarde tout au même endroit — et la mort le relit pour l'effacer.
Brick 5: the monster — one step toward you
Écris `chasse(pos, cible)` : le monstre avance d'UNE case vers sa cible — l'axe X d'abord, l'axe Y ensuite (avec `<=>`). Pas de diagonale : un axe à la fois, comme les fantômes.
Lesson 9: OK
Sans aide : `tour(carte, x, y, dx, dy)` — le tour complet. Case libre → renvoie `[x + dx, y + dy]` · case `"mur"` → tu restes `[x, y]` · case `"monstre"` → `:combat`.
📄 The cheat sheet
print it, stick it on your monitor, pretend it was your idea📄 Cheat sheet — The Roguelike (your recipe)
In 1980, Rogue ran in a green terminal: a new dungeon every game, a death that erases everything. The whole genre fits in six bricks — and you have them all.
The history, in three lines
| Year | What happened | What it brought |
|---|---|---|
| 1980 | Rogue on a green terminal | procedural dungeon + permadeath |
| 80s | NetHack, ADOM… | same recipe, more depth |
| today | thousands of roguelikes | your recipe — lessons 1 to 9 |
The founding "detail": the party is not a level, it's a generator. You don't draw the dungeon — you write the rule that builds it.
The recipe: 6 bricks = 6 lessons
| Brick | Lesson | The piece of code |
|---|---|---|
| the hero (state) | 1 — objects | initialize + reader |
| the save (file) | 2 — formats | to_yaml |
| the dungeon (grid) | 3 + 8 — containers & map | Array.new + cells x,y |
| the loot (no duplicates) | 3 — Set | Set.new(objets) |
| the monster (AI) | 5 — the 4 ghosts | one step, one axis |
| the turn (rules) | 7 + 8 — movement & collisions | case/when |
Brick 1 — the hero (the object)
class Heros
def initialize(pv)
@pv = pv
end
def pv
@pv
end
end
Heros.new(20).pv # => 20
The hero's life is stored state, never a global variable.
Brick 2 — the dungeon (an array of arrays)
def donjon(lignes, colonnes)
Array.new(lignes) { Array.new(colonnes, ".") }
end
donjon(2, 3) # => [[".", ".", "."], [".", ".", "."]]
The empty cell is "." — mine becomes #, yours whatever you want.
Brick 3 — the loot (Set) + the save (YAML)
Set.new(["epee", "epee", "potion"]) # => #<Set: {"epee", "potion"}>
{ "pv" => 20, "x" => 3 }.to_yaml # => "---\npv: 20\nx: 3\n"
An inventory that refuses duplicates, a party that fits in a file — format and container, nothing more.
Brick 4 — the monster: the AI before AI
def chasse(pos, cible)
dx = cible[0] <=> pos[0]
dy = cible[1] <=> pos[1]
dx != 0 ? [pos[0] + dx, pos[1]] : [pos[0], pos[1] + dy]
end
chasse([5, 5], [5, 2]) # => [5, 4]
No trajectory math, no machine learning: one step per turn, one axis at a time. (Pac-Man, also 1980 — lesson 5.)
Brick 5 — the turn: all the gameplay in one case
def tour(carte, x, y, dx, dy)
cible = "#{x + dx},#{y + dy}"
case carte[cible]
when "mur" then [x, y] # facing a wall: I stay
when "monstre" then :combat # facing an enemy: I strike
else [x + dx, y + dy] # free cell: I advance
end
end
A Hash read once per turn — walls, monsters, treasures: same question, same address.
Permadeath (the genre's paradox)
File.write("partie.yaml", sauvegarde(etat)) # saves
File.delete("partie.yaml") # dies → everything restarts
The difficulty comes from what gets erased, not what gets added.
Never say again
"I need an engine / a dungeon generator"→ a rule that fills an array of arrays"a monster AI"→ anifon the distance, like in 1980"roguelikes are complicated"→ six bricks, already written, in this course.
SlopCourse — from slop to clean code.