第 9 课 · Découverte

Roguelike

对象、Hash、Set、YAML、AI、回合:六块砖,一座地牢——外加抹掉一切的死亡。这是你的配方。

~11 min7 个关卡
▶ 视频🧩 练习📄 备忘单

42 秒 · 含中文字幕 · 8.5 Mo

你会学会的 7 件事

第 9 课: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.

第 1 块砖:对象——英雄

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

第 2 块砖:地牢——数组的数组

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

第 3 块砖:战利品——剑不拿两次

Écris `ramasse(objets)` qui renvoie un `Set` : les objets uniques du butin. `require "set"` en haut, et les doublons tombent tout seuls.

第 4 块砖:存档——一个文件,一个 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.

第 5 块砖:怪物——朝你走一步

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

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

分享这一课

← 全部课程

📄 备忘单

打印出来贴显示器上,就当是你自己的主意

📄 备忘单 — Roguelike(你的配方)

1980 年,Rogue 跑在绿色终端里:每局都是新地牢,死亡抹掉一切。整个流派装进六块砖——而你全都有。

历史,三行

年份 发生了什么 带来了什么
1980 绿色终端上的 Rogue 程序生成的地牢 + 永久死亡
80 年代 NetHack、ADOM… 同一配方,更深
今天 成千上万的 roguelike 你的配方 — 第 1 到 9 课

奠基的那点:关卡不是关卡,是生成器。 你不画地牢——你写下造它的规则。

配方:6 块砖 = 6 课

砖 课 那行代码
英雄(状态) 1 — 对象 initialize + 读取器
存档(文件) 2 — 格式 to_yaml
地牢(网格) 3 + 8 — 容器与表 Array.new + 格子 x,y
战利品(不重复) 3 — Set Set.new(objets)
怪物(AI) 5 — 四个幽灵 一步,一个轴
回合(规则) 7 + 8 — 移动与碰撞 case/when

第 1 块砖 — 英雄(对象)

class Heros
  def initialize(pv)
    @pv = pv
  end
  def pv
    @pv
  end
end
Heros.new(20).pv   # => 20

英雄的命是存起来的状态,绝不是全局变量。

第 2 块砖 — 地牢(数组的数组)

def donjon(lignes, colonnes)
  Array.new(lignes) { Array.new(colonnes, ".") }
end
donjon(2, 3)   # => [[".", ".", "."], [".", ".", "."]]

空格子是 "."——我的会变成 #,你的随你。

第 3 块砖 — 战利品(Set)+ 存档(YAML)

Set.new(["epee", "epee", "potion"])   # => #<Set: {"epee", "potion"}>
{ "pv" => 20, "x" => 3 }.to_yaml      # => "---\npv: 20\nx: 3\n"

拒绝重复的背包,装进文件的一局——格式与容器,别无其他。

第 4 块砖 — 怪物:AI 之前的 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]

没有轨迹计算,没有机器学习:每回合一步,一次一个轴。 (Pac-Man,也是 1980——第 5 课。)

第 5 块砖 — 回合:全部玩法在一个 case 里

def tour(carte, x, y, dx, dy)
  cible = "#{x + dx},#{y + dy}"
  case carte[cible]
  when "mur"     then [x, y]        # 面对墙:我留下
  when "monstre" then :combat       # 面对敌人:我打
  else [x + dx, y + dy]             # 空格:我前进
  end
end

每回合读一次 Hash——墙、怪物、宝物:同一个问题,同一条地址。

永久死亡(这个流派的悖论)

File.write("partie.yaml", sauvegarde(etat))   # 存档
File.delete("partie.yaml")                     # 死了 → 一切重来

难度来自被抹掉的东西,不是加进去的。

以后别再说

  • 「我需要引擎 / 地牢生成器」 → 一条填满数组的规则
  • 「怪物 AI」 → 距离上的一个 if,1980 年就这样
  • 「roguelike 太复杂」 → 六块砖,早写好了,就在这门课里。

SlopCourse Microduck — 从 slop 到干净的代码。