汇编贪食蛇

来源:互联网 发布:搞笑网络歌曲排行榜 编辑:程序博客网 时间:2024/06/13 21:54

前言:

随便说说:最近闲得蛋疼,然后之前《汇编语言》看了一遍,没怎样具体写过东西,就想着来用汇编写个贪食蛇吧(话说大一就想写过,想不到是到了大二才完成,还是用汇编写的。。。)
代码可以点这里下载
P.S.由于CPU差异,初始速度可能有差异,可以通过调节速度(开始页面按a)
程序:贪食蛇
环境:masm+dosbox+windows(有doxbox基本win的系统都一样)
功能:(或者说要实现的东西)
1.控制蛇方向
2.蛇撞墙或者撞到自己身体会死
3.蛇吃到食物会长一个身体
4.食物随机出现,但不能出现在蛇的身体中或者墙里面
5.开始游戏前可以调节速度
6.游戏结束时提示分数以及速度
P.S.最后成品有个bug,就是食物随机出现那里(下面说)
下面来根据上面6点逐个解释:

分析:

1.控制蛇方向

先把其分割成几个步骤:
1.检测用户是否按键:有->3 没有->2
2.保持原方向
3.获取盖按键,并判断该按键是否合法(合法按键:w/s/a/d)合法->5 不合法->4
4.保持原方向
5.修改方向(注意:只是修改方向,蛇身体还没动)

检测用户是否按键:采用中断

mov ax,0b00hint 21h

21-0b
这个中断只能用来检测是否有输入而并不能获取输入的按键
因此还需要另一个中断来获取按键:

mov ah,07hint 21h

21-07
注意上面描述中,是等待输入,因此不判断是否有输入而是直接采用这个中断就会造成整个程序停在那里等待输入,因为是个单线程程序
下面是我贪食蛇中代码:

;if input the key,then return the key value;al:    the key value;ah:    ff:true 0:falsecheck_get_input proc    mov ax,0b00h    int 21h    mov ah,0h    cmp al,0ffh    jne finish_cgi    mov ah,07h    int 21h    mov ah,0ffhfinish_cgi:    ret check_get_input endp

还有个关键就是方向的修改,这个算整个程序核心。。。

2、3

把这2个放在一起讲,是因为它们的关键都一样(就是修改方向的那个)

首先,控制蛇方向、蛇撞墙、蛇长身体这些的前提,必须是分清除哪些是蛇身体,哪些是墙,哪些是空白地方,给用户分辨就是很简单的颜色不同来区分,但程序如何区分?

由于我这是通过显示缓冲区来显示,而且修改后不能从内存缓冲区获取原来的信息,因此必须用另外的方法来记录,我一开始想到的是用链表来记录,不过用汇编来实现一个链表好像也挺大过程的,那就算了,就用最普通的建一个图(map)来记录各种信息:

map db 2000 dup('0')

因为是25*80所以就是2000
P.S.这个空间可以缩小,因为一共就3种情况(食物我没有记录)所以用2bit就可以,但是这个后面的比较又比较麻烦所以算了。。。

.1地址转换

采用map的话,还需要一个实际图和map之间的转化(由于实际图,也就是显示缓冲区中,是用2byte来表示一格,而map使用1byte来表示),又由于8086的地址格式是:
segment:[offset]
最后地址=segment*16+offset

因此要先把地址组合起来,再减去0b800h(显示缓冲区起始地址),获得偏移值再除以2,获得最终的偏移值,最后加上map的起始地址就完成了地址的转换

;convert the true address(0b800h:0) to the map-address;temp_seg:  source segment;temp_off:  source offset;ax:    result(offset of the <ds>)convert_address proc    push cx    push temp_seg    push temp_off    sub temp_seg,0b800h ;temp_seg = (temp_seg sub 0b800h) div 2 convert temp_seg    mov ax,temp_seg    mov cx,4    call sal_n          add ax,temp_off    shr ax,1        ;div 2    add ax,offset map    pop temp_off    pop temp_seg    pop cx    retconvert_address endp

P.S.上面的sal_n是把ax算术左移[cx]位

通过地址转换把map与实际图连接起来,这时程序就可以轻松分辨出蛇身体、墙壁和空白处,这时判断是否撞墙(身体)只需要判断蛇头是否在墙壁(身体)处便可,为此蛇头地址(蛇尾地址)也必须记录下来

seg_snake_head dw 0     ;store the segment of the head of the snakeoff_snake_head dw 0     ;store the offset of the head of the snakeseg_snake_tail dw 0     ;store the segment of the tail of the snakeoff_snake_tail dw 0     ;store the offset of the tail of the snake

.2蛇身体

