Human Resource Machine

Human Resource Machine

62 ratings
(缓慢更新至Year 17)全关卡优化及思路浅析(小白向)
By Icarus
这个游戏真的很有意思(尤其是优化),这篇指南是写给我自己的技术文档,以便今后回顾(如果会回顾的话)。非专业,并不一定是最优解,只是阐述一下我的思路。
3
2
   
Award
Favorite
Favorited
Unfavorite
1. 收发室
关于inbox(输入)和outbox(输出)的教程关卡。

很形象的表示方法(我喜欢这个游戏的其中一个原因)。小人的手中其实就是类似栈顶的区域,他只能拿一样东西,此时这项物品(数据)是可变的、危险的、容易丢失的,高级程序语言中通常会使用变量来存储这个值,但我们现在是没有的,取而代之将采用类似于压栈和弹栈的操作来对其进行存储,简单地说,当需要进行运算时一定要考虑是否需要备份。

另外,“你不能两手空空去outbox”。

2. 繁忙的收发室
jump的教程,以及第一次优化(这个似乎是到后面几关开始优化之后才会出来的优化项目,记不清了,这里我就先当第一次了)。

jump是个邪恶的指令,所有的痛苦都是这个该死的指令造成的。然后你就会发现高级语言有多么亲切,因为它们居然有if、for和while!!!我们在后面再来谈跳转,目前可以简单理解为jump就是没有条件判断的循环。

优化

这游戏的优化是两个不同的目标,代码行数最少和计算步数最少,这两个条件无需同时完成,只要你的程序曾经完成过其中一项指标都会被记录下来,所以请尽情地浪。

  • 行数最少通常是指一种普适性的状态,代码简洁,并且不要执着于分辨各种不同的情况,这种优化最需要的是代码的可重复性最高,也就是说只留下最不能去掉的关键步,其他尽量删减合并,这是一种机器的典型思考模式,即重复工作;

  • 与之相反,计算步骤最少则需要分辨各种情况并采取最快路线,可以理解为模拟人脑(偷懒的)思维方式。

我个人十分赞赏第20关乘法研讨会,把优化的这两个特质展现得十分巧妙。我们后续再谈。

说回本关:3行之内完成很简单,无需解释。



可以以调试的思维一步一步观察程序是如何运行的,我们会发现jump的指令也是作为一种“计算”,因此每完成一个数的输出就会需要3步计算,但实际上第一关中输出一个数只需要2步即可。所以我们减少计算步骤的方法即为像第一关的方式进行inbox和outbox的堆叠。需要注意的是这里一共有12个数,但如果单纯堆叠12次输入输出的话,老板会告诉你你的程序“不足以应付所有情况”,此时在程序末端再添加一个jump到最顶端即可。


3. 复印楼层
拷贝的教程。

游戏中的地毯其实就是一个栈,当然它至少很亲切地没有保存先进后出的特质,不然这游戏就更复杂了。从这个方面来看也可以认为是一个数组。总而言之这块地毯是为了弥补这个游戏中没有变量赋予的缺陷,并且冷酷地审视着我们变量使用的根本逻辑。

本关无话可说,依次取出对应值输出即可完成所有优化条件。

S[9({YA)TK8JJJN({9.png]
4. 扰码处理器
和上一关一样是对变量思维的基础训练。永远记住你手上只能拿一个数据就行。这一关算是稍微地模仿了一下“先进后出”的压栈和弹栈的感觉,可能是一种情趣吧。

6. 多雨之夏
变量应用的又一个简单训练。

需要注意的是加法的这个步骤并不会更新你已经定义的变量(放在地毯上的数),它在你的手上(CPU)完成,和第一关一样这个和是一个容易丢失的数据。

7. 零扑灭行动
我们有了第一个条件判断语句。我们简称jump0

需要注意的是这个相当于没有else的if,被jump0包围部分执行完毕后是会接着执行jump0外面的部分的,如果不想要它这样执行请记得使用jump。

4)G5H%)4T.png]
8. 三倍扩大室
这是一个没有乘法的冰冷的世界……

x3即三个相同的数相加,依然注意先储存变量(不记得也没关系,慢慢学到教训就好)。同时要注意add是只add两次。



9. 零保护行动
jump的一种新鲜用法(对于高级程序语言应该比较新鲜吧,至少我写c++还从来没这么写过)。

此处用在这里是为了优化运算步骤。

