course n°9 · Découverte

The Roguelike

Object, Hash, Set, YAML, AI, turn: six bricks, one dungeon — and a death that erases everything. Here's your recipe.

~11 min7 stations
▶ video🧩 exercises📄 cheat sheet

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

Share the lesson

← all courses

📄 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" → an if on the distance, like in 1980
  • "roguelikes are complicated" → six bricks, already written, in this course.

SlopCourse Microduck — from slop to clean code.