写在前面

今天找时间把LC-3的Nim游戏(实验四、五)给做了,浅浅地分享一下吧。

一个目录

  • 写在前面
  • 一、游戏背景和规则
  • 二、实验要求
  • 三、样例输入/输出
  • 四、代码编写
    • 数据存放
    • 编写代码
  • 五、小小总结

一、游戏背景和规则

背景:Nim是一个简单的双人游戏,可能起源于中国。游戏中使用的计数器类型有很多种类,如石头、火柴、苹果等。游戏界面被划分为很多行,每行中有数量不等的计数器,如表所示:

行号 计数器数量
1 ○○○
2 ○○○○○○
··· ···
n ○○○○○○○○○

本次实验对Nim游戏做了一些小的改变,具体如下:
游戏界面由三行组成,计数器类型为石头,其中A行包含3个石头,B行包含5个石头,C行包含8个石头。
规则如下:
⑴ 每个玩家轮流从某一行中移除一个或多个石头。
⑵ 一个玩家不能在一个回合中从多个行中移除石头。
⑶ 当某个玩家从游戏界面上移除最后剩余的石头时,此时游戏结束,该玩家获胜。

二、实验要求

⑴ 在游戏开始时,你应该显示游戏界面的初始化状态。具体包括:在每行石头的前面,你应该先输出行的名称,例如“ROW A”。你应该使用ASCII字符小写字母“o”(ASCII码 x006F)来表示石头。游戏界面的初始化状态应该如下:

ROW A: ooo
ROW B: ooooo
ROW C: oooooooo

⑵ 游戏总是从玩家1先开始,之后玩家1和玩家2轮流进行。在每一个回合开始时,你应该输出轮到哪一个玩家开始,并提示玩家进行操作。例如,对于玩家1,应该有如下显示:

Player 1,choose a row and number of rocks:

⑶ 为了指定要移除哪一行中的多少石头,玩家应该输入一个字母后跟一个数字(输入结束后不需要按Enter键),其中字母(A,B或C)指定行,数字(从1到所选行中石头的数量)指定要移除的石头的数量。你的程序必须要确保玩家从有效的行中移除有效数量的石头,如果玩家输入无效,你应该输出错误提示信息并提示该玩家再次进行输入。例如,如果轮到玩家1:

Player 1, choose a row and number of rocks: D4
Invalid move. Try again.
Player 1, choose a row and number of rocks: A9
Invalid move. Try again.
Player 1, choose a row and number of rocks: A*
Invalid move. Try again.
Player 1, choose a row and number of rocks: &4
Invalid move. Try again.
Player 1, choose a row and number of rocks:

你的程序应保持提示玩家,直到玩家选择有效的输入为止。确保你的程序能够回显玩家的输入到屏幕上,当回显玩家的输入后,此时应该输出一个换行符(ASCII码x000A)使光标指向下一行。
⑷ 玩家选择有效的输入后,你应该检查获胜者。如果有一个玩家获胜,你应该显示相应的输出来表明该玩家获胜。如果没有胜利者,你的程序应该更新游戏界面中每行石头的数量,重新显示更新的游戏界面,并轮到下一个玩家继续。
⑸ 当某个玩家从游戏界面上移除最后的石头时,游戏结束。此时,你的程序应该显示获胜者然后停止。例如,如果玩家2移除了最后的石头,你的程序应该输出一下内容:

Player 2 Wins.

三、样例输入/输出

据说要和样例输出一模一样

ROW A: ooo
ROW B: ooooo
ROW C: oooooooo
Player 1, choose a row and number of rocks: B2ROW A: ooo
ROW B: ooo
ROW C: oooooooo
Player 2, choose a row and number of rocks: A1ROW A: oo
ROW B: ooo
ROW C: oooooooo
Player 1, choose a row and number of rocks: C6ROW A: oo
ROW B: ooo
ROW C: oo
Player 2, choose a row and number of rocks: G1
Invalid move. Try again.
Player 2, choose a row and number of rocks: B3ROW A: oo
ROW B:
ROW C: oo
Player 1, choose a row and number of rocks: A3
Invalid move. Try again.
Player 1, choose a row and number of rocks: C2ROW A: oo
ROW B:
ROW C:
Player 2, choose a row and number of rocks: A1ROW A: o
ROW B:
ROW C:
Player 1, choose a row and number of rocks: A*
Invalid move. Try again.
Player 1, choose a row and number of rocks: &4
Invalid move. Try again.
Player 1, choose a row and number of rocks: A1Player 1 Wins.
----- Halting the processor -----