常规思路可能是这样:我先输入一个数,判断它是否为0,是的话就输出,不是的话就跳转回输入语句之前进行下一步输出。多余的计算步骤来自于顺序设计时每一个输出之后必须要用jump跳转回到inbox之前,本关中有4个0,就有4次这样的跳转,这就是为什么你写出来是28步而被要求只用25步。(同时也说明了正确的算法还会有某一步只执行过一次)

要对此进行优化,我们可以注意到转到inbox这一步骤重复了,不管是0还是非0都需要跳转回输入,因此我们的优化方向就是去除outbox后jump到inbox的操作,具体实现方式如下,其中第一个jump是只执行一次的语句,可以认为是循环开始前的初始化。

SX$0WA57.png]
10. 八倍扩大装置
因为题目限制了只能使用3个add,所以普通的思路肯定不行。这个时候就要从8倍本身出发。8的特别之处在于它是2的三次方,考虑到我们的变量(地毯)可以对每一步运算的结果都进行存储,x8就可以被分解为三次翻倍(这一思路需要理解add的特性,add不会改变初始变量的值)。

这里多讲一下,我们在高级语言里进行x=x+y这个操作时实际上是两步,即相加和赋值。这个游戏的add指令相当于return (x+y),把x+y的值放在手上暂存,赋值的步骤则需要另外一行指令。

具体来说,每一个输入的数,将它存储在0号地毯处,手上的初值与0号地毯add(即x2操作,初学者可以注意一下这里的copy思想)后的值存储在1号地毯处,再把手上的值(此时手上的值是x2后的值,初学者可以从这里理解一下栈的操作)与1号地毯add,达成又一次翻倍(x4),重复到第三个地毯上,最后手上的值就是x8后的值了。

11. 加运算走廊
很简单,sub和add本质是一样的,这一关可以帮助初学者更好地理解什么是计算的中间量,什么是需要反复读取的值,什么是会被丢失的数据,什么是不应该被丢失的数据。形象的来说就是分辨拿在手上的值和放在地毯上的值(不会分辨可以反复调试观看小人的行动)。

当然这一关中的两个数都必须先存储下来才能进行操作。

另外copyto的这个操作即a=b,当采用这个指令后我们手上暂存的数也不会消失,因为它是“复制”而非剪切。(随时注意手上的值,可以省去一些不必要的读取操作,对优化很关键)

12. 四十倍放大器
思路和之前八倍放大是一样的,只不过40不再是2的整数次幂,所以可以拆成5x8来算,即先自己反复加5次,再用八倍放大器当时的程序做幂次方。依然分清楚哪些是需要保存的数就行。

K@98N%S(S[[13XTX5DQ5.png]

13. 均衡之间
两个数是否相等用减法和jump0来判断。

14. 最大值室
这里有一个比较讨巧的做法。判断两个值的大小关系(b-a是否为负)后如何回归原值输出:先将a储存,当a更大时可以直接读取a的值输出,而当b更大时则有b=b-a+a,再用一个add指令即可。

L8Q8`XO(1DJ.png]
16. 绝对正能量
和上一关思路类似。正数直接输出,负数则有-a=a-a-a。

17. 专属休息室
条件判断的训练题。

a为正时,若b为正则输出0,若b为负则输出1;a为负时,若b为负则输出0,b为正则输出1。

读取0或1并输出的这两步是共用的,虽然有四种情况但其实只有两种输出。

个人觉得这道题的结构写出来很漂亮。

J1H9BH3]6{P90IX5.png]
10 Comments
阿狸狸没在摸鱼鱼 Sep 5, 2023 @ 5:36am 
“我个人十分赞赏第20关乘法研讨会,把优化的这两个特质展现得十分巧妙。我们后续再谈。”
————————————————————————————————
大佬快回来谈谈20关——
Strager May 14, 2023 @ 2:35am 
大佬怎么不更新了
Fruity Trump Jul 31, 2022 @ 7:33am 
如何中文汉化
liyongyiisasb Dec 5, 2021 @ 7:56am 
17年想了一晚上,互相加减运算还是判断不了,终于看到解答了:steamhappy:
1839045406 Nov 20, 2020 @ 9:41pm 
不好意思看错了

1839045406 Nov 20, 2020 @ 9:34pm 
零扑灭行动那里不对啊,运行不起来。
summeriver13 Apr 8, 2020 @ 12:49am 
14加回去是真的妙
csc Dec 30, 2018 @ 12:48am 
感谢攻略,13、14学到了。
Pr0vasi Aug 27, 2018 @ 11:39pm 
y13把输出前置太妙了,谢谢!
NLTransition Jul 2, 2018 @ 8:51am 
感谢!