蛇身体和墙不同(map中存储的除了标识符的作用,应该有更多作用),就是蛇身体是一个接一个的(这就是为什么一开始就是想到链表),我们又来分析一下蛇移动的过程:
P.S.这边图形的显示都是通过修改显示缓冲区,所以不用刷新,而是直接改变显示的颜色
1.蛇头按照方向往前走一步(新的一格显色),修改蛇头地址
2.去掉蛇尾(蛇尾去色),修改蛇尾地址

这时问题来了:
1.蛇头方向怎么确定?
2.怎么修改蛇尾地址?也就是怎么找到蛇尾的相连的身体?

.21蛇头

对了,相信大家也是马上想出来了,蛇头的map中存储的就是蛇前进的方向,而修改方向也就是修改相应map位置中的内容便可

;-------------------funtion------------head_toward--------------------;will change <seg_snake_head> and <off_snake_head>;ah:    new direction;al:    direction before changehead_toward proc    push ax    push bx    push cx    ;push dx    push es    push si    push di    mov ax,seg_snake_head    mov temp_seg,ax    mov ax,off_snake_head    mov temp_off,ax    call match_wsad         ;temp_seg    mov bx,ax           ;bx  store the result(cannot be changed)    mov ax,temp_seg    mov seg_snake_head,ax    mov ax,temp_off    mov off_snake_head,ax    mov ah,0    mov ds:[0],ah    mov ah,')'    mov ds:[1],ah    mov es,seg_snake_head    mov si,off_snake_head    mov di,0    mov ah,01110000b    call paint    call is_body    cmp ah,0h    jne lose    mov ax,seg_snake_head    mov temp_seg,ax    mov ax,off_snake_head    mov temp_off,ax    mov ah,bh    call set_map        ;set the mapfinish_ht:    pop di    pop si    pop es    ;pop dx    pop cx    pop bx    pop ax    rethead_toward endp;------------------------------------------------------------

上面的set_map就是修改map中的内容

.22蛇身体(除了蛇头)

蛇头是特殊的,当它不再是蛇头的时候,它便承担了另一个任务,由于蛇的前进,该格迟早会变成蛇尾(它指的是图中的一格),所以它身上必须存储可以帮助找到与其连接的身体的信息,对了,就是存储与它连接的身体的那格的方向(w/s/a/d)

;--------------------funtion---------delete_tail------------------delete_tail proc    push ax    push bx    push cx    push es    push si    push di    mov ax,seg_snake_tail    mov temp_seg,ax    mov ax,off_snake_tail    mov temp_off,ax    call match_wsad    mov bx,ax           ;bx  store the result(cannot be changed)    push temp_seg           ;push    push temp_off           ;push    mov ah,0    mov ds:[0],ah    mov ah,')'    mov ds:[1],ah    mov es,seg_snake_tail    mov si,off_snake_tail    mov di,0    mov ah,0h    call paint    mov ax,seg_snake_tail    mov temp_seg,ax    mov ax,off_snake_tail    mov temp_off,ax    mov ah,'0'    call set_map    pop off_snake_tail      ;pop    pop seg_snake_tail      ;pop    pop di    pop si    pop es    pop bx    pop cx    pop ax    retdelete_tail endp;---------------------------------------------------------------------

有这两个,蛇的移动和吃食物长身体就很容易了
一个就是需要删尾巴,一个不用删尾巴。。。

4.食物随机出现,但不能出现在蛇的身体中或者墙里面

4.1产生食物

1.这个我是把 系统时间秒数 * 4 来产生随机数,这样产生的随机数其实并不随机,随机效果很差,因为秒数是递增的,导致产生的随机数也是递增的,表现在游戏中就是食物一层一层往下出现
2.而每次产生一个随机数后,还需要判断该地址是否合法

以上个原因共同导致了一个问题(bug):
当食物被随机在底墙(顶墙)时,不合法会再次产生食物,但由于随机数的问题,食物又会被产生在同一行(也就是依然在底墙或顶墙之中),这时意味着程序会不断产生食物然后判断,导致整个程序卡在这里直至最后食物成功诞生。
在游戏中的表现就是,蛇吃了食物后突然停在那里,然后又突然动起来。。。通常出现在底层吃了食物之后,而且这个现象应该会随着蛇越来越长而出现的频率越来越高,算一个比较大的问题……