四、代码编写

数据存放

1、程序从x3000开始存放
2、每行石头的数量从x3200开始存放,且以-1结尾作为结束标志,以支持非题目要求的D~Z行(属于是自找麻烦了)

编写代码

注意:
以下代码片段并非代码实际顺序。
并且子程序没有严格地保存和恢复寄存器状态,这是不对滴。

1、石头状态输出子程序:

;石头状态输出
;R1-石头数量地址,R2-数量
FUNC_DRAW       ST  R7,SAVER7       ;保存R7的值,因为PUTS和OUT会对其进行修改LD  R1,PTR_STONES  ;石头堆首地址LD  R3,CHAR_ROWBASE  ;'A'的ASCII码;遍历石头堆
LOOP_1          LDR R2,R1,#0        ;读取当前行石头数量BRn END_DRAW      ;若数字为-1,结束输出LEA R0,STR_ROWPUTSAND R0,R0,#0ADD R0,R3,#0OUTLEA R0,STR_ROW2PUTSLD  R0,CHAR_SYMBOL  ;ADD R2,R2,#0       ;将CC恢复为R2;循环输出o
LOOP_2          BRz OUT_2           ;若数字R2降到0,结束当前循环OUT ADD R2,R2,#-1BRnzp LOOP_2
OUT_2           LD  R0,CHAR_LFOUTADD R1,R1,#1ADD R3,R3,#1       BRnzp LOOP_1
END_DRAW        LD R7,SAVER7RET

2、检查石头是否被取完:

;检查石头是否被取完了
;Output(R6):没取完(0),取完了(1)
FUN_CHECK       LD  R1, PTR_STONES  ;R1指向石头AND R6, R6, #0ADD R6, R6, #1     ;初始化R6为1
SUB_CHECK_LOOP  LDR R2, R1, #0      ;读取当前行石头数BRp SUB_CHECK_FALSE    ;任意行非零则返回0BRn SUB_CHECK_END ;遇到-1,说明石头数全为0,返回1ADD R1, R1, #1BRnzp SUB_CHECK_LOOP    SUB_CHECK_FALSE ADD R6, R6, #-1     ;R6置为0
SUB_CHECK_END   RET

3、判断当前输入的合法性并取走石头:

;判断当前的输入是否还有剩余,若无剩余输出错误信息
;Input(PTR_ROW,PTR_CNT)  Output(R6):非法输入(0),取走前一切正常(n)
FUN_HASLEFT     ST  R7, SAVER7      ;保存R7,因为PUTSLD  R1, PTR_STONES  ;R1指向首堆石头LD  R2, PTR_ROWLD  R3, NCHAR_AADD R2, R2, R3       ;行距离A的偏移BRn SUB_HAS_ERROR   ;行号非法ADD R1, R1, R2     ;指向输入指定的行AND R6, R6, #0LDR R2, R1, #0BRnz SUB_HAS_ERROR ;剩下的石头<=0,可同时判断行号非法和当前行为0ADD R6, R6, R2     ;R6 = R2
SUB_HAS_SUB     LD  R2, PTR_CNT     ;读取输入的数字LD  R3, NCHAR_0ADD R2, R2, R3       ;ASCII -> numBRnz SUB_HAS_ERROR  ;数字非法NOT R2, R2ADD R2, R2, #1       ;R2 = -R2ADD R4, R6, R2        ;R4 = 该行剩余 - 取走数量BRn SUB_HAS_ERROR ;该行剩下的石头不够STR R4, R1, #0        ;更新石头数量BRnzp SUB_HAS_END
SUB_HAS_ERROR   AND R6, R6, #0      ;需要返回的0LD  R0, CHAR_LFOUTLEA R0, STR_INVALIDPUTSLD  R0, CHAR_LFOUT
SUB_HAS_END     LD  R7, SAVER7      ;载入R7RET

4、行号及数量输入:

;输入行号(ABCDEFGHIJKLMNOPQRSTUVWXYZ)
INPUT_ROW       ST  R7, SAVER7GETCOUTST  R0, PTR_ROWLD  R7, SAVER7RET;输入个数(123456789)
INPUT_CNT       ST  R7, SAVER7GETCOUTST  R0, PTR_CNTLD  R7, SAVER7RET;此处用PTR命名是因为原来是打算用LDI和STI的,后来改用LD和ST懒得改了哈哈哈
PTR_ROW         .BLKW 1         ;储存输入的行号(ASCII码A~Z)
PTR_CNT         .BLKW 1         ;储存输入的数字(ASCII码0~9)
PTR_PLAYER      .FILL x0000     ;储存当前玩家,0为P1,1为P2

