Human Resource Machine · 关卡攻略
关卡逐关攻略
HRM 把编程扒到最裸:你只有搬运、跳转、判断几条命令,却要拼出解法。这里每一关给你目标、可直接照抄的解法代码、和它为什么这样写。解法均经权威解法库核准。全 41 关(第 5、15、18、27、33 关为剧情过场,无代码)。后期关卡(乘除、排序、质因数分解…)已进入正经算法,难度陡增,可当进阶挑战。
第 1 关收发室Mail Room顺序 · 输入输出
把 INBOX 里的三样东西,一件一件搬到 OUTBOX。
INBOX OUTBOX INBOX OUTBOX INBOX OUTBOX
inbox 从进货口拿一件、outbox 往出货口送一件,重复三次。这是最基础的“取输入、给输出”,先熟悉两条命令。
第 2 关忙碌收发室Busy Mail Room循环 ★
不管 INBOX 里有几件,全部原样搬到 OUTBOX。
a:
INBOX
OUTBOX
JUMP ajump a 让程序不断跳回标签 a,形成循环——只写一次、重复执行;INBOX 空了游戏自动结束。核心一句:别重复自己,让机器重复。
第 3 关拷贝地板Copy Floor内存 · 按编号取
地板上摆着一排字母方块。按题目要求,把其中三个(正好在 4、0、3 号格)按这个顺序送出去。
COPYFROM 4 OUTBOX COPYFROM 0 OUTBOX COPYFROM 3 OUTBOX
copyfrom N = 从 N 号地板格把东西拿到手上(不清空原格)。地板格就是能“按编号存取”的抽屉——这是内存/变量最朴素的样子。
第 4 关乱序处理器Scrambler Handler暂存 · 交换顺序
INBOX 里的东西成对出现。把每一对的前后两个“调个个儿”,再送出去。
a:
INBOX
COPYTO 0
INBOX
OUTBOX
COPYFROM 0
OUTBOX
JUMP a先拿第一个、copyto 0 存进 0 号格暂存;再拿第二个直接送出;最后把 0 号格里存的第一个送出——两个的顺序就换过来了。这就是“用一个临时变量交换”。
第 5 关咖啡时间Coffee Time过场
这一关是剧情过场——喝杯咖啡、歇口气,没有编程任务,直接进入下一关。
第 6 关多雨的夏天Rainy Summer加法 ADD
INBOX 成对出现。把每一对的两个数相加,送出它们的和。
a:
INBOX
COPYTO 0
INBOX
ADD 0
OUTBOX
JUMP a第一个数存进 0 号格;拿第二个时 add 0 = 手上的数 + 0号格的数,得到和再送出。第一次用到 ADD——让程序会算数。
第 7 关零的歼灭者Zero Exterminator条件跳转 · 判零
把 INBOX 里的东西送出去,但把所有的 0 都扔掉、不要送。
a:
INBOX
JUMPZ a
OUTBOX
JUMP aJUMPZ = 如果手上的值是 0 就跳转。这里“是 0 就跳回开头重新拿”(等于丢弃这个 0),不是 0 才送出。第一次“看情况做决定”的条件分支。
第 8 关三倍房间Tripler Room乘法=重复加法
把 INBOX 里的每个数乘以 3,送出结果。
a:
INBOX
COPYTO 0
ADD 0
ADD 0
OUTBOX
JUMP a机器没有乘法,就用加法凑:存一份 x 到 0 号格,手上是 x,add 0 → 2x,再 add 0 → 3x。记住:乘法就是重复的加法。
第 9 关保零计划Zero Preservation Initiative条件 · 反向过滤
和第 7 关正好相反——只把 0 送出去,其它数字全部扔掉。
JUMP c
a:
OUTBOX
b:
c:
INBOX
JUMPZ a
JUMP b拿一个数:如果是 0(JUMPZ 成立)就跳去 outbox 送出;不是 0 就跳回去重拿、丢掉。同一个 JUMPZ,把“成立时去哪”换一下,就从“扔掉0”变成“只留0”。
第 10 关八倍套间Octoplier Suite倍增技巧 ×8
把 INBOX 里的每个数乘以 8,送出结果。
a:
INBOX
COPYTO 0
ADD 0
COPYTO 0
ADD 0
COPYTO 0
ADD 0
OUTBOX
JUMP a不用傻加 8 次。每次把当前结果存回 0 号格再自加:x → 2x → 4x → 8x,三次翻倍就到 8 倍。这叫“倍增”,比一个个加快得多——效率意识从这里开始。
第 11 关暗巷减法Sub Hallway减法 SUB
INBOX 成对出现。每对输出两个方向的差:先 b−a,再 a−b。
a:
INBOX
COPYTO 0
INBOX
COPYTO 1
SUB 0
OUTBOX
COPYFROM 0
SUB 1
OUTBOX
JUMP aSUB 是减法命令。把 a、b 分别暂存到 0、1 号格,正着减一次、反着减一次,各输出一个。第一次用到减法。
第 12 关四十倍器Tetracontiplier乘法 · 倍增
把每个数乘以 40。
a:
INBOX
COPYTO 0
ADD 0
COPYTO 0
ADD 0
COPYTO 0
ADD 0
COPYTO 0
ADD 0
ADD 0
ADD 0
ADD 0
OUTBOX
JUMP a先连续翻倍得到 8 倍(×2×2×2),再把这 8 倍自加到 40 倍(8×5)。大乘数的诀窍:先翻倍逼近、再累加,比一个个加省得多。
第 13 关相等房间Equalization Room相等判断
INBOX 成对。两数相等就输出其中一个,不等就都扔掉。
JUMP b
a:
COPYFROM 0
OUTBOX
b:
c:
INBOX
COPYTO 0
INBOX
SUB 0
JUMPZ a
JUMP c两数相减,若差为 0(JUMPZ 成立)说明相等、就输出;否则跳过。“相减是不是 0”正是编程里判断两值相等的底层做法。
第 14 关取大房间Maximization Room比较 · 取最大
INBOX 成对。输出较大的那个。
a:
INBOX
COPYTO 0
INBOX
SUB 0
JUMPN b
ADD 0
JUMP c
b:
COPYFROM 0
c:
OUTBOX
JUMP a算 a−b:若为负(JUMPN 成立)说明 a 小、输出 b;否则输出 a。“相减看正负”是比大小的通用手段。
第 15 关过场过场
这一关是剧情过场,没有编程任务。
第 16 关绝对正能量Absolute Positivity条件 · 绝对值
把每个数变成非负:负数取相反数,正数原样输出。
JUMP c
a:
COPYTO 0
SUB 0
SUB 0
b:
OUTBOX
c:
INBOX
JUMPN a
JUMP b若是负数(JUMPN),用“0 减两次”把它翻正(x−x−x = −x);正数直接输出。绝对值 = 去掉符号、只留大小。
第 17 关专属休息厅Exclusive Lounge条件 · 异或
INBOX 成对。两数“同号还是异号”,分别输出地板上预存的两个标记之一。
a:
INBOX
JUMPN c
INBOX
JUMPN d
b:
COPYFROM 4
JUMP e
c:
INBOX
JUMPN b
d:
COPYFROM 5
e:
OUTBOX
JUMP a分别判断两个数的正负,符号相同和不同走不同分支。这就是“异或(XOR)”的思想:两者不一样时为真。
第 18 关过场过场
这一关是剧情过场,没有编程任务。
第 19 关倒计时Countdown循环 · 计数
对每个数,从它一路数到 0,逐个输出(正数递减、负数递增)。
a:
INBOX
COPYTO 0
b:
c:
OUTBOX
COPYFROM 0
JUMPZ a
JUMPN d
BUMPDN 0
JUMP b
d:
BUMPUP 0
JUMP c每次输出当前值,再朝 0 挪一步:正数用 BUMPDN 减 1、负数用 BUMPUP 加 1,到 0 就停。BUMPUP/BUMPDN 是“某格 ±1”的快捷命令。
第 20 关乘法车间Multiplication Workshop乘法 = 重复加法
INBOX 成对。输出两数之积 a×b。
a:
COPYFROM 9
COPYTO 4
INBOX
COPYTO 1
INBOX
COPYTO 0
b:
JUMPZ c
COPYFROM 4
ADD 1
COPYTO 4
BUMPDN 0
JUMP b
c:
COPYFROM 4
OUTBOX
JUMP a机器没有乘法,就把 a 累加 b 次:用一个计数器存 b,每加一次就减 1,减到 0 停。乘法的本质就是“把一个数加另一个数那么多次”。
第 21 关零止求和Zero Terminated Sum循环 · 哨兵值
INBOX 是若干段,每段以 0 结尾。输出每一段的和。
a:
INBOX
JUMPZ d
b:
COPYTO 0
INBOX
JUMPZ c
ADD 0
JUMP b
c:
COPYFROM 0
d:
OUTBOX
JUMP a一路累加,直到碰到 0(哨兵/终止符)就输出这段的和、重新开始。用一个特殊值 0 标记“段结束”,真实数据里很常见(如字符串的结尾)。
第 22 关斐波那契访客Fibonacci Visitor变量 · 数列递推
对每个输入 n,输出不超过 n 的斐波那契数列(1,1,2,3,5,8…)。
a:
INBOX
COPYTO 0
COPYFROM 9
COPYTO 1
COPYTO 2
BUMPUP 2
b:
OUTBOX
COPYFROM 1
ADD 2
COPYTO 3
COPYFROM 0
SUB 3
JUMPN a
COPYFROM 2
COPYTO 1
COPYFROM 3
COPYTO 2
JUMP b用两个格子记住“前两项”,每次输出当前项、再把两项相加得下一项,直到超过 n。斐波那契就是“下一项 = 前两项之和”的经典递推。
第 23 关最小的数The Littlest Number比较 · 求最小
每段(0 结尾)里,输出最小的那个数。
JUMP b
a:
COPYFROM 0
OUTBOX
b:
INBOX
JUMP d
c:
ADD 0
d:
COPYTO 0
e:
INBOX
JUMPZ a
SUB 0
JUMPN c
JUMP e记住“目前最小”,每来一个就比一比、更小就替换;到段尾输出冠军。这是“擂台式”求最值:保留当前最好,逐个挑战。
第 24 关取余模块Mod Module取余 = 反复减
INBOX 成对。输出 a mod b(a 除以 b 的余数)。
a:
INBOX
COPYTO 0
INBOX
COPYTO 1
COPYFROM 0
b:
SUB 1
JUMPN c
JUMP b
c:
ADD 1
OUTBOX
JUMP a用 a 反复减 b,直到再减就变负——最后剩下的就是余数。取余的本质是“减到不够减为止,剩多少”。
第 25 关累计倒数Cumulative Countdown循环 · 累加
对每个 n,输出 n+(n−1)+…+1 的总和。
a:
INBOX
JUMPZ d
COPYTO 1
b:
COPYTO 0
BUMPDN 1
JUMPZ c
ADD 0
JUMP b
c:
COPYFROM 0
d:
OUTBOX
JUMP a从 n 开始,把 n、n−1、n−2… 一直加到 1。等差数列求和最朴素的写法:一个个加起来。
第 26 关短除法Small Divide除法 = 数减几次
INBOX 成对。输出 a÷b 的商(整数,向下取整)。
a:
COPYFROM 9
COPYTO 7
INBOX
COPYTO 0
INBOX
COPYTO 1
b:
COPYFROM 0
SUB 1
JUMPN c
COPYTO 0
BUMPUP 7
JUMP b
c:
COPYFROM 7
OUTBOX
JUMP a数 a 里能减掉几个 b,商就是几。用计数器记录减的次数,减到不够为止。除法就是“反复减、数次数”,和取余(第 24 关)是一对孪生。
第 27 关过场过场
这一关是剧情过场,没有编程任务。
第 28 关三数排序Three Sort排序 · 比较交换
每三个数一组,从小到大排好序输出。
a:
INBOX
COPYTO 0
INBOX
COPYTO 1
INBOX
COPYTO 2
SUB 1
JUMPN c
b:
COPYFROM 2
COPYTO 3
COPYFROM 1
COPYTO 2
COPYFROM 3
COPYTO 1
c:
COPYFROM 1
SUB 0
JUMPN d
COPYFROM 1
COPYTO 3
COPYFROM 0
COPYTO 1
COPYFROM 3
COPYTO 0
d:
COPYFROM 1
SUB 2
JUMPN b
COPYFROM 2
OUTBOX
COPYFROM 1
OUTBOX
COPYFROM 0
OUTBOX
JUMP a三个数两两比较,谁小谁靠前,需要时交换位置,比完输出。这是“排序”的最小案例;再多的数也是同样“比较+交换”反复做(见第 41 关)。
第 29 关存储楼层Storage Floor指针 · 间接寻址
INBOX 给一个地址编号,输出地板上那个编号格子里的东西。
a:
INBOX
COPYTO 10
COPYFROM [10]
OUTBOX
JUMP a`COPYFROM [10]` 是关键——方括号表示“取 10 号格里存的数当地址、再去那个地址取值”。这就是指针/间接寻址:格子里存的不是数据,而是“数据在哪”。
第 30 关字符串存储楼层String Storage Floor指针 · 字符串
INBOX 给一个起始地址,从那里开始输出一串字母,直到遇到 0(结尾)。
a:
INBOX
COPYTO 24
b:
COPYFROM [24]
JUMPZ a
OUTBOX
BUMPUP 24
JUMP b用间接寻址从起点逐格读取,读到 0 就停。字符串在内存里就是“一串连续字符 + 一个结尾标记 0”——这里第一次把指针和字符串合起来用。
第 31 关字符串反转String Reverse栈 · 反转
读入一个以 0 结尾的字符串,倒着输出它。
a:
b:
BUMPUP 14
INBOX
JUMPZ c
COPYTO [14]
JUMP b
c:
d:
BUMPDN 14
JUMPZ a
COPYFROM [14]
OUTBOX
JUMP d先把字符一个个存进连续的格子(指针向上走),到结尾再从最后一格往回读(指针向下走)。“后进先出”正是“栈”的思想,反转是它最经典的用途。
第 32 关库存报告Inventory Report遍历 · 计数
对每个输入值,数一数它在地板上那串数里出现了几次,输出次数。
a:
INBOX
COPYTO 16
COPYFROM 14
COPYTO 15
COPYTO 17
b:
COPYFROM [15]
JUMPZ e
SUB 16
JUMPZ d
c:
BUMPUP 15
JUMP b
d:
BUMPUP 17
JUMP c
e:
COPYFROM 17
OUTBOX
JUMP a从头到尾扫一遍地板列表,每个都和目标比一比,相等就计数 +1,扫到 0 结束、输出计数。“遍历一遍、边走边数”是最基础的统计操作。
第 33 关过场过场
这一关是剧情过场,没有编程任务。
第 34 关元音焚化炉Vowel Incinerator字符串 · 过滤
读入一个字符串,输出它、但烧掉所有元音(A E I O U)。
JUMP c
a:
COPYFROM 7
OUTBOX
b:
c:
INBOX
COPYTO 7
COPYFROM 5
COPYTO 6
d:
COPYFROM [6]
JUMPZ a
SUB 7
JUMPZ b
BUMPUP 6
JUMP d每读一个字母,挨个和地板上预存的 5 个元音比一比:撞上任何一个就跳过不输出,否则原样送出。“逐个对照黑名单过滤”是文本处理的常见套路。
第 35 关去重Duplicate Removal去重 · 查表
读入一个字符串,每个字母只留第一次出现,重复的删掉。
COPYFROM 14
JUMP b
a:
COPYFROM 11
COPYTO [14]
OUTBOX
BUMPUP 14
SUB 14
b:
COPYTO [14]
c:
COPYTO 14
INBOX
COPYTO 11
d:
COPYFROM [14]
JUMPZ a
SUB 11
JUMPZ c
BUMPUP 14
JUMP d每输出一个字母,就把它记进一张“见过的表”;后面再遇到就查表、见过就跳过。“用一张表记录见过什么”是去重的通用办法(哈希集合的雏形)。
第 36 关字典排序器Alphabetizer字符串比较 · 字典序
给两个字符串,输出按字典顺序排在前面的那个。
a:
b:
COPYFROM 23
COPYTO 22
c:
INBOX
COPYTO [22]
JUMPZ d
BUMPUP 22
JUMP c
d:
COPYFROM 23
COPYTO 22
e:
COPYFROM [22]
JUMPZ j
INBOX
COPYTO 21
JUMPZ a
SUB [22]
JUMPZ f
JUMPN l
JUMP g
f:
COPYFROM 21
OUTBOX
BUMPUP 22
JUMP e
g:
h:
COPYFROM [22]
JUMPZ i
OUTBOX
BUMPUP 22
JUMP h
i:
j:
k:
INBOX
JUMP k
l:
COPYFROM 21
OUTBOX
m:
INBOX
JUMPZ b
OUTBOX
JUMP m从头逐字符比较两个字符串:第一个不同的位置谁的字母小谁排前;若一个是另一个的前缀,短的排前。“逐字符比大小”就是字典序,也是所有文字排序的基础。
第 37 关寻宝链Scavenger Chain链表 · 指针跳转
地板上是一条寻宝链:每站存着[一个字母, 下一站地址]。INBOX 给起点,顺着链一路输出字母,直到走到头。
a:
INBOX
b:
COPYTO 22
COPYFROM [22]
OUTBOX
BUMPUP 22
COPYFROM [22]
JUMPN a
JUMP b读出当前站的字母输出,再读出“下一站地址”跳过去,直到地址为负(链尾)。这就是“链表”:数据不必连续摆放,每个节点自己记着“下一个在哪”。
第 38 关数字爆破Digit Exploder除法 · 拆数位
把每个三位数拆成单个数字输出(如 123 → 1、2、3)。
a:
COPYFROM 9
COPYTO 0
COPYTO 1
INBOX
COPYTO 2
b:
COPYFROM 2
SUB 11
JUMPN c
COPYTO 2
BUMPUP 0
JUMP b
c:
d:
COPYFROM 2
SUB 10
JUMPN e
COPYTO 2
BUMPUP 1
JUMP d
e:
COPYFROM 0
JUMPZ f
OUTBOX
JUMP g
f:
COPYFROM 1
JUMPZ h
g:
COPYFROM 1
OUTBOX
h:
COPYFROM 2
OUTBOX
JUMP a除以 100 得百位、余数再除以 10 得十位、剩下的是个位。“除法取商和余数”就能把一个数按位拆开——这是进制的核心操作。
第 39 关重定坐标Re-Coordinator除法 · 商与余数
对每个输入,求它除以每行宽度(这里是 15)的商和余数,分别输出——相当于把线性编号换算成网格的行、列坐标。
JUMP b
a:
ADD 15
OUTBOX
COPYFROM 1
OUTBOX
b:
COPYFROM 14
COPYTO 1
INBOX
c:
SUB 15
JUMPN a
COPYTO 0
BUMPUP 1
COPYFROM 0
JUMP c反复减 15、数减了几次(商=行),减到不够、剩下的(余数=列)。一次除法同时给出商和余数,正是“一维编号 ↔ 二维坐标”互换的关键。
第 40 关质数工厂Prime Factory质因数分解
把每个数分解成质因数,从小到大逐个输出(如 12 → 2、2、3)。
COPYFROM 24
a:
COPYTO 5
COPYTO 2
BUMPUP 5
INBOX
COPYTO 0
b:
BUMPUP 5
c:
COPYFROM 24
COPYTO 1
COPYTO 2
BUMPDN 0
JUMPZ a
BUMPUP 0
d:
SUB 5
JUMPN b
COPYTO 2
BUMPUP 1
COPYFROM 2
JUMPZ e
JUMP d
e:
COPYFROM 5
OUTBOX
COPYFROM 1
COPYTO 0
JUMP c从最小的质数 2 开始试除:能整除就输出这个因子、并把数除掉;除不尽就换更大的除数,直到剩 1。这是“质因数分解”,也是很多数学/密码学问题的地基——难度已相当高。
第 41 关排序楼层Sorting Floor排序算法
读入一串以 0 结尾的数,全部存到地板上,从小到大排好序后输出。
a:
b:
BUMPUP 24
INBOX
JUMPZ d
COPYTO [24]
JUMP a
c:
COPYFROM [19]
OUTBOX
COPYFROM [24]
COPYTO [19]
d:
BUMPDN 24
JUMPZ b
COPYTO 23
e:
COPYFROM 23
COPYTO 19
f:
BUMPDN 23
JUMPZ c
COPYFROM [23]
SUB [19]
JUMPN e
JUMP f把所有数先存下来,再反复扫描、每轮挑出剩下里最小的一个输出(选择排序)。这是完整的“排序算法”——第 28 关三个数的放大版,也是计算机最核心的基本功之一。
怎么用 卡住了再来看。先自己试,真想不出再照解法抄一遍,重点是读懂「讲解」里它为什么这样写——比抄答案更重要的是弄懂那一步。解法参考 cowboy/human-resource-machine-solutions。