create_food proc    push ax    push cx    push dxcf_l:   mov ah,2ch          ;get the system time    int 21h             mov ax,0            ;create the random address    mov al,dh       mov cx,2    call sal_n    add ax,0b800h    mov temp_seg,ax    mov temp_off,4    call is_wall    cmp ah,0ffh    call is_body    cmp ah,0ffh    je cf_l    mov ax,temp_seg    mov seg_food,ax    mov ax,temp_off    mov off_food,ax    mov ah,'*'    mov ds:[0],ah    mov ah,')'    mov ds:[1],ah    mov ax,temp_seg    mov es,ax    mov ax,temp_off    mov si,ax    mov di,0    mov ah,00000111b    call paint    pop dx    pop cx    pop ax    retcreate_food endp

4.2吃食物

上面也提过食物并没有在map中标记,而是记录食物的地址,再来与蛇头相比较看是否一致,来判断是否吃食物,而且判断必须是把segment和offset合在一起再进行判断

;temp_seg:  segment of the head of snake;temp_off:  offset  of the head of snake;ah:    0ffh:   true    0:falseis_food proc    push bx    push cx    mov ax,temp_seg    mov cx,4    call sal_n    add ax,temp_off    mov bx,ax    mov ax,seg_food    mov cx,4    call sal_n    add ax,off_food    cmp ax,bx    mov ah,0ffh    je finish_if    mov ah,0hfinish_if:    pop cx    pop bx    retis_food endp

5.开始游戏前可以调节速度

其实,在每次蛇移动了一格之后,并不是马上开始下一次移动,因为计算机的运行速度远超人类,所以每次移动后都用空循环来延迟,这个也就是调节速度关键:循环的次数

;time is greater,speed is littlerdelay proc    push cx    mov cx,1000dl1:    push cx    mov cx,timedl2:    push cx    mov cx,10dl3:    loop dl3    pop cx    loop dl2    pop cx    loop dl1    pop cx    retdelay endp

上面的代码,只要通过修改time的值便可以调节速度

以上就是比较大的几个部分

还有个问题就是地图导致的问题:就是它这个显示屏是25*80,也就是竖列也就25个格,但是和80格横行相比,没差多少,也就是它的格不是正方形,而且高比宽大,导致上下走的时候,会感觉速度快很多。。。

下面来几张具体截图吧:
我的是win7,需要用dosbox,不知道怎么用dosbox可以看这里
在dosbox转到程序目录就可以了
开始页面:
开始
调节速度:
调节速度
玩:
玩
结束:
结束

总结:

这次代码最后是1000行左右,感觉有几点注意:
1.push和pop必须一一对应好,特别在函数中的push、pop,而且用于暂时存储的push、pop最好写上注释提高注意
2.一定要写注释
3.函数,最好把不是用于传递结果的寄存器都用push、pop保存好
4.有时用内存传递变量可以简化代码
5.函数要明确作用

最后,代码写得也不怎样,不过感觉汇编挺好玩的。。。不过也好麻烦,下次写类似的小程序的时候,感觉用c+汇编也不错。。。

0 1
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 香炉里面放的小米生虫子了怎么办 肉肉上长满了白色的小虫子怎么办 多肉上面有白色的虫子怎么办 多肉植物根部长白色虫子怎么办 朋友玩期货把我钱赔了怎么办 宝宝床上虫子咬了肿大包怎么办 喷药的杀虫剂的喷头坏了怎么办 宅e经营贷个人没有公司怎么办 浏览器下载完插件安装不好用怎么办 要求评审专家复核他不来怎么办 微博国际版不能用微博号登录怎么办 云南省特岗登录名忘了怎么办 人行登录名忘了再怎么办 生源地贷款登录名忘了怎么办 举报19楼帖子但是不给删除怎么办 新浪微博手机号被注册了怎么办 忘记新浪微博绑定的邮箱账号怎么办 申诉找回微信账号密码失败怎么办 注册微信号时验证码错误怎么办 老板让写的报道没有当天写完怎么办 洛奇英雄传镶嵌有微章的时装怎么办 上午12点用24小时制怎么办 三分钟看懂捷信个人贷款怎么办 精神不正常的父亲到单位闹怎么办 母亲和父亲一直和我闹怎么办 儿子拿了父亲的钱买手机怎么办 电动车骑的慢的时候车头打漂怎么办 二晓啊相公太爱我了怎么办全文免费 车牌被套牌了又有违章怎么办 高中生只学好主课副科学不好怎么办 母泰迪怀孕23天了不吃饭怎么办 媳妇一再触碰我的底线怎么办 发票商品编码好多选错了分类怎么办 吃了用福尔马林泡的食物怎么办 没大没小说话不尊重人的孩子怎么办 升级安卓8.0后app闪退怎么办 业主装门占用消防通道物业怎么办? 想改名字派出所不给改怎么办 物业不给地热打压影响装修怎么办 pos机pin效码验证错误怎么办 苹果下载东西要发验证码怎么办