5、主程序及玩家控制:

;主程序
;FUNC_MAIN
LOOP_PLAY       JSR FUNC_DRAW       ;开局先输出一次石堆LD  R6, PTR_PLAYERBRz FUNC_P1         ;为0就是玩家1BRp FUNC_P2         ;为1就是玩家2
PLAY_AFTER      LD  R0, CHAR_LFOUT                  ;输出换行JSR FUNC_CHANGE        ;切换玩家BRnzp LOOP_PLAY        ;循环就是了,跳出在玩家控制中;玩家1控制
FUNC_P1         LEA R0, STR_PLAYER1 ;输出游戏提示PUTSJSR INPUT_ROW        ;输入行号JSR INPUT_CNT      ;输入数量JSR FUN_HASLEFT        ;进行一个的判断和取石头ADD R6, R6, #0      ;刷新CCBRnz FUNC_P1       ;非法输入,再来LD  R0, CHAR_LFOUT                  ;为了让输出和样例一模一样JSR FUN_CHECKADD R6, R6, #0        ;刷新CCBRnz PLAY_AFTER        ;R6=0,还没赢LD  R0, CHAR_LF       ;R6=1,赢了OUTLEA R0, STR_WIN_1PUTSBRnzp _END;玩家2控制(结构同玩家1)
FUNC_P2         LEA R0, STR_PLAYER2PUTSJSR INPUT_ROW        ;输入行号JSR INPUT_CNT      ;输入数量JSR FUN_HASLEFT        ;进行一个的判断和取石头ADD R6, R6, #0      ;刷新CCBRnz FUNC_P2       ;非法输入,再来LD  R0, CHAR_LFOUT                  ;为了让输出和样例一模一样JSR FUN_CHECKADD R6, R6, #0        ;刷新CCBRnz PLAY_AFTER        ;R6=0,还没赢LD  R0, CHAR_LF       ;R6=1,赢了OUTLEA R0, STR_WIN_2PUTSBRnzp _END     ;玩家切换子程序(没有用到TRAP故)
FUNC_CHANGE LD  R6, PTR_PLAYERBRz SUB_ZEROADD R6, R6, #-1       ;如果是1就切0ST  R6, PTR_PLAYERBRnzp SUB_CHANGE_RET
SUB_ZERO        ADD R6, R6, #1      ;如果是0就切1ST  R6, PTR_PLAYER
SUB_CHANGE_RET  RET

完整代码如下(仅供参考,建议独立思考,所以我删掉了完整代码中的很多注释哈哈哈哈):

             .ORIG x3000;主程序
