​持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天, 点击查看活动详情

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

计数器数量

○○○○○○

○○○○○○○○○○

本次实验对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: 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

	.orig x3000
again	jsr print
	jsr datain1
	jsr print
	jsr datain2
	br again
print	st r0,save_r0
	st r1,save_r1
	st r7,save_r7
	lea r0,row_a
	ld r0,stone
	ld r1,num_a
loop_a	out
	add r1,r1,#-1
	brp loop_a
	ld r0,cr
	lea r0,row_b
	ld r0,stone
	ld r1,num_b
loop_b	out
	add r1,r1,#-1
	brp loop_b
	ld r0,cr
	lea r0,row_c
	ld r0,stone
	ld r1,num_c
loop_c	out
	add r1,r1,#-1
	brp loop_c
	ld r0,cr
	ld r0,save_r0
	ld r1,save_r1
	ld r7,save_r7
save_r0 .fill #0
save_r1 .fill #0
stone 	.fill x006f
cr	.fill x000d
row_a	.stringz "ROW A: "
row_b	.stringz "ROW B: "
row_c	.stringz "ROW C: "
num_a	.fill #3
num_b	.fill #5
num_c	.fill #8
cue1	st r0,save_r0
	st r7,save_r7
	lea r0,play1
	ld r0,save_r0
	ld r7,save_r7
play1	.stringz "Player 1,choose a row and number of rocks:"
cue2	st r0,save_r0
	st r7,save_r7
	lea r0,play2
	ld r0,save_r0
	ld r7,save_r7
play2	.stringz "Player 2,choose a row and number of rocks:"
save_r7 .fill #0
datain1	st r0,save_r0
	st r2,save_r2
	st r3,save_r3
	st r7,saver7
try1	jsr cue1
	add r2,r0,#0
	not r2,r2
	add r2,r2,#1
	add r3,r0,#0
	ld r0,lf
test1a	ld r0,char_a
	add r0,r2,r0
	brnp test1b
	ld r0,char_0
	not r0,r0
	add r0,r0,#1
	add r0,r0,r3
	brn error1
	ld r3,num_a
	not r0,r0
	add r0,r0,#1
	add r3,r0,r3
	brn error1
	st r3,num_a
	ld r3,sum_abc
	add r3,r3,r0
	brz win1
	st r3,sum_abc
	br save
test1b	ld r0,char_b
	add r0,r2,r0
	brnp test1c
	ld r0,char_0
	not r0,r0
	add r0,r0,#1
	add r0,r0,r3
	brn error1
	ld r3,num_b
	not r0,r0
	add r0,r0,#1
	add r3,r0,r3
	brn error1
	st r3,num_b
	ld r3,sum_abc
	add r3,r3,r0
	brz win1
	st r3,sum_abc
	br save
test1c	ld r0,char_c
	add r0,r2,r0
	brnp error1
	ld r0,char_0
	not r0,r0
	add r0,r0,#1
	add r0,r0,r3
	brn error1
	ld r3,num_c
	not r0,r0
	add r0,r0,#1
	add r3,r0,r3
	brn error1
	st r3,num_c
	ld r3,sum_abc
	add r3,r3,r0
	brz win1
	st r3,sum_abc
	br save		
win1	ld r0,lf
	lea r0,wins1
error1	lea r0,invalid
	ld r0,lf
	br try1
datain2	st r0,save_r0
	st r2,save_r2
	st r3,save_r3
	st r7,saver7
try2	jsr cue2
	add r2,r0,#0
	not r2,r2
	add r2,r2,#1
	add r3,r0,#0
	ld r0,lf
test2a	ld r0,char_a
	add r0,r2,r0
	brnp test2b
	ld r0,char_0
	not r0,r0
	add r0,r0,#1
	add r0,r0,r3
	brn error2
	ld r3,num_a
	not r0,r0
	add r0,r0,#1
	add r3,r0,r3
	brn error2
	st r3,num_a
	ld r3,sum_abc
	add r3,r3,r0
	brz win2
	st r3,sum_abc
	br save
test2b	ld r0,char_b
	add r0,r2,r0
	brnp test2c
	ld r0,char_0
	not r0,r0
	add r0,r0,#1
	add r0,r0,r3
	brn error2
	ld r3,num_b
	not r0,r0
	add r0,r0,#1
	add r3,r0,r3
	brn error2
	st r3,num_b
	ld r3,sum_abc
	add r3,r3,r0
	brz win2
	st r3,sum_abc
	br save
test2c	ld r0,char_c
	add r0,r2,r0
	brnp error2
	ld r0,char_0
	not r0,r0
	add r0,r0,#1
	add r0,r0,r3
	brn error2
	ld r3,num_c
	not r0,r0
	add r0,r0,#1
	add r3,r0,r3
	brn error2
	st r3,num_c
	ld r3,sum_abc
	add r3,r3,r0
	brz win2
	st r3,sum_abc
	br save	
win2	ld r0,lf
	lea r0,wins2
error2	lea r0,invalid
	ld r0,lf
	br try2
save	ld r0,lf
	ld r0,saver0
	ld r2,save_r2
	ld r3,save_r3
	ld r7,saver7
lf	.fill x000a
char_a	.fill x0041
char_b	.fill x0042
char_c	.fill x0043
char_0	.fill x0030
wins1	.stringz "Player 1 Wins."
wins2	.stringz "Player 2 Wins."
invalid	.stringz "Invalid move. Try again."
sum_abc	.fill #16
saver0	.fill #0
save_r2 .fill #0
save_r3 .fill #0
saver7	.fill #0

程序总体设计

核心数据结构

1、显示游戏页面

首先将寄存器的值存进内存,待子函数完成任务后再将该内存的值存进寄存器,用伪操作.stringz开辟内存用来存储字符串,将Row A、Row B和Row C的石头数目也存储在内存中。

先用LEA指令将字符串的首地址存进R0,然后通过PUTS输出,用LD指令将字符o的ascll码存进R0,然后用LD指令将石头的数目存进R1,R1作为计数器,用OUT循环输出字符o,最后用LD指令将换行符的ascll码存进R0,用OUT输出。

2、用户操作

(1)输出提示

用伪操作.stringz将提示字符串存进内存中,先将用到的寄存器R0和R7的值存进内存保存起来,然后用LEA指令将字符串的首地址存进R0,用PUTS输出提示,然后将R0和R7的值恢复。

(2)用户输入

用GETC读取输入的第一个数据,然后用OUT回显,ADD指令将R0的数据转入R2,然后用NOT将R2取反,ADD将R2加一,即将R2取负,再用GETC读取输入的第二个数据,OUT回显,ADD指令将R0的数据转入R3。

(3)判断数据是否有效

用伪操作.fill将字符A、B和C的ascll码存进内存,用LD指令将相应字符的ascll码存进R0,然后ADD指令将R0和R2相加的结果存在R0,通过判断R0是否为0来判断是A、B、C或无效输入。

(4)取石头

用LD指令将字符0的ascll码存进R0,然后将R0取负,与R3相加的结果存放到R0中,然后用LD指令将石头的数目存进R3,将R0取负,与R3相加的结果存进R3,最后将R3的值存进内存。

1、显示游戏页面

2、player操作

_心灵骇客_ 36.4k
粉丝