汇编贪食蛇
来源:互联网 发布:搞笑网络歌曲排行榜 编辑:程序博客网 时间: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
这个中断只能用来检测是否有输入而并不能获取输入的按键
因此还需要另一个中断来获取按键:
mov ah,07hint 21h
注意上面描述中,是等待输入,因此不判断是否有输入而是直接采用这个中断就会造成整个程序停在那里等待输入,因为是个单线程程序
下面是我贪食蛇中代码:
;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+汇编也不错。。。
- 汇编贪食蛇
- 贪食蛇的汇编代码(8086)
- 8086汇编双人贪食蛇游戏
- 贪食蛇
- 贪食蛇
- 贪食蛇
- 贪食蛇
- 贪食蛇
- 贪食蛇
- 贪食蛇
- 贪食蛇
- 贪食蛇游戏代码
- 贪食蛇JS源码
- 3d 贪食蛇
- 贪食蛇源程序
- 贪食蛇程序
- 贪食蛇1
- 贪食蛇 - 自己动手小游戏
- GRE写作必备句型
- 杭电 2005 第几天
- NSString的使用
- 集合ArrayList类的使用和与其他实现类的区别
- 服务的用法
- 汇编贪食蛇
- EasyUI获取DataGrid中某一列的所有值
- http://blog.sina.com.cn/s/blog_877e9c3c0101gpnz.html
- dSYM 文件分析工具
- Andriod图片选择器(单选-头像,多选-朋友圈上传):library+sample
- Linux_centos6.5安装QtCreator
- 平安校园解决方案
- 用Python操作Oracle
- FilterDispatcher过滤器会出现的异常