;FUNC_MAIN
LOOP_PLAY       JSR FUNC_DRAWLD  R6, PTR_PLAYERBRz FUNC_P1BRp FUNC_P2
PLAY_AFTER      LD  R0, CHAR_LFOUT          ;LFJSR FUNC_CHANGEBRnzp LOOP_PLAY;玩家1
FUNC_P1         LEA R0, STR_PLAYER1PUTSJSR INPUT_ROWJSR INPUT_CNTJSR FUN_HASLEFTADD R6, R6, #0BRnz FUNC_P1LD  R0, CHAR_LFOUTJSR FUN_CHECKADD R6, R6, #0BRnz PLAY_AFTERLD  R0, CHAR_LFOUTLEA R0, STR_WIN_1PUTSBRnzp _END;玩家2
FUNC_P2         LEA R0, STR_PLAYER2PUTSJSR INPUT_ROWJSR INPUT_CNTJSR FUN_HASLEFTADD R6, R6, #0BRnz FUNC_P2LD  R0, CHAR_LFOUTJSR FUN_CHECKADD R6, R6, #0BRnz PLAY_AFTERLD  R0, CHAR_LFOUTLEA R0, STR_WIN_2PUTSBRnzp _END     ;玩家切换子程序
FUNC_CHANGE LD  R6, PTR_PLAYERBRz SUB_ZEROADD R6, R6, #-1ST  R6, PTR_PLAYERBRnzp SUB_CHANGE_RET
SUB_ZERO        ADD R6, R6, #1ST  R6, PTR_PLAYER
SUB_CHANGE_RET  RET;输入行号(ABCDEFGHIJKLMNOPQRSTUVWXYZ)
INPUT_ROW       ST  R7, SAVER7GETCOUTST  R0, PTR_ROWLD  R7, SAVER7RET;输入个数
INPUT_CNT       ST  R7, SAVER7GETCOUTST  R0, PTR_CNTLD  R7, SAVER7RETPTR_ROW            .BLKW 1
PTR_CNT         .BLKW 1
PTR_PLAYER      .FILL x0000;石头状态输出
;R1-石头数量地址,R2-数量
FUNC_DRAW       ST  R7,SAVER7LD  R1,PTR_STONESLD  R3,CHAR_ROWBASE;遍历石头堆
LOOP_1          LDR R2,R1,#0BRn END_DRAWLEA R0,STR_ROWPUTSAND R0,R0,#0ADD R0,R3,#0OUTLEA R0,STR_ROW2PUTSLD  R0,CHAR_SYMBOLADD R2,R2,#0;循环输出o
LOOP_2          BRz OUT_2OUT    ADD R2,R2,#-1BRnzp LOOP_2
OUT_2           LD  R0,CHAR_LFOUTADD R1,R1,#1ADD R3,R3,#1       BRnzp LOOP_1
END_DRAW        LD R7,SAVER7RET;检查石头是否完了
;Output(R6):false(0),true(1)
FUN_CHECK       LD  R1, PTR_STONESAND R6, R6, #0ADD R6, R6, #1
SUB_CHECK_LOOP  LDR R2, R1, #0BRp SUB_CHECK_FALSEBRn SUB_CHECK_ENDADD R1, R1, #1BRnzp SUB_CHECK_LOOP    SUB_CHECK_FALSE ADD R6, R6, #-1
SUB_CHECK_END   RET;判断当前的输入是否还有剩余,若无剩余输出错误信息
;Input(x5001)  Output(R6):false(0),p-left(n)
FUN_HASLEFT     ST  R7, SAVER7LD  R1, PTR_STONESLD  R2, PTR_ROWLD  R3, NCHAR_AADD R2, R2, R3BRn SUB_HAS_ERRORADD R1, R1, R2AND R6, R6, #0LDR R2, R1, #0BRnz SUB_HAS_ERRORADD R6, R6, R2
SUB_HAS_SUB     LD  R2, PTR_CNTLD  R3, NCHAR_0ADD R2, R2, R3BRnz SUB_HAS_ERRORNOT R2, R2ADD R2, R2, #1ADD R4, R6, R2BRn SUB_HAS_ERRORSTR R4, R1, #0BRnzp SUB_HAS_END
SUB_HAS_ERROR   AND R6, R6, #0LD  R0, CHAR_LFOUTLEA R0, STR_INVALIDPUTSLD  R0, CHAR_LFOUT
SUB_HAS_END     LD  R7, SAVER7RET_END           HALTCHAR_ROWBASE    .FILL x0041 ;'A'的ASCII码
NCHAR_A         .FILL xFFBF ;'A'的负值
NCHAR_0         .FILL xFFD0 ;'0'的负值
CHAR_SYMBOL     .FILL x006f ;'o'的ASCII码
CHAR_LF         .FILL x000A ;换行符的ASCII
NCHAR_LF        .FILL xFFF6 ;LF的负值
PTR_STONES      .FILL x3200 ;石头数量信息储存起点STR_ROW          .STRINGZ "ROW "
STR_ROW2        .STRINGZ ": "
STR_PLAYER1     .STRINGZ "Player 1, choose a row and number of rocks:"
STR_PLAYER2     .STRINGZ "Player 2, choose a row and number of rocks:"
STR_WIN_1       .STRINGZ "Player 1 Wins."
STR_WIN_2       .STRINGZ "Player 2 Wins."
STR_INVALID     .STRINGZ "Invalid move. Try again."SAVER7         .BLKW 1     ;保存R7.END

五、小小总结

这次的实验长度(难度)肯定是比上次要翻倍的,不过有了子程序思想的加持,以及对LC-3汇编指令理解的加深,感觉也不是特别的难。
以上代码可能仍存在一些问题,但毕竟只是个实验嘛,凑合着做做(逃)~

