概述
目录
目的与要求
内容与方法
步骤与过程
程序总体设计
核心数据结构及算法流程
核心代码
调试过程
界面展示子程序DISPLAY(嵌套:球数展示子程序PUTBALL)
游戏子程序GAME(嵌套:单人每轮子程序PLAY)
结论或体会
目的与要求
⑴ 在游戏开始时,你应该显示游戏界面的初始化状态。具体包括:在每行石头的前面,你应该先输出行的名称,例如“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.
内容与方法
本次实验对Nim游戏做了一些小的改变,具体如下:游戏界面由三行组成,计数器类型为石头,其中A行包含3个石头,B行包含5个石头,C行包含8个石头。
规则如下:
⑴ 每个玩家轮流从某一行中移除一个或多个石头。
⑵ 一个玩家不能在一个回合中从多个行中移除石头。
⑶ 当某个玩家从游戏界面上移除最后剩余的石头时,此时游戏结束,该玩家获胜。
输入输出的格式必须完全和样例中的格式相一致
ROW A: ooo
ROW B: ooooo
ROW C: oooooooo
Player 1, choose a row and number of rocks: B2
ROW A: ooo
ROW B: ooo
ROW C: oooooooo
Player 2, choose a row and number of rocks: A1
ROW A: oo
ROW B: ooo
ROW C: oooooooo
Player 1, choose a row and number of rocks: C6
ROW 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: B3
ROW 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: C2
ROW A: oo
ROW B:
ROW C:
Player 2, choose a row and number of rocks: A1
ROW 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: A1
Player 1 Wins.
----- Halting the processor -----
⑴ 记住,程序中所有的输入输出使用ASCII字符,你应该负责进行必要的转换。
⑵ 从键盘中输入字符你应该使用TRAP x20(GETC)指令,同时为了回显输入的字符到屏幕上,你应该使用TRAP x21(OUT)指令,该指令紧跟在TRAP x20指令之后。
⑶ 你应该在适当的时候使用子程序。
⑷ 在你编写的每个子程序中,应该保存并还原所使用的任何寄存器。这将避免你在调试过程中遇到问题。
⑸ 在一个回合中,玩家的输入必须包含指定为A,B或C(即大写字母)的行,后面紧跟不大于该行仍然存在的石头数量的数字。
提示:
① 你应该设置程序的开始地址在x3000(如,程序的第一行指令应该为 .ORIG x3000)
② 源文件命名为nim.asm
步骤与过程
(依照实验内容,逐条撰写实验过程与实验所得结果:包括程序总体设计,核心数据结构及算法流程,调试过程。请附上核心代码,及注意格式排版的美观。实验提交时,以上为评分依据,请不删除本行)
程序总体设计
程序可大致分以下功能模块,可用子程序分别实现:
注意子程序的嵌套调用需要调用者保护R7。
每轮游戏包括2名玩家分别玩(调用PLAY子程序)。R5记录玩家身份,0为玩家1(Player1),1为玩家2(Player2),以便警告后可以正确返回到对应玩家重新开始以及判断胜负。每人每轮结束进行胜负判断,看总球数是否为0判断是否胜出。
-
- 界面展示子程序DISPLAY:先输出提示语句,在嵌套调用PUTBALL子程序输出球,ABC球数存于寄存器R1,R2,R3。
- 球数展示子程序PUTBALL:循环输出球,直至球数减为0。(do-while)
- 单人每轮子程序PLAY:根据输入的行名和球数判断是否有效,无效就警告并重新开始。注意类型转换以及数字字符(数字下限’0’)的判断。球数判断上限我采用直接取球,结果为负说明无效,改回并报错重来。警告操作包括输出警告并根据玩家身份(R5)跳转回对应起始重新开始。
- 界面展示子程序DISPLAY:先输出提示语句,在嵌套调用PUTBALL子程序输出球,ABC球数存于寄存器R1,R2,R3。
-
核心数据结构及算法流程
核心代码
.ORIG X3000
LD R1,Anum ;R1存A球数,初始3
LD R2,Bnum ;R2存B球数,初始5
LD R3,Cnum ;R3存C球数,初始7
;
;主函数游戏部分
player1 JSR DISPLAY ;调用展示界面子程序
AND R5,R5,0 ;R5清0表示为玩家1
begin1 LEA R0,Prompt1 ;传player1串
PUTS ;输出提示语句
JSR PLAY ;开始游戏
LD R0,NewLine ;/n
OUT
LD R0,NewLine ;/n
OUT
ADD R4,R1,R2
ADD R4,R4,R3
BRnp player2 ;球数不说明游戏继续
LEA R0,Win1 ;总球数为0玩家1胜利
PUTS
BRnzp exit ;游戏结束
player2 JSR DISPLAY ;调用展示界面子程序
ADD R5,R5,1 ;玩家2
begin2 LEA R0,Prompt2 ;传player2串
PUTS ;输出提示语句
JSR PLAY ;开始游戏
LD R0,NewLine ;/n
OUT
LD R0,NewLine ;/n
OUT
ADD R4,R1,R2
ADD R4,R4,R3
BRnp player1 ;球数不说明游戏继续
LEA R0,Win2 ;总球数为0玩家2胜利
PUTS
exit HALT
;
;--main数据区------------
Anum .FILL 3
Bnum .FILL 5
Cnum .FILL 8
NewLine .FILL 10 ;/n
Prompt1 .STRINGZ "Player 1, choose a row and number of rocks: " ;玩家1提示语句
Prompt2 .STRINGZ "Player 2, choose a row and number of rocks: " ;玩家2提示语句
Win1 .STRINGZ "Player 1 Wins.";玩家1胜利语句
Win2 .STRINGZ "Player 2 Wins.";玩家2胜利语句
;******************************************************************************************
;*******展示界面子程序**************************************
DISPLAY ST R7,DISPLAYR7
LEA R0,PromptA ;传A串
PUTS ;输出A提示语句
ADD R4,R1,0 ;传A球数
JSR PUTBALL ;调用展示球数子程序
LEA R0,PromptB ;传B串
PUTS ;输出B提示语句
ADD R4,R2,0 ;传B球数
JSR PUTBALL ;调用展示球数子程序
LEA R0,PromptC ;传C串
PUTS ;输出C提示语句
ADD R4,R3,0 ;传C球数
JSR PUTBALL ;调用展示球数子程序
LD R7,DISPLAYR7
RET
;
;--DISPLAY数据区----------------------------------
DISPLAYR7 .FILL 0
PromptA .STRINGZ "ROW A: " ;A提示语句
PromptB .STRINGZ "ROW B: " ;B提示语句
PromptC .STRINGZ "ROW C: " ;C提示语句
;********************************************************
;********展示球数子程序****************************
PUTBALL ST R7,PUTBALLR7
LD R0,o ;'o'
output ADD R4,R4,-1 ;计数--
BRn finish ;输出完毕跳到换行
OUT
BRnzp output
finish LD R0,NewLine ;/n
OUT
LD R7,PUTBALLR7
RET
;
;--PUTBALL数据区-----------------------------
PUTBALLR7 .FILL 0
o .FILL 111 ;'o'
;*******************************************************
;************每人每轮游戏子程序*************
PLAY ST R5,SaveR5
ST R7,PLAYR7
GETC ;获得行名
OUT ;回显
ADD R6,R0,0 ;将行名存于R6
GETC ;获得取球数
OUT ;回显
LD R5,Term ;-'0'=-48
ADD R5,R0,R5
BRnz warn
LD R4,nA
ADD R4,R6,R4 ;=?'A'
BRnp testb ;不是就跳转查B
NOT R4,R5
ADD R4,R4,1 ;-取球数
ADD R1,R1,R4 ;取球
BRzp return ;取球有效返回
ADD R1,R1,R5 ;取球无效改回
BRnzp warn ;警告重来
testb LD R4,nB
ADD R4,R6,R4 ;=?'B'
BRnp testc ;不是就跳转查C
NOT R4,R5
ADD R4,R4,1 ;-取球数
ADD R2,R2,R4 ;取球
BRzp return ;取球有效返回
ADD R2,R2,R5 ;取球无效改回
BRnzp warn ;警告重来
testc LD R4,nC
ADD R4,R6,R4 ;=?'C'
BRnp warn ;不是就表明输入有误跳至警告
NOT R4,R5
ADD R4,R4,1 ;-取球数
ADD R3,R3,R4 ;取球
BRzp return ;取球有效返回
ADD R3,R3,R5 ;取球无效改回
warn LD R0,NewLine ;/n
OUT
LEA R0,Warning
PUTS ;输出警告
LD R0,NewLine ;/n
OUT
LD R5,SaveR5 ;获得玩家序号
BRz begin1 ;为0跳回玩家1
BRp begin2 ;为1跳回玩家2
return LD R5,SaveR5
LD R7,PLAYR7
RET
;
;--PLAY数据区----------------------------------------
SaveR5 .FILL 0
PLAYR7 .FILL 0
Term .FILL -48 ;数值ASCII转换
nA .FILL -65 ;-'A'
nB .FILL -66 ;-'B'
nC .FILL -67 ;-'C'
Warning .STRINGZ "Invalid move. Try again.";输入无效警告
;**************************************************************
调试过程
界面展示子程序DISPLAY(嵌套:球数展示子程序PUTBALL)
- 问题1:子程序无法正确返回(图1)
球数输出出错,一直输出,经调试是输出球的子程序返回报错,无法正常返回。它返回到该子程序调用的OUT的下一行代码,说明R7返回地址在嵌套调用TRAP服务程序时已被修改破坏,无法正常返回。这说明我应当做到调用者保护R7,调用前应当保护R7原值。
图表 1 地址返回有误
于是在2个子程序都加入调用者保护R7。
ST R7,SaveR7
LD R7,SaveR7
- 问题2:嵌套子程序破坏R7(图2)
修改后仍然无法正确返回,这一次返回到的是嵌套调用的子程序PUTBALL的下一条代码,说明在嵌套调用子程序时破坏了R7。
图表 2 DISPALY子程序调用者保护失效
多次嵌套使用子程序却用同一内存SaveR7保护,会导致进入嵌套子程序时SaveR7就被破坏。所以我们改变策略对每个子程序的调用者保护返回地址,都用新内存存储。
ST R7,DISPLAYR7
LD R7,DISPLAYR7
- 修改成功(图3)
另外发现需要添加换行。修改后如下,结果正确:
图表 3 界面展示子程序DISPLAY和球数展示子程序PUTBALL
游戏子程序GAME(嵌套:单人每轮子程序PLAY)
- 问题1:字符未转换为数字(Figure 1)
运行报错,经调试发现是所取数字没有做类型转换(-48【‘0’】)。(图)
Figure 1 字符未转换为数字
- 问题2:函数未返回(Figure 2)
忘加ret。
Figure 2 函数未返回
- 问题3:2人间应当显示界面(Figure 3)
Figure 3 2人间应当显示界面
- 问题4:换行破坏球数寄存器(Figure 4)
Figure 4 换行破坏球数寄存器
- 问题5:无效输入后没有新的提示语句(Figure 5)
Figure 5 无效输入后没有新的提示语句
- 问题6:0球还输出(Figure 6)
Figure 6
-
- 问题7:缺乏判断是否是数字(Figure 7)
Figure 7
- 运行结果正确(Figure 8 9)
Figure 8 运行结果正确
Figure 9 运行结果正确
结论或体会
通过本次实验尝试运用LC3实现Nim游戏,首次尝试运用子程序结构进行代码编写,同时完成了界面实现。在该过程中我意识到对返回地址的保护(调用者保护)的重要性,尤其在嵌套调用子程序时。另外也要注意字符和数字的类型转换。以及do-while的运用避免0时仍输出。
最后
以上就是现代龙猫为你收集整理的【计算机系统1】4 Nim游戏目的与要求内容与方法步骤与过程核心代码结论或体会的全部内容,希望文章能够帮你解决【计算机系统1】4 Nim游戏目的与要求内容与方法步骤与过程核心代码结论或体会所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复