8086汇编实现的五子棋小游戏

一、五子程序设计要求

汇编实现五子棋游戏。

首先显示空白棋盘,让玩家选择旗色,白子先行。

进入游戏对弈循环

只要有一方连成同色五子,即获胜。

事先写了一个普通的c++五子棋,然后将这个思路用汇编实现出来,代码逾500行,五子棋功能比较完善了,获胜逻辑什么的判断部分基本没有问题,花费了我很大的精力。选择旗色,先手玩家即为白色,后手玩家为黑色,分别以W,B代表黑白棋子。

二、设计思路

   五子棋,首先需要显示棋盘,然后玩家输入落子坐标,白方落子,黑方落子,落子循环,其中每次落子都要判断一次落子点是否已经有棋子了,那就需要提醒玩家重新输入落子坐标,同理玩家输入的坐标超出棋盘边界的时候也要提醒玩家。

   落子的部分解决了,就得考虑获胜的判定逻辑了,每次成功的落子,就要对该坐标进行判定,我的判定获胜的方法是这样的,对于落子坐标进行四类判定,一种是横向的,一种是纵向的,另外两类是左上到右下,与左下到右上的,这样就涵盖了所有的获胜的情况,并且具体怎么实现?设定一个变量COUNT计算同一线上的同色棋子数目,COUNT初始为1。从落子坐标出发,对于横向的,分两种搜索,一类向左搜索,遇到同类的棋子COUNT+1,一旦不是同色棋子或者是搜索到棋盘外便转到向右搜索的循环中去,每次COUNT+1后判断是否达到5,达到5了则当前落子方获胜,遇到非同色棋子或搜索到棋盘外跳出循环,重置COUNT为1后进入其它三类判定,其它三类的获胜判定与该法原理一致。原理简单易懂,但是在汇编上实现起来还是遇到了不少问题的。同时,每次有效落子的时候有个变量OVERFLOW需要+1,这个是计算和棋的变量,五子棋的棋盘是15*15大小的,当OVERFLOW达到225时还没有决出胜负,这时候便可以宣布和棋了。

   一些实现上的问题,由于8086汇编的输入中断一次仅识别一个ASII字符,导致两位数的输入还得手撸,便设置了两个暂存量TI,TJ用于暂存输入,事后再赋值给真正的落子坐标I,J。I,J的作用主要是控制循环,在获胜逻辑判断内判定是否已经超出棋盘边界了。汇编语言没有什么数据结构,因此我的棋盘MP二维数组的所有元素访问都要以一维的形式进行,应用基址+偏移寻址来找到需要的元素进行修改。还有条件跳转的范围实在太小了,汇编代码写着写着就很长了,需要经过好几个中转的跳转来实现一个大的循环,每次找错需要非常认真一句一句认真看,不过最后还是做完了。

三、核心代码

 

 

   1.显示棋盘部分:

……

     MOV DX,OFFSET STR2   ;输入提示语句

     MOV AH,9

     INT 21h

     MOV BX,OFFSET MP ;当前是第几个元素

     MOV CH,0  ;行数

NICE:

     MOV CL,0  ;列数

     CMP CH,10  ;行数大于等于10不用加前缀0

     JAE UPTEN 

     MOV AH,02H

     MOV DL,'0' ;加0前缀

     INT 21H

     MOV AH,02H

     MOV DL, CH

     ADD DL,30H

     INT 21H

     ……

     JMP LINE  ;小于10的数进入打印棋盘

UPTEN:

     MOV AH,02H

     MOV DL,'1'      ;十位

     INT 21H

     MOV AH,02H

     MOV DL,CH

     ADD DL,38  ;个位

     INT 21H

     ……      ;省略的是字符间隔调整部分

LINE:

     MOV AH, 02H

     MOV DL,[BX]

     ADD DL,30H

     INT 21H

     MOV AH,02H

     MOV DL, ' '

     INT 21H

     MOV AH,02H

     MOV DL, ' '

     INT 21H

     ADD BX,1  ;往后一个元素

     ADD CL,1

     CMP CL,15

     JB LINE        ;当前行未到尾部,继续输出

     MOV DL,0DH

     MOV AH,2

     INT 21H

     MOV DL,0AH      ;显示换行

     MOV AH,2

     INT 21H

     ADD CH,1;显示当前行  自增到下一行

     CMP CH,15

     JAE FIN   ;棋盘显示完毕,结束

     JMP NICE

FIN:

     POP DX         ;结束棋盘输出 恢复现场

     POP CX

     POP BX

     POP AX

     RET  ;返回原调用程序

SHOW ENDP

该段代码将棋盘MP的元素逐行输出,同时在左侧和上侧显示行数列数提示,便于用户确定落子坐标。

2.获胜逻辑判断部分:

其中BX是玩家选择的落子坐标的偏移地址,在主调用中给出

WIN PROC NEAR   ;获胜逻辑判断子程序

     PUSH AX

     PUSH BX

     PUSH CX

     PUSH DX;保存现场

     MOV AL,[BX]     ;当前是哪一方落子         这个NOW是W/B的ASII码减去30H的值

     MOV NOW,AL

     MOV CL,J;  从左到右的判断 根据J当前值确定是否退出循环

LEFT_TO_RIGHT_LEFT:        ;从左到右时count不刷新,进入新的一条线五子判断时才需要

     DEC CL          ;往左一个元素

     CMP CL,0  ;小于0则跳入右判断

     JB LEFT_TO_RIGHT_RIGHT

     SUB BX,1       ;BX地址减1

     CMP AL,[BX]

     JNE LEFT_TO_RIGHT_RIGHT    ;与落子的棋子不同

     ADD COUNT,1     ;若相同,一条线上的棋子数+1

     CMP COUNT,5     ;若COUNT达到5,说明有一方已经胜利了

     JE FIVE_TO_WINN ;此处条件跳转可能超出 需要找个中继继续跳转

     JMP LEFT_TO_RIGHT_LEFT ;循环继续

LEFT_TO_RIGHT_RIGHT: 

     MOV CL,J  ;需要初始化一些数据

     MOV BX,OFFSET MP ;重新定位到落子坐标

     ADD BX,CNTW     ;CNTW是落子坐标相对于MP的地址的16位偏移量

START_1:      

     INC CL         ;往右一个元素

     CMP CL,14  ;大于14则跳入下一个判断

     JA UP_TIL_DOWN_UP

     ADD BX,1

     CMP AL,[BX]

     JNE UP_TIL_DOWN_UP   ;不等时跳入下一个判断区间

     ADD COUNT,1     ;若相同,一条线上的棋子数+1

     CMP COUNT,5     ;若COUNT达到5,说明有一方已经胜利了

     JE FIVE_TO_WINN

     JMP START_1     ;循环继续

……

后续的三类判断与此相似,此处便不赘叙,需要注意每次继续搜索判断的下一个元素地址上会相差±1,15±1,±15,这样。但原理是差不多的。

3.落子坐标输入部分:格式3 4#当后一位数仅有一位需要以#结尾

MOV AH, 1 ; ah设置成1模式,用于从键盘输入

INT 21H ; 调用系统中断,输入

     CMP AL,30H

     JB W_NOT

     CMP AL,39H

     JA W_NOT

     JMP NEXTW  ;第一个输入无误

W_NOT:

     MOV DL,0DH

     MOV AH,2

     INT 21H

     MOV DL,0AH      ;显示换行

     MOV AH,2

     INT 21H

     MOV DX,OFFSET STR6

     MOV AH,9

     INT 21h  

     JMP IN_WHITE    ;返回白方落子循环处

NEXTW:

    SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字

     MOV TI,AL  ;暂存这个量

     MOV AH, 1 ; ah设置成1模式,用于从键盘输入

    INT 21H ; 调用系统中断,输入

     CMP AL,20H

     JE NEXTWW  ;检测到空格 跳到接受第二个数的位置

     CMP AL,30H

     JB W_NOT

     CMP AL,34H

     JA W_NOT

     CMP TI,1

     JA W_NOT  ;超出14的说明输入错误 否则TI为1

     MOV TI,10

     SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字

     MOV TJ,AL  ;暂存这个量

NEXTWW:

     MOV BL,TI

     ADD BL,TJ

     MOV I,BL  ;保留行数据

     MOV TI,0

     MOV TJ,0  ;初始化,防止数据遗留出问题

SEC:

     MOV AH, 1 ; ah设置成1模式,用于从键盘输入

    INT 21H ; 调用系统中断,输入

     CMP AL,20H

     JE SEC    ;检测到空格 返回一段

     CMP AL,30H

     JB W_NOT

     CMP AL,39H

follow:   JA W_NOT  ;下面接力,超出跳转位置

     SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字

     MOV TI,AL  ;暂存这个量

     MOV AH, 1 ; ah设置成1模式,用于从键盘输入

    INT 21H ; 调用系统中断,输入

     CMP AL,23H ;#结尾

     JE ENDWW  ;输入结束

     CMP AL,30H

     JB W_NOT

     CMP AL,34H

     JA W_NOT

     CMP TI,1

     JA follow  ;超出14的说明输入错误 否则TI为1

     MOV TI,10  ;转化成10便于操作。。因为只有10-14所以十位只有这个

     SUB AL, 30H ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字

     MOV TJ,AL  ;暂存这个量

ENDWW:

     MOV DL,0DH

     MOV AH,2

     INT 21H

     MOV DL,0AH      ;显示换行

     MOV AH,2

     INT 21H

     MOV BL,TI

     ADD BL,TJ

     MOV J,BL  ;保留列数据

 

这一块主要是把键盘输入的数据转化为需要落子的坐标,由于一次只识别一个ASII字符,需要进行多次判断,当前输入的是否是两位数,是否输入超出棋盘,会及时更正输入错误,提示玩家重新输入。

 运行视频:

https://vkceyugu.cdn.bspapp.com/VKCEYUGU-imgbed/a864674a-c59a-492d-a63f-1ccdd1d12962.mp4

 

由于几乎是从零开始做,需要汇编环境的准备,去图书馆查阅相关汇编程序设计的书,在书里学到了一些基本的编程语句用法就开始做了。先用c++实现了五子棋pvp对弈的demo,然后打算把这个思路用汇编来实现。demo写出来没花多少时间,但是手动转成汇编可着实有些难办。因为汇编几乎没有什么数据结构,我设想的棋盘数组MP得靠基址+偏移寻址来访问元素,二维化一维,繁琐程度加重了一些,但是好歹费尽一番功夫解决了,期间花一下午排除bug终于弄明白了一些简单的知识,只有BX可以当基址寻址的指定寄存器,偏移地址是不准用变量来表示的,这些句子书里有但是缺乏实操的情况下很难注意到。

8086汇编语言的输入和输出实在是太简陋了,这个部分踩了不少坑,但是最后还是在各路查询资料以及做小的输入输出试验demo下完成了。

循环控制部分也比较麻烦,但是理清这里面的逻辑关系之后,认真写,写着写着反而也挺顺手了,不过总是要考虑每个寄存器的变量要怎么变化,每个细节都不能漏掉。

总体而言,踩过的坑变成了我吸取的教训,修过的bug变成了我难忘的经历,500多行的汇编代码,着实是一番不小的工程了。

下面是全部的500行代码:

  1 SSEG SEGMENT STACK
  2     STK DB 20 DUP(0)
  3 SSEG ENDS
  4 DSEG SEGMENT
  5     MP DB 225 DUP(0)
  6     I DB 0    ;横坐标
  7     J DB 0    ;纵坐标
  8     TI DB 0;
  9     TJ DB 0;暂用变量
 10     CNTW DW 0;基址偏移的16位量表示
 11     CNT DB 0;基址偏移的8位量表示
 12     FINISH DB 0    ;棋局是否完成
 13     COUNT DB 1    ;连线上的棋子个数
 14     OVERFLOW DB 0    ;棋盘是否满了 下满225次未有人胜利即后宣布和棋
 15     NOW DB 0
 16     STR1 DB 'Game start,either side can win by reaching five pieces on a line.',0DH,0AH,'$'
 17     STR2 DB '    0  1  2  3  4  5  6  7  8  9 10 11 12 13 14',0DH,0AH,'$'
 18     STR3 DB 0DH,0AH,'$'
 19     STR4 DB 'White round, please enter two numbers, separated by spaces,end withs #',0DH,0AH,'$'    ;各个游戏提示语句
 20     STR5 DB 'Black round, please enter two numbers, separated by spaces,end withs #',0DH,0AH,'$'
 21     STR6 DB 'The data is wrong, please re-enter it',0DH,0AH,'$'
 22     STR7 DB 'White win.',0DH,0AH,'$'
 23     STR8 DB 'Black win.',0DH,0AH,'$'
 24     STR9 DB 'Game ends,draw.'
 25 DSEG ENDS
 26 CSEG SEGMENT 
 27     ASSUME CS:CSEG,DS:DSEG
 28     ASSUME SS:SSEG
 29 MAIN PROC
 30 START:    MOV AX,DSEG
 31     MOV DS,AX
 32     MOV AX,SSEG
 33     MOV SS,AX
 34     MOV SP,LENGTH STK
 35     MOV DX,OFFSET STR1
 36     MOV AH,9
 37     INT 21h        ;显示游戏开始
 38 ;这是最开始的外圈循环  白方先手继而黑方
 39 
 40 IN_WHITE:        ;白方落子循环                此处黑白落子逻辑判断代码有大部分相似,但是避免跳转逻辑过于复杂因此黑白落子判断分开执行
 41     CALL SHOW    ;显示棋盘
 42     MOV TI,0
 43     MOV TJ,0    ;多一层防止数据错误
 44     MOV DX,OFFSET STR4    ;白方回合提示
 45     MOV AH,9
 46     INT 21h
 47     MOV AH, 1 ; ah设置成1模式,用于从键盘输入
 48     INT 21H ; 调用系统中断,输入
 49     CMP AL,30H
 50     JB W_NOT
 51     CMP AL,39H
 52     JA W_NOT
 53     JMP NEXTW    ;第一个输入无误
 54 W_NOT:
 55     MOV DL,0DH
 56     MOV AH,2
 57     INT 21H
 58     MOV DL,0AH        ;显示换行
 59     MOV AH,2
 60     INT 21H
 61     MOV DX,OFFSET STR6
 62     MOV AH,9
 63     INT 21h    
 64     JMP IN_WHITE    ;返回白方落子循环处
 65 NEXTW:
 66     SUB AL, 30H    ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
 67     MOV TI,AL    ;暂存这个量
 68     MOV AH, 1 ; ah设置成1模式,用于从键盘输入
 69     INT 21H ; 调用系统中断,输入
 70     CMP AL,20H
 71     JE NEXTWW    ;检测到空格 跳到接受第二个数的位置
 72     CMP AL,30H
 73     JB W_NOT
 74     CMP AL,34H
 75     JA W_NOT
 76     CMP TI,1
 77     JA W_NOT    ;超出14的说明输入错误 否则TI为1
 78     MOV TI,10
 79     SUB AL, 30H    ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
 80     MOV TJ,AL    ;暂存这个量
 81 NEXTWW:
 82     MOV BL,TI
 83     ADD BL,TJ
 84     MOV I,BL    ;保留行数据
 85     MOV TI,0
 86     MOV TJ,0    ;初始化,防止数据遗留出问题
 87 SEC:
 88     MOV AH, 1 ; ah设置成1模式,用于从键盘输入
 89     INT 21H ; 调用系统中断,输入
 90     CMP AL,20H
 91     JE SEC    ;检测到空格 返回一段
 92     CMP AL,30H
 93     JB W_NOT
 94     CMP AL,39H
 95 follow:    JA W_NOT    ;下面接力,超出跳转位置
 96     SUB AL, 30H    ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
 97     MOV TI,AL    ;暂存这个量
 98     MOV AH, 1 ; ah设置成1模式,用于从键盘输入
 99     INT 21H ; 调用系统中断,输入
100     CMP AL,23H    ;#结尾
101     JE ENDWW    ;输入结束
102     CMP AL,30H
103     JB W_NOT
104     CMP AL,34H
105     JA W_NOT
106     CMP TI,1
107     JA follow    ;超出14的说明输入错误 否则TI为1
108     MOV TI,10    ;转化成10便于操作。。因为只有10-14所以十位只有这个
109     SUB AL, 30H    ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
110     MOV TJ,AL    ;暂存这个量
111 ENDWW:
112     MOV DL,0DH
113     MOV AH,2
114     INT 21H
115     MOV DL,0AH        ;显示换行
116     MOV AH,2
117     INT 21H
118     MOV BL,TI
119     ADD BL,TJ
120     MOV J,BL    ;保留列数据
121     MOV TI,0
122     MOV TJ,0    ;初始化,防止数据遗留出问题
123     ;获得相对MP的8位/16位的偏移量
124     MOV AL,I    ;行数*15+列数才是当前元素的相对于MP的偏移量
125     MOV CL,15
126     MUL CL
127     MOV CNTW,AX
128     MOV DL,BYTE PTR CNTW
129     ADD DL,J
130     MOV CNT,DL    ;保留相对于MP的偏移量 为byte
131     MOV AX,WORD PTR CNT
132     MOV CNTW,AX    ;保留相对于MP的偏移量 为Word
133     MOV BX,OFFSET MP
134     ADD BX,CNTW
135     MOV AL,[BX]    ;取到MP当前玩家准备落子的坐标
136     CMP AL,0    ;此处是否没有落子
137     JNE follow    ;不等于0表示此处有落子 该次落子失败,需要重新选择落子处
138     ;成功落子,改变MP的坐标数据
139     ADD OVERFLOW,1    ;落子+1
140     MOV BYTE PTR[BX],27H    ;以W代表白棋  B代表黑棋        因为棋盘输出时每个数字自动加30H转为字符,所以此处的W ASII码先减去30H
141     CALL WIN;调用判断获胜与否的子程序
142     CMP FINISH,1
143     JE EXIT_TEMP        ;获胜条件已达成
144 
145 IN_BLACK:        ;黑方落子循环                
146     CALL SHOW    ;显示棋盘
147     MOV TI,0
148     MOV TJ,0    ;多一层防止数据错误
149     MOV DX,OFFSET STR5    ;黑方回合提示
150     MOV AH,9
151     INT 21h
152     MOV AH, 1 ; ah设置成1模式,用于从键盘输入
153     INT 21H ; 调用系统中断,输入
154     CMP AL,30H
155     JB B_NOT
156     CMP AL,39H
157     JA B_NOT
158     JMP NEXTB    ;第一个输入无误
159 B_NOT:
160     MOV DL,0DH
161     MOV AH,2
162     INT 21H
163     MOV DL,0AH        ;显示换行
164     MOV AH,2
165     INT 21H
166     MOV DX,OFFSET STR6
167     MOV AH,9
168     INT 21h    
169     JMP IN_BLACK    ;返回黑方落子循环处
170 ;中转处
171 EXIT_TEMP:
172     CMP FINISH,1
173     JE EXIT_TEMPP        ;获胜条件已达成
174 NEXTB:
175     SUB AL, 30H    ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
176     MOV TI,AL    ;暂存这个量
177     MOV AH, 1 ; ah设置成1模式,用于从键盘输入
178     INT 21H ; 调用系统中断,输入
179     CMP AL,20H
180     JE NEXTBB    ;检测到空格 跳到接受第二个数的位置
181     CMP AL,30H
182     JB B_NOT
183     CMP AL,34H
184     JA B_NOT
185     CMP TI,1
186     JA B_NOT    ;超出14的说明输入错误 否则TI为1
187     MOV TI,10
188     SUB AL, 30H    ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
189     MOV TJ,AL    ;暂存这个量
190 NEXTBB:
191     MOV BL,TI
192     ADD BL,TJ
193     MOV I,BL    ;保留行数据
194     MOV TI,0
195     MOV TJ,0    ;初始化,防止数据遗留出问题
196 THR:
197     MOV AH, 1 ; ah设置成1模式,用于从键盘输入
198     INT 21H ; 调用系统中断,输入
199     CMP AL,20H
200     JE THR    ;检测到空格 返回一段
201     CMP AL,30H
202     JB B_NOT
203     CMP AL,39H
204 folloB:    JA B_NOT    ;下面接力,超出跳转位置
205     SUB AL, 30H    ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
206     MOV TI,AL    ;暂存这个量
207     MOV AH, 1 ; ah设置成1模式,用于从键盘输入
208     INT 21H ; 调用系统中断,输入
209     CMP AL,23H    ;#结尾
210     JE ENDBB    ;输入结束
211     CMP AL,30H
212     JB B_NOT
213     CMP AL,34H
214     JA B_NOT
215     CMP TI,1
216     JA folloB    ;超出14的说明输入错误 否则TI为1
217     MOV TI,10    ;转化成10便于操作。。因为只有10-14所以十位只有这个
218     SUB AL, 30H    ; 输入的结果默认是放在al的,减去30h是为了把字符转换成数字
219     MOV TJ,AL    ;暂存这个量
220     JMP ENDBB    ;后面安排一个中转处
221 ;中转处
222 EXIT_TEMPP:
223     CMP FINISH,1
224     JE EXIT        ;获胜条件已达成
225 ENDBB:
226     MOV DL,0DH
227     MOV AH,2
228     INT 21H
229     MOV DL,0AH        ;显示换行
230     MOV AH,2
231     INT 21H
232     MOV BL,TI
233     ADD BL,TJ
234     MOV J,BL    ;保留列数据
235     MOV TI,0
236     MOV TJ,0    ;初始化,防止数据遗留出问题
237     ;获得相对MP的8位/16位的偏移量
238     MOV AL,I    ;行数*15+列数才是当前元素的相对于MP的偏移量
239     MOV CL,15
240     MUL CL
241     MOV CNTW,AX
242     MOV DL,BYTE PTR CNTW
243     ADD DL,J
244     MOV CNT,DL    ;保留相对于MP的偏移量 为byte
245     MOV AX,WORD PTR CNT
246     MOV CNTW,AX    ;保留相对于MP的偏移量 为Word
247     MOV BX,OFFSET MP
248     ADD BX,CNTW
249     MOV AL,[BX]    ;取到MP当前玩家准备落子的坐标
250     CMP AL,0    ;此处是否没有落子
251     JNE folloB    ;不等于0表示此处有落子 该次落子失败,需要重新选择落子处
252     ;成功落子,改变MP的坐标数据
253     ADD OVERFLOW,1    ;落子+1
254     MOV BYTE PTR[BX],12H    ;以W代表白棋  B代表黑棋        因为棋盘输出时每个数字自动加30H转为字符,所以此处的W ASII码先减去30H
255     CALL WIN;调用判断获胜与否的子程序
256     CMP OVERFLOW,225
257     JE OVER_FLOW    ;棋盘满了
258     CMP FINISH,1
259     JE EXIT        ;获胜条件已达成
260     JMP NEAR PTR IN_WHITE        ;游戏没有达到结束条件,继续
261 EXIT:    ;棋局结束
262     CALL SHOW
263     MOV AL,NOW            ;NOW是获胜时落子的一方
264     CMP AL,27H
265     JE WHITE_WIN
266     CMP AL,12H
267     JE BLACK_WIN
268 OVER_FLOW:
269     CMP FINISH,1    ;最后一子导致五子成线了,由于白子先行必定是白棋落的最后一子,所以此时会白棋胜
270     JE WHITE_WIN
271     MOV DX,OFFSET STR9
272     MOV AH,9
273     INT 21h        ;棋盘满了没人获胜,和棋
274     JMP GAME_OVER
275 WHITE_WIN:
276     MOV DX,OFFSET STR7
277     MOV AH,9
278     INT 21h        ;白棋获胜
279     JMP GAME_OVER
280 BLACK_WIN:
281     MOV DX,OFFSET STR8
282     MOV AH,9
283     INT 21h        ;黑棋获胜
284     JMP GAME_OVER
285 GAME_OVER:
286     MOV AX,4C00H;退出
287     INT 21H
288 MAIN ENDP
289 
290 WIN PROC NEAR    ;获胜逻辑判断子程序
291     PUSH AX 
292     PUSH BX
293     PUSH CX
294     PUSH DX;保存现场
295     MOV COUNT,1
296     MOV AL,[BX]        ;当前是哪一方落子        这个NOW是W/B的ASII码减去30H的值
297     MOV NOW,AL
298     MOV CL,J;    从左到右的判断 根据J当前值确定是否退出循环
299 LEFT_TO_RIGHT_LEFT:        ;从左到右时count不刷新,进入新的一条线五子判断时才需要
300     DEC CL         ;往左一个元素
301     CMP CL,0    ;小于0则跳入右判断
302     JB LEFT_TO_RIGHT_RIGHT
303     SUB BX,1        ;BX地址减1
304     CMP AL,[BX]
305     JNE LEFT_TO_RIGHT_RIGHT    ;与落子的棋子不同
306     ADD COUNT,1        ;若相同,一条线上的棋子数+1
307     CMP COUNT,5        ;若COUNT达到5,说明有一方已经胜利了
308     JE FIVE_TO_WINN    ;此处条件跳转可能超出 需要找个中继继续跳转
309     JMP LEFT_TO_RIGHT_LEFT    ;循环继续
310 LEFT_TO_RIGHT_RIGHT:    
311     MOV CL,J    ;需要初始化一些数据
312     MOV BX,OFFSET MP    ;重新定位到落子坐标
313     ADD BX,CNTW
314 START_1:        
315     INC CL        ;往右一个元素
316     CMP CL,14    ;大于14则跳入下一个判断
317     JA UP_TIL_DOWN_UP
318     ADD BX,1
319     CMP AL,[BX]
320     JNE UP_TIL_DOWN_UP    ;不等时跳入下一个判断区间
321     ADD COUNT,1        ;若相同,一条线上的棋子数+1
322     CMP COUNT,5        ;若COUNT达到5,说明有一方已经胜利了
323     JE FIVE_TO_WINN
324     JMP START_1        ;循环继续
325 UP_TIL_DOWN_UP:
326     MOV CL,I    ;需要初始化一些数据
327     MOV BX,OFFSET MP    ;重新定位到落子坐标
328     ADD BX,CNTW
329     MOV COUNT,1        ;count初始化
330 START_2:
331     DEC CL         ;往上一个元素
332     CMP CL,0    ;小于0则跳入下判断
333     JB UP_TIL_DOWN_DOWN
334     SUB BX,15        ;BX地址减15
335     CMP AL,[BX]
336     JNE UP_TIL_DOWN_DOWN    ;与落子的棋子不同
337     ADD COUNT,1        ;若相同,一条线上的棋子数+1
338     CMP COUNT,5        ;若COUNT达到5,说明有一方已经胜利了
339 FIVE_TO_WINN:    JE FIVE_TO_WIN
340     JMP START_2    ;循环继续
341 UP_TIL_DOWN_DOWN:
342     MOV CL,I    ;需要初始化一些数据
343     MOV BX,OFFSET MP    ;重新定位到落子坐标
344     ADD BX,CNTW
345 START_3:
346     INC CL         ;往下一个元素
347     CMP CL,14    ;大于14则跳入下判断
348     JA LEFTUP_TO_RIGHTDOWN_LEFTUP
349     ADD BX,15        ;BX地址加15
350     CMP AL,[BX]
351     JNE LEFTUP_TO_RIGHTDOWN_LEFTUP    ;与落子的棋子不同
352     ADD COUNT,1        ;若相同,一条线上的棋子数+1
353     CMP COUNT,5        ;若COUNT达到5,说明有一方已经胜利了
354     JE FIVE_TO_WIN
355     JMP START_3    ;循环继续
356 LEFTUP_TO_RIGHTDOWN_LEFTUP:        ;左上到右下 左上部分
357     MOV CL,I    ;需要初始化一些数据    这是行 数据
358     MOV DL,J    ;这是列 数据
359     MOV BX,OFFSET MP    ;重新定位到落子坐标
360     ADD BX,CNTW
361     MOV COUNT,1        ;count初始化
362 START_4:
363     DEC CL         ;往左上一个元素
364     DEC DL
365     CMP CL,0    ;小于0则跳入下判断
366     JB LEFTUP_TO_RIGHTDOWN_RIGHTDOWN
367     CMP DL,0
368     JB LEFTUP_TO_RIGHTDOWN_RIGHTDOWN
369     SUB BX,16        ;BX地址减16
370     CMP AL,[BX]
371     JNE LEFTUP_TO_RIGHTDOWN_RIGHTDOWN    ;与落子的棋子不同
372     ADD COUNT,1        ;若相同,一条线上的棋子数+1
373     CMP COUNT,5        ;若COUNT达到5,说明有一方已经胜利了
374 FIVE_TO_WIN:
375     JE FIVE_WINNN
376     JMP START_4    ;循环继续
377 LEFTUP_TO_RIGHTDOWN_RIGHTDOWN:    ;右下部分
378     MOV CL,I    ;需要初始化一些数据    这是行 数据
379     MOV DL,J    ;这是列 数据
380     MOV BX,OFFSET MP    ;重新定位到落子坐标
381     ADD BX,CNTW
382 START_5:        
383     INC CL        ;往右下一个元素
384     INC DL
385     CMP CL,14    ;大于14则跳入下一个判断
386     JA LEFTDOWN_TO_RIGHTUP_LEFTDOWN
387     CMP DL,14
388     JA LEFTDOWN_TO_RIGHTUP_LEFTDOWN
389     ADD BX,16
390     CMP AL,[BX]
391     JNE LEFTDOWN_TO_RIGHTUP_LEFTDOWN    ;不等时跳入下一个判断区间
392     ADD COUNT,1        ;若相同,一条线上的棋子数+1
393     CMP COUNT,5        ;若COUNT达到5,说明有一方已经胜利了
394 FIVE_WINNN:    JE FIVE_WIN
395     JMP START_5        ;循环继续
396 LEFTDOWN_TO_RIGHTUP_LEFTDOWN:        ;左下到右上 左下部分
397     MOV CL,I    ;需要初始化一些数据    这是行 数据
398     MOV DL,J    ;这是列 数据
399     MOV BX,OFFSET MP    ;重新定位到落子坐标
400     ADD BX,CNTW
401     MOV COUNT,1        ;count初始化
402 START_6:
403     INC CL         ;往左下一个元素
404     DEC DL
405     CMP CL,14    ;大于14则跳入下判断
406     JA LEFTDOWN_TO_RIGHTUP_RIGHTUP
407     CMP DL,0
408     JB LEFTDOWN_TO_RIGHTUP_RIGHTUP
409     ADD BX,14        ;BX地址加14
410     CMP AL,[BX]
411     JNE LEFTDOWN_TO_RIGHTUP_RIGHTUP    ;与落子的棋子不同
412     ADD COUNT,1        ;若相同,一条线上的棋子数+1
413     CMP COUNT,5        ;若COUNT达到5,说明有一方已经胜利了
414     JE FIVE_WIN
415     JMP START_6    ;循环继续
416 LEFTDOWN_TO_RIGHTUP_RIGHTUP:    ;右上部分
417     MOV CL,I    ;需要初始化一些数据    这是行 数据
418     MOV DL,J    ;这是列 数据
419     MOV BX,OFFSET MP    ;重新定位到落子坐标
420     ADD BX,CNTW
421 START_7:        
422     DEC CL        ;往右上一个元素
423     INC DL
424     CMP CL,0    ;小于0则结束四个获胜判断
425     JB OK
426     CMP DL,14
427     JA OK
428     SUB BX,14
429     CMP AL,[BX]
430     JNE OK
431     ADD COUNT,1        ;若相同,一条线上的棋子数+1
432     CMP COUNT,5        ;若COUNT达到5,说明有一方已经胜利了
433     JE FIVE_WIN
434     JMP START_7    ;循环继续
435 FIVE_WIN:    ;出现了获胜的棋面 结束游戏并显示胜利者
436     MOV FINISH,1    ;用这个变量判断胜利
437 OK:
438     MOV COUNT,1
439     POP DX        ;结束胜负判断 恢复现场
440     POP CX
441     POP BX
442     POP AX
443     RET    ;返回原调用程序
444 WIN ENDP
445 
446 SHOW PROC NEAR    ;屏幕显示棋盘子程序
447     PUSH AX 
448     PUSH BX
449     PUSH CX
450     PUSH DX;保存现场    
451     MOV DX,OFFSET STR2
452     MOV AH,9
453     INT 21h
454     MOV BX,OFFSET MP    ;当前是第几个元素
455     MOV CH,0    ;行数
456 NICE:    
457     MOV CL,0    ;列数
458     CMP CH,10    ;行数大于等于10不用加空格前缀
459     JAE UPTEN    
460     MOV AH,02H
461     MOV DL,' '    ;加空格前缀
462     INT 21H
463     MOV AH,02H
464     MOV DL, CH
465     ADD DL,30H
466     INT 21H
467     MOV AH,02H
468     MOV DL, ' '
469     INT 21H
470     MOV AH,02H
471     MOV DL, ' '
472     INT 21H
473     JMP LINE    ;小于10的数进入打印棋盘
474 UPTEN: 
475     MOV AH,02H
476     MOV DL,'1'        ;十位
477     INT 21H
478     MOV AH,02H
479     MOV DL,CH
480     ADD DL,38    ;个位
481     INT 21H
482     MOV AH,02H
483     MOV DL, ' '
484     INT 21H
485     MOV AH,02H
486     MOV DL, ' '        ;字符间隔调整
487     INT 21H
488 LINE:    
489     MOV AH, 02H
490     MOV DL,[BX]
491     ADD DL,30H
492     INT 21H
493     MOV AH,02H
494     MOV DL, ' '
495     INT 21H
496     MOV AH,02H
497     MOV DL, ' '
498     INT 21H
499     ADD BX,1    ;往后一个元素
500     ADD CL,1
501     CMP CL,15
502     JB LINE        ;当前行未到尾部,继续输出
503     MOV DL,0DH
504     MOV AH,2
505     INT 21H
506     MOV DL,0AH        ;显示换行
507     MOV AH,2
508     INT 21H
509     ADD CH,1;显示当前行  自增到下一行
510     CMP CH,15
511     JAE FIN    ;棋盘显示完毕,结束
512     JMP NICE
513 FIN:    
514     POP DX        ;结束棋盘输出 恢复现场
515     POP CX
516     POP BX
517     POP AX
518     RET    ;返回原调用程序
519 SHOW ENDP
520 CSEG ENDS
521 END START        ;终于写完了!!!

 

posted @ 2020-11-15 22:34  夜雨闻铃风声起  阅读(1561)  评论(0)    收藏  举报