21,459
社区成员
发帖
与我相关
我的任务
分享
;*********************************************************************************
; 程 序: snake.exe *
; 源文件: snake.asm *
; 环 境: masmplus 1.2 *
; 编 译: ml /c /coff /Fo"snake.obj" ".\snake.asm" *
; 链 接: link /out:snake.exe /subsystem:windows /merge:.rdata=.text snake.obj *
; 作 者: hiroyukki *
; 日 期: 2011.03.04 *
; 玩 法: P: 暂停和继续 C: 切换颜色模式 上下左右WASD键: 调整蛇头方向 *
; Q或ESC: 退出游戏 N: 开始新游戏 *
; 说 明: 模型如下,因为总共的格子是有限的,所以我采取了静态内存的方法,以 *
; 避免每吃一个食物就申请一次内存的麻烦,在程序初始化时,就把所有空格上 *
; 的结点初始化好了,类似内存池的机制。 *
; 程序共 23 * 17 = 391个格子,故在 data? 域申请 391 个 DWORD。这些 *
; DWORD的4个字节从高到低分别表示一个结点的 x位置 y位置 结点类型(食物还 *
; 是蛇身,绘制时根据此指定不同颜色) 有效标志(即蛇到达的位置的结点才是 *
; 有效的)。 *
; 程序时刻记录着当前头的结点,在每次移动时根据方向不同来获取下一个 *
; 将要到达的位置,如果该位置是墙或蛇身,则撞死,否则把下一个位置上的结 *
; 点标志为有效(同时应该把当前蛇尾标志为无效)。 *
; 当前的所有有效结点都存放在另一个数组中,此数组看做一个结点的链表 *
; 数组的第一个元素是蛇尾,最后一个非0元素是蛇头。每当吃掉一个食物或者 *
; 前进一步,视做换掉了蛇头。但是不一样的是,吃掉食物时只简单地把新结点 *
; 放到数组的第一个0元素,即成为新的蛇头。而前进的情况下,在数组最后加 *
; 入新结点后,要把蛇尾,即数组中第一个结点移除,做法是把数组后面的结点 *
; 依次左移。绘制时其实是使用这个链表里的元素进行绘制,所以要保持链表里 *
; 元素与全局结点列表里的相应元素同步。 *
; 共 23 * 17个结点,第一个结点位置为 (1, 1),最后一个为(23, 17)。 *
; 这些结点在一个一维数组中,则由 (x, y)得到相应结点在一维数组中的索引的 *
; 公式为 (y - 1) * 23 + (x - 1) *
; 为了进一步减小可执行文件体积,合并了只读数据段和代码段,然后使用 *
; upx 压,EXE为 4096 bytes 。 *
;*********************************************************************************
.386
.model flat, stdcall
option casemap:none
;***************************************************************************
; Include
;***************************************************************************
include windows.inc
include gdi32.inc
includelib gdi32.lib
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
includelib Coredll.lib
;***************************************************************************
; 这个结构并未使用,只是大致描述下一个结点的样子,一个节点是一个DWORD *
; 从高位到低位分别是: x坐标 y坐标 类型(食物或蛇身) 有效标志 *
; 每个字段点一个字节 *
;***************************************************************************
Node struct
x BYTE ?
y BYTE ?
ntype BYTE ?
bValid BYTE ?
Node ends
TIMER equ 1
FOOD equ 0
BODY equ 1
RIGHT equ 2
LEFT equ 3
UP equ 4
DOWN equ 5
;***************************************************************************
; 数据段
;***************************************************************************
.data?
hInstance dd ?
hWinMain dd ?
hDc dd ?
isPaused dd ?
isAlive dd ?
bRanColor dd ?
bTurnPending dd ?
direction dd ?
food dd ?
nodes dd 391 dup (?)
nodeList dd 391 dup (?)
curX dd ?
curY dd ?
foodX dd ?
foodY dd ?
score dd ?
rClient RECT <>
wndWidth dd ?
wndHeight dd ?
szScoreBuffer db 32 dup (?)
seed dd ?
speed dd ?
timerCounts dd ?
eatCounts dd ?
.const
szClassName db '1', 0
szCaptionMain db '贪食蛇', 0
szCaptionPause db '已暂停', 0
szCaptionDie db '游戏结束,请按N键开始新游戏', 0
szDown db '下', 0
szUp db '上', 0
szRight db '右', 0
szLeft db '左', 0
szScore db '得分:%d', 0
;***************************************************************************
; 代码段
;***************************************************************************
.code
;***************************************************************************
; 随机数生成
;***************************************************************************
_NextInt proc uses edx ebx max
push edx
invoke GetTickCount
add eax, seed
add seed, eax
xor edx, edx
mov ebx, max
div ebx
xor seed, eax
mov eax, edx
inc eax
pop edx
ret
_NextInt endp
;***************************************************************************
; 初始化所有蛇身结点把前三个置为有效
;***************************************************************************
_InitNodes proc
push edi
push edx
push ebx
push ecx
xor edi, edi
@@:
xor edx, edx
mov eax, edi
mov ebx, 17h
div ebx
; 商是 y 坐标,余数是 x 坐标
xor ecx, ecx
or ecx, edx
inc ecx
shl ecx, 8
or ecx, eax
inc ecx
shl ecx, 8
or ecx, BODY
shl ecx, 8
; ecx 为结点, ebx 为有效结点列表
; nodeList 数组首元素即蛇尾,最后一个不为0的元素为蛇头
xor ebx, ebx
.if edi < 4
or ecx, 1
mov ebx, ecx
.endif
mov [offset nodes + edi * 4], ecx
mov [offset nodeList + edi * 4], ebx
inc edi
.if edi >= 187h
jmp @F
.endif
jmp @B
@@:
pop ecx
pop ebx
pop edx
pop edi
ret
_InitNodes endp
;***************************************************************************
; 节点构造函数,参数分别是X坐标,Y坐标,类型(食物还是身体),有效标志
;***************************************************************************
_CreateNode proc nx, ny, nntype, nInvalid
xor eax, eax
or eax, nx
shl eax, 8
or eax, ny
shl eax, 8
or eax, nntype
shl eax, 8
or eax, nInvalid
ret
_CreateNode endp
;***************************************************************************
; 查看指定结点是否在(x, y)
;***************************************************************************
_IsNodeAt proc node, x, y
push ebx
push ecx
mov eax, node
mov ebx, eax
mov ecx, eax
shr ebx, 24
and ebx, 0ffh
sub ebx, x
shr ecx, 16
and ecx, 0ffh
sub ecx, y
.if ecx == 0 && ebx == 0
xor eax, eax
.endif
pop ecx
pop ebx
ret
_IsNodeAt endp
;***************************************************************************
; 绘制单个节点
;***************************************************************************
_DrawNode proc uses ebx ecx _hDC, hNode
local @stColor: COLORREF
local @hBrush: HBRUSH
local @hOldBrush: HBRUSH
local @stRect: RECT
push ecx
push ebx
mov eax, hNode
; 先取标志状态,如果是无效,直接退出
and eax, 000000ffh
.if !eax
jmp @F
.endif
invoke _IsNodeAt, hNode, curX, curY
mov ecx, eax
mov eax, hNode
and eax, 0000ff00h
shr eax, 8
.if (eax == BODY) && (ecx == 0)
mov @stColor, 00ffffffh
.elseif eax == BODY
.if bRanColor
invoke _NextInt, 255
mov ecx, eax
shl ecx, 8
invoke _NextInt, 255
or ecx, eax
shl ecx, 8
invoke _NextInt, 255
or ecx, eax
mov @stColor, ecx
.else
mov @stColor, 000000ffh
.endif
.else
mov @stColor, 0000ff00h
.endif
invoke CreateSolidBrush, @stColor
mov @hBrush, eax
invoke SelectObject, _hDC, @hBrush
mov @hOldBrush, eax
; 算出左右要画的地方
mov ecx, hNode
and ecx, 0ff000000h
shr ecx, 24
xor eax, eax
mov al, cl
mov bl, 20
mul bl
sub eax, 4
mov @stRect.left, eax
add eax, 18
mov @stRect.right, eax
; 算出上下要画的地方
mov ecx, hNode
and ecx, 00ff0000h
shr ecx, 16
xor eax, eax
mov al, cl
mov bl, 20
mul bl
sub eax, 4
mov @stRect.top, eax
add eax, 18
mov @stRect.bottom, eax
invoke FillRect, _hDC, addr @stRect, NULL
invoke SelectObject, _hDC, @hOldBrush
; 狂他妈漏啊……
invoke DeleteObject, @hBrush
@@:
pop ebx
pop ecx
ret
_DrawNode endp