📄 snoke.asm
字号:
;********************************************************************************
;编译模式="WND"
;********************************************************************************
.386
.model flat, stdcall
option casemap:none
;********************************************************************************
;头文件
;********************************************************************************
include windows.inc
include user32.inc
include kernel32.inc
include gdi32.inc
include comctl32.inc
includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib
includelib comctl32.lib
SNOKENODE struct
x dd ?
y dd ?
pPrev dd ?
pNext dd ?
SNOKENODE ends
;********************************************************************************
;全局变量
;********************************************************************************
IDM_MAINMENU equ 100
IDM_FILE_NEW equ 1000
IDM_FILE_CLOSE equ 1001
IDM_FILE_STOP equ 1002
IDM_FILE_EXIT equ 1003
IDM_SPEED_SLOWEST equ 1010
IDM_SPEED_SLOW equ 1011
IDM_SPEED_NORMAL equ 1012
IDM_SPEED_FAST equ 1013
IDM_SPEED_FASTEST equ 1014
IDM_SPEED_USER equ 1015
IDM_FOOD_ONE equ 1016
IDM_FOOD_TWO equ 1017
IDM_FOOD_FOUR equ 1018
IDM_FOOD_16 equ 1019
IDM_FOOD_32 equ 1020
IDM_FOOD_USER equ 1021
IDM_OPTION_ENVIRONMENT equ 1022
IDM_HELP_ABOUT equ 1023
IDD_INPUT equ 200
IDC_INPUT_OK equ 2000
IDC_INPUT_CANCEL equ 2001
IDC_INPUT_EDT1 equ 2002
IDC_INPUT_STC1 equ 2003
IDI_ICON1 equ 100
WNDCX equ 400
WNDCY equ 400
SNKSIZE equ 8
INITCOUNT equ 10
ID_TIMER1 equ 1000
IS_SPEED equ 0
IS_FOOD equ 1
.const
.data
szClassName db "STANDARDCLASS", 0
szWindowsName db "贪食蛇", 0
szPause db "贪食蛇 - 暂停中", 0
szGameOver db "游戏结束 (GAME OVER)", 0
szPlaying db "游戏进行中, 请先结束游戏", 0
szSpeed db "输入数度值(1~1000)毫秒/格, 1秒=1000毫秒", 0
szFood db "输入食物值(1~256)个", 0
bPause db 0
pHead dd 0
pEnd dd 0
dwTimeStop dd 100
dwFoodCount dd 4
dwKeyPrev dd VK_DOWN
dwKeyFlag dd VK_DOWN
.data?
hInstance dd ?
hWndMain dd ?
hMenu dd ?
hBrushSnoke dd ?
hBrushFood dd ?
dwCurrentCount dd ?
nrandom_seed dd ?
stFood POINT 256 dup(<>)
;********************************************************************************
.code
nrandom proc uses ebx edx base
mov eax, nrandom_seed
xor edx, edx
mov ebx, 127773
div ebx
mov ebx, eax
mov eax, 16807
mul edx
mov edx, ebx
mov ebx, eax
mov eax, 2836
mul edx
sub ebx, eax
xor edx, edx
mov eax, ebx
mov nrandom_seed, ebx
div base
mov eax, edx
ret
nrandom endp
nseed proc TheSeed
mov eax, TheSeed
mov nrandom_seed, eax
ret
nseed endp
;********************************************************************************
;释放所有节点
;********************************************************************************
_FreeNodes proc uses esi
mov eax, pHead
assume eax:ptr SNOKENODE
.while eax
mov esi, [eax].pNext
invoke GlobalFree, eax
mov eax, esi
.endw
mov pHead, NULL
mov pEnd, NULL
mov dwKeyFlag, VK_DOWN
ret
_FreeNodes endp
;********************************************************************************
;添加节点
;********************************************************************************
_AddHead proc uses esi @dwX, @dwY
invoke GlobalAlloc, GMEM_FIXED or GMEM_ZEROINIT, sizeof SNOKENODE
mov esi, eax
assume esi:ptr SNOKENODE
assume eax:ptr SNOKENODE
.if pHead
xchg eax, pHead
mov [esi].pNext, eax
mov [eax].pPrev, esi
.else
mov pHead, eax
mov pEnd, eax
.endif
mov eax, @dwX
mov [esi].x, eax
mov eax, @dwY
mov [esi].y, eax
ret
_AddHead endp
;********************************************************************************
;产生食物
;********************************************************************************
_NewFoods proc uses esi @dwCount
lea esi, stFood
assume esi:ptr POINT
mov ecx, @dwCount
mov dwCurrentCount, ecx
@@:
invoke nrandom, WNDCX/SNKSIZE
shl eax, 3
add eax, SNKSIZE
mov [esi].x, eax
invoke nrandom, WNDCY/SNKSIZE
shl eax, 3
add eax, SNKSIZE
mov [esi].y, eax
add esi, 8
loop @B
ret
_NewFoods endp
;********************************************************************************
;游戏结束
;********************************************************************************
_GameOver proc
invoke KillTimer, hWndMain, ID_TIMER1
invoke _FreeNodes
invoke MessageBox, hWndMain, addr szGameOver, addr szWindowsName, MB_OK
invoke InvalidateRect, hWndMain, NULL, TRUE
invoke CheckMenuItem, hMenu, IDM_FILE_STOP, MF_UNCHECKED
invoke SetWindowText, hWndMain, addr szWindowsName
mov bPause, 0
ret
_GameOver endp
;********************************************************************************
;定时器消息处理函数
;********************************************************************************
_TimerProc proc uses ebx ecx esi hWnd, uMsg, idEvent, dwTime
local @stRectHead:RECT, @stRectEnd:RECT
local @hrgn
;取得头节点数据
mov esi, pHead
assume esi:ptr SNOKENODE
mov eax, [esi].x
mov @stRectHead.left, eax
mov eax, [esi].y
mov @stRectHead.top, eax
;由键盘按键记录, 设置新节点数据
mov eax, dwKeyFlag
.if eax == VK_LEFT
sub @stRectHead.left, SNKSIZE
.elseif eax == VK_RIGHT
add @stRectHead.left, SNKSIZE
.elseif eax == VK_UP
sub @stRectHead.top, SNKSIZE
.elseif eax == VK_DOWN
add @stRectHead.top, SNKSIZE
.endif
mov dwKeyPrev, eax
;判断横坐标
mov eax, @stRectHead.left
.if eax < SNKSIZE || eax > WNDCX
invoke _GameOver
ret
.endif
;设置新值
mov @stRectHead.right, eax
add @stRectHead.right, SNKSIZE-1
;取得尾节点数据
mov esi, pEnd
xchg [esi].x, eax
mov @stRectEnd.left, eax
add eax, SNKSIZE-1
mov @stRectEnd.right, eax
;判断纵坐标
mov eax, @stRectHead.top
.if eax < SNKSIZE || eax > WNDCY
invoke _GameOver
ret
.endif
;设置新值
mov @stRectHead.bottom, eax
add @stRectHead.bottom, SNKSIZE-1
;取尾节点数据
xchg [esi].y, eax
mov @stRectEnd.top, eax
add eax, SNKSIZE-1
mov @stRectEnd.bottom, eax
;设置节点链路, [倒二].pNext=NULL, [倒一].pNext=第一, [倒一].pPrev=NULL, [第一].pPrev=倒一
mov esi, [esi].pPrev
mov [esi].pNext, NULL
xchg esi, pEnd
mov eax, pHead
mov [esi].pNext, eax
mov [esi].pPrev, NULL
xchg esi, eax
mov [esi].pPrev, eax
mov pHead, eax
;判断是否撞倒自身
mov eax, @stRectHead.left
mov ebx, @stRectHead.top
.while esi
.if eax == [esi].x && ebx == [esi].y
invoke _GameOver
ret
.endif
mov esi, [esi].pNext
.endw
;判断是否吃到食物
lea esi, stFood
assume esi:ptr POINT
sub esi, 8
mov ecx, dwFoodCount
LOP1:
add esi, 8
cmp eax, [esi].x
je LOP2
loop LOP1
jmp LOP3
LOP2:
cmp ebx, [esi].y
je LOP3
loop LOP1
LOP3:
;ecx != 0, 则吃到食物, 食物位置标记清 0, 蛇长度 +1, 食物数量 -1
.if ecx
mov [esi].x, 0
mov [esi].y, 0
invoke _AddHead, @stRectHead.left, @stRectHead.top
dec dwCurrentCount
.endif
;食物数量为 0, 重新产生 dwFoodCount 个食物, 否则, 绘制蛇下一形态
.if !dwCurrentCount
invoke _NewFoods, dwFoodCount
invoke InvalidateRect, hWndMain, NULL, TRUE
.else
invoke CreateRectRgnIndirect, addr @stRectHead
mov @hrgn, eax
invoke CreateRectRgnIndirect, addr @stRectEnd
push eax
invoke CombineRgn, @hrgn, @hrgn, eax, RGN_OR
pop eax
invoke DeleteObject, eax
invoke InvalidateRgn, hWndMain, @hrgn, TRUE
invoke DeleteObject, @hrgn
.endif
ret
_TimerProc endp
;********************************************************************************
;对话框消息处理函数
;********************************************************************************
_InputDlgProc proc hWnd, uMsg, wParam, lParam
.if uMsg == WM_COMMAND
mov eax, wParam
.if ax == IDC_INPUT_OK
invoke GetDlgItemInt, hWnd, IDC_INPUT_EDT1, NULL, FALSE
invoke EndDialog, hWnd, eax
.elseif ax == IDC_INPUT_CANCEL
invoke EndDialog, hWnd, NULL
.endif
.elseif uMsg == WM_CLOSE
invoke EndDialog, hWnd, NULL
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -