(因为不会用子程序所以直接写了)
一、Nim游戏规则
Nim是一个简单的双人游戏,可能起源于中国。游戏中使用的计数器类型有很多种类,如石头、火柴、苹果等。游戏界面被划分为很多行,每行中有数量不等的计数器,如图1所示:
行号 计数器数量
1 ooo
2 ooooo
… …
n oooooooooooooo…
本次实验对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.
二、实验步骤与过程
由于游戏过程整体比较复杂,所以把整个流程分割成几个部分分别编写。
战术思维导图↓
代码展示:
在正式浏览代码之前,展示一下说明:
;Nim游戏编程
;R0:输入与输出的寄存器
;R1:表示石头数量的寄存器
;R2: 表示行动方的寄存器
;其他寄存器:用于操作
.ORIG x3000
- 初始化寄存器:
;初始化
AND R2,R2,#0;R2清零,说明第一次行动由p1开始
- 输出棋盘
;输出棋盘
pvt1
LEA R0,HeadA; 输出a行的开头和石头数量
PUTS;
LD R0,Stone;
LD R1,LineA;
Loop1
BRz #3;
OUT;
ADD R1,R1,#-1;
BRnzp Loop1;
LD R0,ENDL
OUT
图中仅展示了A行的输出,B和C行代码相似,仅需将HeadA,LineA改为B和C对应的符号、Loop1分别改为别的名字即可
- 输入数据、合法判断
;输入和合法判断处理部分
pvt2
ADD R2,R2,#0; R2为0则表示轮到p1行动,为-1则表示轮到p2行动
;输入数据相关的代码
BRn Player2 ; 判断当前棋手
Player1 LEA R0,OutP1;
BRnzp #1;
Player2 LEA R0,OutP2;
PUTS;
GETC;
OUT;
LD R6,NegA; 将输入的行数的ascii码(ABC)对应转化为数字(012)
ADD R6,R0,R6;
GETC;
OUT;
LD R5,NegZero; 同上
ADD R5,R0,R5;
LD R0,ENDL;
OUT;
;判断R5是否会小于等于0
ADD R5,R5,#0;
BRnz Restart;
;判断行合法
ADD R7,R6,#-2;
BRp Restart; 说明输入行数大于c(ASCII),直接重新输入
ADD R7,R6,#0;
BRn Restart; 说明输入行数小于a(ASCII),直接重新输入
;判断石头数合法
LEA R3,LineA;
ADD R3,R3,R6;
LDR R3,R3,#0;
NOT R5,R5;
ADD R5,R5,#1;
ADD R4,R3,R5;
BRn Restart; 说明输入的石头数大于了对应行的石头数,直接重新输入
LEA R3,LineA;
ADD R3,R3,R6
STR R4,R3,#0;
BRnzp pvt3; 说明输入合法,存放完数据之后就进入判断是否游戏结束阶段
- 非法输入折返
Restart
LEA R0,Invalid;
PUTS;
LD R0,ENDL;
OUT;
BRnzp pvt2;
- 判断游戏结束
pvt3
AND R3,R3,#0;
ADD R3,R3,#3;
LD R1,LineA; 若a行数量为0则r3-=1
BRp #1;
ADD R3,R3,#-1;
LD R1,LineB; 同上
BRp #1;
ADD R3,R3,#-1;
LD R1,LineC; 同上
BRp #1;
ADD R3,R3,#-1;
ADD R3,R3,#0;
BRz OVER; 如果r3为0,则游戏结束
NOT R2,R2; 否则,交换棋手,游戏继续
LD R0,ENDL;
OUT; 为了美观输出一下换行
BRnzp pvt1;
- 游戏结束语
OVER
ADD R2,R2,#0; 判断获胜棋手
BRn #2;
LEA R0,over1;
BRnzp #1;
LEA R0,over2;
PUTS;
LD R0,ENDL;
OUT;
HALT
最后是符号表:
;公用符号表
LineA .FILL X3
LineB .FILL X5
LineC .FILL X8
ENDL .FILL x000A;
;pvt1输出棋盘部分的符号表
HeadA .STRINGZ "Row A: ";
HeadB .STRINGZ "Row B: ";
HeadC .STRINGZ "Row C: ";
Stone .FILL x006F;
;pvt2输入和合法判断处理部分的符号表
OutP1 .STRINGZ "Player 1, choose a row and number of stone:"
OutP2 .STRINGZ "Player 2, choose a row and number of stone:"
Invalid .STRINGZ "Invalid move. Try again."
NegZero .FILL xFFD0;
NegA .FILL xFFBF;
;OVER部分用到的字符串符号表
over1 .STRINGZ "Player 1 Wins."
over2 .STRINGZ "Player 2 Wins."
.END
以上就是思路和代码的展示了
三、小结
既然这不是正式的实验报告(而是网上的文章分享),那么测试样例和实验结论啥的咱就不写了
结尾声明一下:本文章中的思路和代码仅供参考,本人仅仅初次接触汇编语言,代码的书写或有不规范,方法的运用或有不熟练,请学界大佬多多包涵与指教,请前来参考的同志在阅读文章过后更要独立思考。