计算机系统1 实验 LC-3 Nim游戏 子程序相关推荐

  1. LC-3 汇编语言 Nim游戏

    @_@汇编T_T 目录 题目描述 AC代码 思路分析 题目描述 Nim是一个简单的双人游戏,可能起源于中国.游戏中使用的计数器类型有很多种类,如石头.火柴.苹果等.游戏界面被划分为很多行,每行中有数量 ...

  2. 计算机系统安装和维护实验,2计算机系统安装维护实验报告.doc

    2计算机系统安装维护实验报告 计算机系统安装维护实验报告 院系: 班级: 姓名: 学号: [实验目的] 学习了解u盘硬盘启动维护系统 [实验内容] 通过GHOST11软件进行系统的相关操作 用户可以通 ...

  3. 【bzoj3150】 cqoi2013—新Nim游戏

    www.lydsy.com/JudgeOnline/problem.php?id=3105 (题目链接) 题意 在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴.可以一堆都不拿,但不可以全部拿 ...

  4. LeetCode实战:Nim 游戏

    背景 为什么你要加入一个技术团队? 如何加入 LSGO 软件技术团队? 我是如何组织"算法刻意练习活动"的? 为什么要求团队的学生们写技术Blog 题目英文 You are pla ...

  5. POJ 1704 Georgia and Bob (Nim游戏变形)

    题目:http://poj.org/problem?id=1704 思路:Nim游戏策略,做如下转换,如果N是偶数,则两两配对,将两个数之间的格子数(距离)看做成这一堆石头的数量. 如果N是奇数,则将 ...

  6. BZOJ 3105:[cqoi2013]新Nim游戏

    BZOJ 3105:[cqoi2013]新Nim游戏 题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3105 题目大意:在传统的Nim取石子 ...

  7. 【bzoj3105】新Nim游戏

    Portal--> bzoj3105 新Nim游戏 Solution 转化一下问题 首先看一下原来的Nim游戏,先手必胜的条件是:每堆数量的异或和不为\(0\) 所以在新的游戏中,如果要保证自己 ...

  8. 文巾解题 292. Nim 游戏

    1 题目描述 2 解题思路 这其实是一个脑筋急转弯一样的题目.先给出结论吧:如果堆中石头的数量 n 不能被 4 整除,那么你总是可以赢得 Nim 游戏的胜利. 下面是推导部分: 让我们考虑一些小例子. ...

  9. 洛谷P4301 [CQOI2013]新Nim游戏

    洛谷P4301 [CQOI2013]新Nim游戏 题目描述 传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同).两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火 ...

最新文章

  1. jdbcTemplate 的update 和batchUpdate 方法的使用
  2. 2021年全球知名开源项目大更新
  3. 西门子rwd68温控器说明书_西门子RWD68说明书
  4. iPhone 播放音频声音文件
  5. document.execCommand() 解析
  6. elastix中NAT穿越问题解决办法
  7. 被final关键字坑了
  8. 写在今年(2022)清明节前
  9. linux操作系统期末试卷及答案,Linux操作系统期末复习题(含答案).pdf
  10. Springer-Verlag免费下载图书400本
  11. Vine已死,秒拍抱团崛起,同是视频社交差距在哪?
  12. AUTOSAR I-PDU的理解以及I-PDU的Callout
  13. Qt无边框窗体(Windows)
  14. Typora编辑器(蓝奏)
  15. 5G时代下的物联网发展前景,物联网人才的发展机遇有哪些?
  16. 【Python基础学习笔记day08】python变量的基本使用+变量定义ipython+pycharm+超市买苹果+变量的类型+关于函数+变量的格式化输出+个人名片案例
  17. 【无标题】一款功能非常强大的免费串口示波器串口助手,支持绘图,logo保存数据保存,历史数据加载与对比。
  18. Centos离线安装Kubernetes集群
  19. Electron系列教程——第二篇:第一个示例
  20. SegFormer: Simple and Efficient Design for Semantic Segmentation with Transformers

热门文章

  1. 去掉CAJViewer广告的方法
  2. [LBS学习笔记4]地理特征POI、AOI、路径轨迹
  3. java安卓app开发教程_[Android教程] Cordova开发App入门(一)创建android项目
  4. 【processing码绘】简单实现码绘动态图形与拓展
  5. python 动态图形_在matplotlib中动态更新图形
  6. 计算机的命令用英语怎么说,计算机外部和内部命令的英语及词汇
  7. ss3ex集成Beet记录日志
  8. React中文文档之Rendering Elements
  9. 鲲鹏Arm64 openEuler 虚拟机学习
  10. 算法 64式 17、排列组合算法整理