题目要求
四子棋是一款普遍流行的简易型桌面游戏,据说,虎克船长曾因专注于此游戏而长期隐身在住所,当船员们发现船长的这一专长之后,他们称这个游戏为“船长的情妇”。
四子棋是个双人游戏,两人轮流下棋,棋盘由行和列组成的网格,每个选手每次下一个子直到两人中有一人的棋子连成一条水平线、垂直线或者是对角线。
本实验需要在LC-3中实现简易版四子棋的游戏,两位选手通过键盘和输出窗口轮流交互操作,棋盘由6 X 6的网格组成。
- 两位选手依次轮流落子;
- 选手不能悔棋;
- 有子的地方不能继续落子;
- 直到有一方的四个棋子能够连成一条水平线、垂直线或者是对角线;
- 如果棋盘已满,无人获胜,则平局。
游戏最初时应该打印空的棋盘,可以用ASCII码"-" (即ASCII 码 x002D)来表示该处为空,"O"(ASCII 码 x004F)表示第一位选手的棋子,"X" (ASCII 码 x0058)来表示第二位选手的棋子,为了让棋盘更易于观察,在各列间加一个空格,第6列之后不要添加。
选手一始终先下第一步棋,然后两者轮流落子,在每次落子之后,应该打印该选手的信息,提示他落子,以选手一为例,应该打印信息如下:
Player 1, choose a column:
为了明确选手的落子的位置,该选手应该输入数字1-6,然后回车,数字1-6指示在落子所在的列,从左到右,无需输入行号,程序应默认从行号6到行号1递减的顺序填入该棋子,若前后输入的列号相同,则行号减一。例如,如果选手第一次在左起第二列落子,应该输入2,然后回车,则该棋子落在行6列2处,当后面输入的列号再次为2时,则将棋子落子行5列2处,以此类推,详情见后续示例输出。程序应该确保选手输入的数字对应正确的列的范围,如果输入不合理,应该输出一条错误信息,提示该选手继续输入,例如,如果对于选手一:
Player 1, choose a column: D
Invalid move. Try again.
Player 1, choose a column: 7
Invalid move. Try again.
Player 1, choose a column:
程序应该一直提示该选手,知道输入正确的数字,当用户输入完成,程序应通过显示回馈给选手,然后通过换行符(ASCII 码 x000A)换行。
当选手输入成功后,程序应打印更新后的棋盘,并检查是否有人获胜,如果没人获胜,则轮到下一位输入。
当其中一位获胜或平局时,游戏结束,程序显示最后的棋盘情况并终止(Halt)。例如,如果选手二有四子相连,应该输出:
Player 2 Wins.
如果平局,程序应该输出:
Tie Game.
示例输出(点击展开)
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
Player 1, choose a column: 1
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
O - - - - -
Player 2, choose a column: 2
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- - - - - -
O X - - - -
Player 1, choose a column: 2
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- O - - - -
O X - - - -
Player 2, choose a column: 3
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- O - - - -
O X X - - -
Player 1, choose a column: 3
- - - - - -
- - - - - -
- - - - - -
- - - - - -
- O O - - -
O X X - - -
Player 2, choose a column: 1
- - - - - -
- - - - - -
- - - - - -
- - - - - -
X O O - - -
O X X - - -
Player 1, choose a column: 4
- - - - - -
- - - - - -
- - - - - -
- - - - - -
X O O - - -
O X X O - -
Player 2, choose a column: 4
- - - - - -
- - - - - -
- - - - - -
- - - - - -
X O O X - -
O X X O - -
Player 1, choose a column: 3
- - - - - -
- - - - - -
- - - - - -
- - O - - -
X O O X - -
O X X O - -
Player 2, choose a column: 4
- - - - - -
- - - - - -
- - - - - -
- - O X - -
X O O X - -
O X X O - -
Player 1, choose a column: 4
- - - - - -
- - - - - -
- - - O - -
- - O X - -
X O O X - -
O X X O - -
Player 1 Wins.
----- Halting the processor -----
思路
代码流程
- LOOP
- 更换玩家
- 展示地图
- 输入列号
- 不是1-6重新输入
- 这一列没有空位重新输入
- 修改地图内存实现落子
- 判断胜利(子程序)
- 判断平局(子程序)
难点
地图存储
一行加上换行刚好12个字符,所以使用一个内存指针R5访问地图,初始化为MAP的地址,移动到下一行+12,移动到下一列+2
判断是不是1-6
使用两个label存储1和6的ascall码,然后比较即可
BRnzp #2
NUMBER1 .FILL x0031
NUMBER6 .FILL x0036
;判断输入字符是否可行
LD R0,NUMBER1
NOT R0,R0
ADD R0,R0,#1;正数取反
ADD R0,R0,R2
BRn INVAL;小于1违法
LD R0,NUMBER6
NOT R0,R0
ADD R0,R0,#1;正数取反
ADD R0,R0,R2
BRp INVAL;大于6违法
判断是否有空位落子
内存开辟一段6位的空间,用ACCOUNT标记,存放每一列已落棋子个数,落子时从内存中取出对应值,判断是否小于6即可。然后将行存储下来(我赋值给了R3,从0开始)
;判断有没有空位
LEA R0, ACCOUNT
ADD R0,R0,R2
LDR R3,R0,#0
ADD R3,R3,#-6
BRzp INVAL;没有空位,违法,重新输入
ADD R3,R3,#7
STR R3,R0,#0;更新account记录
NOT R3,R3
ADD R3,R3,#7;R3现在为行坐标(0开始)
修改地图实现落子
采用指针R5来定位,初始化为MAP首地址,然后+12*行+2*列即可定位(PS:我的行和列从0开始),然后LDR R4,R5,#0即可将当前玩家对应的子存到地图中
;修改地图
ADD R0,R3,#1;
LEA R5,MAP;R5为地图指针
LOOP1
ADD R0,R0,#-1
BRnz LOOPEND1
ADD R5,R5,#12
BRnzp LOOP1
LOOPEND1
ADD R5,R5,R2;
ADD R5,R5,R2;R5指针定位完毕
STR R4,R5,#0
判断胜利
该实验最大的难点是如何判断胜利,因为LC3的寄存器有限,无法像C++那样多重循环去判断。所以我分成两步进行
- 计算八个方向(上下左右,右上右下左上左下)以落子点开始的连续相同棋子个数(不计入当前落子)
- 每个方向使用循环一直将R3,R2,R5(行,列,map内存指针)往对应方向变,直到出现和当前落子不相等的符号,跳出循环,存储连续个数R0
- 注意在取R5对应的字符前判断是否越界,越界跳出循环,存储连续个数R0
- 在每个方向的循环前有个存储R0,加载R2,R3,R5的过程
- 按照左右,上下,主对角线,副对角线4个方向计算连续棋子个数是否>=4(eg上+下,左上+右下)
- 占用8位内存的ACCOUNT2按顺序存储右,左,上,下,右上,右下,左上,左下的连续个数
如果胜利BR到胜利逻辑然后HALT,否则RET(JMP R7简写)
判断平局
这个简单,将ACCOUNT中所有列的落子个数相加然后和36比较就行,注意ADD一次最多加15
如果平局BR到平局逻辑然后HALT,否则RET
一些思考
本来我没用JSR,但是JUDGE_WIN逻辑的代码太多了,导致BR的PC_offset9不够用,只能用11位的JSR了
除此之外要充分利用BR的负数部分,可以吧结尾的.STRINGZ / .FILL / .BLKW放一些在程序开始,用BRNZP跳过即可,这样可以充分利用LD,ST,BR等指令9位offset的范围,防止越界(只在不行还是用子程序吧TAT)
因为我所有的寄存器都是当全局变量使用的,所以子程序也就没有ST和LD恢复的部分了
代码实现
.ORIG x3000
AND R1,R1,#0
ADD R1,R1,#1;R1存储当前玩家
BRNZP LOOP0
NUMBER1 .FILL x0031
NUMBER6 .FILL x0036
ENTER .FILL x000A
OCCUPY_1 .FILL x004F;"O"
OCCUPY_2 .FILL x0058;"X"
LOOP0
NOT R1,R1
ADD R1,R1,#2
BRz #2
LD R4,OCCUPY_2
BRnzp #1
LD R4,OCCUPY_1
;R4存放当前玩家的子
LEA R0, MAP
PUTS;展示地图
INPUT
LEA R0,PROMPT_1
PUTS
LD R0,NUMBER1;载入字符1的ascall码
ADD R0,R0,R1
OUT;显示玩家编号
LEA R0, PROMPT_2
PUTS
GETC
OUT;回显
ADD R2,R0,#0;R2存储输入的字符
LD R0,ENTER
OUT;换行
;判断输入字符是否可行
LD R0,NUMBER1
NOT R0,R0
ADD R0,R0,#1;正数取反
ADD R0,R0,R2
BRn INVAL;小于1违法
LD R0,NUMBER6
NOT R0,R0
ADD R0,R0,#1;正数取反
ADD R0,R0,R2
BRp INVAL;大于6违法
;'1'对应0,将ascall转成int
LD R0,NUMBER1
NOT R0,R0
ADD R0,R0,#1;
ADD R2,R0,R2;R2存放列
;判断有没有空位
LEA R0, ACCOUNT
ADD R0,R0,R2
LDR R3,R0,#0
ADD R3,R3,#-6
BRzp INVAL;没有空位,违法,重新输入
ADD R3,R3,#7
STR R3,R0,#0;更新account记录
NOT R3,R3
ADD R3,R3,#7;R3现在为行坐标(0开始)
;修改地图
ADD R0,R3,#1;
LEA R5,MAP;R5为地图指针
LOOP1
ADD R0,R0,#-1
BRnz LOOPEND1
ADD R5,R5,#12
BRnzp LOOP1
LOOPEND1
ADD R5,R5,R2;
ADD R5,R5,R2;R5指针定位完毕
STR R4,R5,#0
;连线判断输赢
JSR JUDGE_WIN
JSR JUDGE_TIE
BRnzp LOOP0;while循环
INVAL
LEA R0,WARNING
PUTS
BRNZP INPUT;违法,重新输入
MAP .STRINGZ "- - - - - -\n- - - - - -\n- - - - - -\n- - - - - -\n- - - - - -\n- - - - - -\n"
PROMPT_1 .STRINGZ "Player "
PROMPT_2 .STRINGZ ", choose a column: "
PROMPT_WIN .STRINGZ " Wins."
PROMPT_TIE .STRINGZ "Tie Game."
WARNING .STRINGZ "Invalid move. Try again.\n"
ACCOUNT .STRINGZ "\0\0\0\0\0\0\0";计数器,记录每列已落棋子个数
JUDGE_TIE
AND R5,R5,#0
LEA R0,ACCOUNT
LDR R6,R0,#0
ADD R5,R5,R6
LDR R6,R0,#1
ADD R5,R5,R6
LDR R6,R0,#2
ADD R5,R5,R6
LDR R6,R0,#3
ADD R5,R5,R6
LDR R6,R0,#4
ADD R5,R5,R6
LDR R6,R0,#5
ADD R5,R5,R6
ADD R5,R5,#-10
ADD R5,R5,#-10
ADD R5,R5,#-10
ADD R5,R5,#-6
BRz TIE
RET
;平局逻辑
TIE
LEA R0,MAP
PUTS
LEA R0,PROMPT_TIE
PUTS
HALT
;胜利逻辑
WIN
LEA R0,MAP
PUTS
LEA R0,PROMPT_1
PUTS
LD R0,NUMBER1;载入字符1的ascall码
ADD R0,R0,R1
OUT;显示玩家编号
LEA R0, PROMPT_WIN
PUTS
HALT;结束程序
;判断胜利子程序
JUDGE_WIN
ST R2,Y
ST R3,X
ST R5,POINTER;
;右
RIGHT_BEFORE
AND R0,R0,#0
LD R2,Y
LD R3,X
LD R5,POINTER
RIGHT
ADD R2,R2,#1
ADD R6,R2,#-6
BRzp LEFT_BEFORE;超边界跳出
ADD R5,R5,#2
LDR R6,R5,#0
NOT R6,R6;
ADD R6,R6,#1
ADD R6,R6,R4
BRnp #2
ADD R0,R0,#1
BRNZP RIGHT
;左
LEFT_BEFORE
LEA R6,ACCOUNT2
STR R0,R6,#0
AND R0,R0,#0
LD R2,Y
LD R3,X
LD R5,POINTER
LEFT
ADD R2,R2,#-1
BRn UP_BEFORE;超边界跳出
ADD R5,R5,#-2
LDR R6,R5,#0
NOT R6,R6;
ADD R6,R6,#1
ADD R6,R6,R4
BRnp #2
ADD R0,R0,#1
BRNZP LEFT
;上
UP_BEFORE
LEA R6,ACCOUNT2
STR R0,R6,#1
AND R0,R0,#0
LD R2,Y
LD R3,X
LD R5,POINTER
UP
ADD R3,R3,#-1
BRn DOWN_BEFORE;超边界跳出
ADD R5,R5,#-12
LDR R6,R5,#0
NOT R6,R6;
ADD R6,R6,#1
ADD R6,R6,R4
BRnp #2
ADD R0,R0,#1
BRNZP UP
;下
DOWN_BEFORE
LEA R6,ACCOUNT2
STR R0,R6,#2
AND R0,R0,#0
LD R2,Y
LD R3,X
LD R5,POINTER
DOWN
ADD R3,R3,#1
ADD R6,R3,#-6
BRzp RIGHTUP_BEFORE;超边界跳出
ADD R5,R5,#12
LDR R6,R5,#0
NOT R6,R6;
ADD R6,R6,#1
ADD R6,R6,R4
BRnp #2
ADD R0,R0,#1
BRNZP DOWN
;右上
RIGHTUP_BEFORE
LEA R6,ACCOUNT2
STR R0,R6,#3
AND R0,R0,#0
LD R2,Y
LD R3,X
LD R5,POINTER
RIGHTUP
ADD R2,R2,#1
ADD R6,R2,#-6
BRzp RIGHTDOWN_BEFORE;超边界跳出
ADD R3,R3,#-1
BRn RIGHTDOWN_BEFORE;超边界跳出
ADD R5,R5,#-10;-12+2
LDR R6,R5,#0
NOT R6,R6;
ADD R6,R6,#1
ADD R6,R6,R4
BRnp #2
ADD R0,R0,#1
BRNZP RIGHTUP
;右下
RIGHTDOWN_BEFORE
LEA R6,ACCOUNT2
STR R0,R6,#4
AND R0,R0,#0
LD R2,Y
LD R3,X
LD R5,POINTER
RIGHTDOWN
ADD R2,R2,#1
ADD R6,R2,#-6
BRzp LEFTUP_BEFORE;超边界跳出
ADD R3,R3,#1
ADD R6,R3,#-6
BRzp LEFTUP_BEFORE;超边界跳出
ADD R5,R5,#14
LDR R6,R5,#0
NOT R6,R6;
ADD R6,R6,#1
ADD R6,R6,R4
BRnp #2
ADD R0,R0,#1
BRNZP RIGHTDOWN
;左上
LEFTUP_BEFORE
LEA R6,ACCOUNT2
STR R0,R6,#5
AND R0,R0,#0
LD R2,Y
LD R3,X
LD R5,POINTER
LEFTUP
ADD R2,R2,#-1
BRn LEFTDOWN_BEFORE;超边界跳出
ADD R3,R3,#-1
BRn LEFTDOWN_BEFORE;超边界跳出
ADD R5,R5,#-14
LDR R6,R5,#0
NOT R6,R6;
ADD R6,R6,#1
ADD R6,R6,R4
BRnp #2
ADD R0,R0,#1
BRNZP LEFTUP
;左下
LEFTDOWN_BEFORE
LEA R6,ACCOUNT2
STR R0,R6,#6
AND R0,R0,#0
LD R2,Y
LD R3,X
LD R5,POINTER
LEFTDOWN
ADD R2,R2,#-1
BRn PRE_OP_END;超边界跳出
ADD R3,R3,#1
ADD R6,R3,#-6
BRzp PRE_OP_END;超边界跳出
ADD R5,R5,#10
LDR R6,R5,#0
NOT R6,R6;
ADD R6,R6,#1
ADD R6,R6,R4
BRnp #2
ADD R0,R0,#1
BRNZP LEFTDOWN
PRE_OP_END
LEA R6,ACCOUNT2
STR R0,R6,#7
;左右、上下、主副对角线求和,判断有没有>=3个子
LEA R6,ACCOUNT2
;左右
LDR R2,R6,#0
LDR R3,R6,#1
ADD R2,R2,R3
ADD R2,R2,#-3
BRzp WIN
;上下
LDR R2,R6,#2
LDR R3,R6,#3
ADD R2,R2,R3
ADD R2,R2,#-3
BRzp WIN
;主对角线
LDR R2,R6,#5
LDR R3,R6,#6
ADD R2,R2,R3
ADD R2,R2,#-3
BRzp WIN
;副对角线
LDR R2,R6,#4
LDR R3,R6,#7
ADD R2,R2,R3
ADD R2,R2,#-3
BRzp WIN
RET;返回循环
ACCOUNT2 .STRINGZ "\0\0\0\0\0\0\0\0"
X .BLKW 1
Y .BLKW 1
POINTER .BLKW 1
.END
Comments 3 条评论
博主 我不理解
好深奥欧
博主 99wen
Warning: 通过Sakurairo获取IP地理位置失败:返回的数据不是json格式 in /www/wordpress/wwwroot/wp-content/themes/Sakurairo/inc/classes/IpLocation.php on line 58
中国 广东 广州市
就是这个作业!!写完太感动让我发了一篇csdn
博主 ChainPray
Warning: 通过Sakurairo获取IP地理位置失败:返回的数据不是json格式 in /www/wordpress/wwwroot/wp-content/themes/Sakurairo/inc/classes/IpLocation.php on line 58
中国 广东 深圳
@99wen crazy