我选择了一个编程游戏TIS-100。编程手册也可以在Steam上找到,但是我在问题中已经描述了相关的语法。它自己的装配变体。几乎是学习装配的游戏化。

机器由节点组成。每个节点都有可读写的管道(上左下右)。读取管道后,管道中的值就会消失。它还具有两个寄存器ACCBAK。只能使用BAKSAV操作码访问SWP。每个节点都有自己执行的代码。此代码限制为每节点15行,每行18个字符。

我已经解决了这一级别。我们的目标是计算序列:

- SEQUENCE COUNTER -
> SEQUENCES ARE ZERO-TERMINATED
> READ A SEQUENCE FROM IN
> WRITE THE SUM TO OUT.S
> WRITE THE LENGTH TO OUT.L


语法语法变短(基于我使用的命令):

MOV <src> <dest> //moves from source to destination. Blocks if source is a pipe and doesn't have a value available. Blocks if dest is a pipe and already has a value.
ADD value //adds to ACC
<label>: //defines a label (for jumps)
JMP <label> //jumps execution to label
JEZ <label> //jumps to label if ACC = 0. JumpifEqualsZero
//there's also JumpifNotZero (JNZ), JumpifGreaterZero (JGZ), JumpifLesserZero (JLZ).
SAV //MOV ACC BAK
SWP //switches values of ACC and BAK


游戏是这样描述的:



我的程序有点难以完整发布,因此将其限制在三个主要节点。 br />
这是负责计算序列长度的节点:

S: MOV LEFT ACC
JEZ E
SWP
ADD 1
SAV
JMP S
E: SWP
MOV ACC DOWN
MOV 0 ACC
SAV


这是负责对序列求和的节点:

S: MOV UP ACC
JEZ E
MOV ACC LEFT
SWP
ADD LEFT
SAV
JMP S
E: SWP
MOV ACC DOWN
MOV 0 ACC


这是我滥用作为临时存储的节点:

MOV RIGHT ACC
MOV ACC RIGHT


我不喜欢我的代码是

序列计数器如下所示:

START
read value to ACC
if ACC is 0, then GOTO END
swap ACC and BAK
add 1 to ACC
write ACC to BAK
GOTO START
END
swap ACC and BAK
write ACC as output
set ACC to 0
write ACC to BAK


这里有重复项,无论您在哪里在thenelse的(隐式)JEZ情况下,首先交换ACCBAK。另外,我正在滥用“交换集写入”来更改BAK,但这也许是最短的方法。仅用于临时存储。

它可以正常工作。



作为参考,以下是直方图,显示了各个级别的人员得分,并通过箭头突出显示了我的得分:



#1 楼

不幸的是,包含我的保存游戏的硬盘驱动器昨天崩溃了,因此我无法查找解决方案,但是我可以说您认为这很丑陋是由于旧计算机系统的局限性。我一直都在滥用节点作为临时存储,而我的解决方案看上去几乎一样(如果我没记错的话)。如直方图所示,您还不错(尤其是在周期上)。

我们可以根据指令数和周期来优化您的解决方案。我专注于优化现有算法,而不是创建新程序。我无法测试优化的解决方案,因为我没有在此包装盒上安装Steam,但是它们仍然可以正常工作:



您的临时存储节点可以简化为:

MOV RIGHT RIGHT


这是一条有效的指令,可为您节省一条指令。


我只保存我的ACC在它被覆盖之前。这将删除一些重复的代码(即计数器中至少有一个SAV)并简化了控制流程。我重写的算法比您少了一条指令:

L:
SAV          # Save counter
MOV LEFT ACC # Next item
JEZ EZ       # End of sequence
SWP          # Restore counter
ADD 1        # Increment
JMP L        # Loop
EZ:          # End of sequence
SWP          # Restore counter
MOV ACC DOWN # Output
SUB ACC      # Clear ACC 
您的求和节点中不需要BAK。您只需要临时存储节点中的值和新值。指令的某些重新排序使您可以直接将保存的总和添加到新值。我在最后一刻再次保存了ACC

L:
MOV ACC LEFT  # Save sum 
MOV UP ACC    # Next item
JEZ EZ        # End of sequence
ADD LEFT      # Add sum 
JMP L         # Loop
EZ:           # End of sequence
MOV LEFT DOWN # Output sum 
SUB ACC       # Clear ACC


摆脱BAK,为我们节省了3条说明!



结论:绝对滥用指令集(例如,我的第一个要点)和游戏的可能性(在管道中存储值),以在不同指标上取得良好的成绩。不要尝试对所有内容进行优化,每个级别可以节省3次!

评论


\ $ \ begingroup \ $
不知道MOV RIGHT RIGHT是有效的,我认为这将写入仍然包含传入值的管道。几分钟后,我自己发现了#2(我说了几句话,但更像是15)……至于#3,那是非常好的发现。新的解决方案在253/6/22中运行,我可能也可以通过夏季MOV ACC RIGHT来减少节点。
\ $ \ endgroup \ $
– Pimgd
2015年6月7日在18:38



\ $ \ begingroup \ $
空白行是否免费(如L:\ nMOV ...)?
\ $ \ endgroup \ $
– Pimgd
2015年6月7日在18:46

\ $ \ begingroup \ $
@Pimgd我很确定它们是,但是可以随意将标签后的行移动到带有标签的行中。
\ $ \ endgroup \ $
– TimWolla
2015年6月7日18:47

\ $ \ begingroup \ $
也可以将解决方案减少到仅4个节点(我的第一个解决方案),但这并不是最快的。我以某种方式倾向于使用尽可能少的节点,在SpaceChem中也是如此
\ $ \ endgroup \ $
– max-m
15年6月7日在19:07

\ $ \ begingroup \ $
@Pimgd空行和标签不会增加您的指令数量,但会占用宝贵的空间(即每个节点可以有15行文本)
\ $ \ endgroup \ $
–格兰特·彼得斯
15年7月3日在4:36

#2 楼

与上述关于针对不同方法进行优化的声明相一致,增加节点数以减少周期数是降低指标的有效方法。正是由于这个原因,单独列出了表格。实际上,我认为在1个周期内,您可以运行许多指令,只要它们分散并并行运行即可。因此,您可以尝试在周期,指令和节点数这三种方法中的任何一种中降低一个。