贪食蛇男的贪食蛇

时间:2021-08-18 10:33:53
WIN32汇编的贪食蛇,体积可想而知。好像代码有点长,CSDN不支持,分几段吧。 贪食蛇男的贪食蛇

;*********************************************************************************
; 程  序: 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

192 个解决方案

#1



;***************************************************************************
; 画蛇身,其实就是遍历所有结点画,绘画函数会自己根据结点的状态来选择绘
; 制方式。
;***************************************************************************
_DrawBody  proc  uses ecx edi _hDC
    push edi
    push ecx
    push eax
    xor edi, edi
    mov ecx, 187h
@@:
    mov eax, [offset nodeList + edi * 4]
    .if !eax
        jmp @F
    .endif
    invoke _DrawNode, _hDC, eax
    inc edi
    loop @B

@@:
    pop eax
    pop ecx
    pop edi
    ret
_DrawBody  endp

;***************************************************************************
; 绘制分数
;***************************************************************************
_DrawScore  proc  _hDC
    local @oldColor: COLORREF
    local @oldBkMode: DWORD
    local @szBuffer[128]: BYTE
    local @hFont: HFONT
    local @hOldFont: HFONT
    local @hPen: HPEN
    local @hOldPen: HPEN
    local @strLen: DWORD

    push ebx
    invoke wsprintf, offset szScoreBuffer, offset szScore, score
    ; 求字符串长度
    xor eax, eax
@@:
    mov bl, BYTE ptr [offset szScoreBuffer + eax]
    .if bl != 0
        inc eax
        jmp @B
    .endif
    mov @strLen, eax

    invoke SetBkMode, _hDC, TRANSPARENT
    mov @oldBkMode, eax
    invoke SetTextColor, _hDC, 0000ffffh
    mov @oldColor, eax

    invoke CreateFont, 14, 7, 0, 0, 8, FALSE, FALSE, FALSE, GB2312_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, 5, DEFAULT_PITCH, NULL
    mov @hFont, eax
    invoke SelectObject, _hDC, eax
    mov @hOldFont, eax
    invoke CreatePen, PS_SOLID, 1, 00ffffffh
    mov @hPen, eax
    invoke SelectObject, _hDC, eax
    mov @hOldPen, eax

    invoke TextOut, _hDC, 410, 15, offset szScoreBuffer, @strLen
    invoke MoveToEx, _hDC, 10, 10, 0
    invoke LineTo, _hDC, 480, 10
    invoke LineTo, _hDC, 480, 360
    invoke LineTo, _hDC, 10, 360
    invoke LineTo, _hDC, 10, 10

    invoke SetBkMode, _hDC, @oldBkMode
    invoke SetTextColor, _hDC, @oldColor
    invoke SelectObject, _hDC, @hOldFont
    invoke SelectObject, _hDC, @hOldPen
    invoke DeleteObject, @hFont
    invoke DeleteObject, @hPen

    pop ebx
    ret
_DrawScore  endp

;***************************************************************************
; 获得指定位置上的结点
;***************************************************************************
_GetNodeAt  proc  xLocation, yLocation
    push ebx
    mov eax, yLocation
    dec eax
    mov ebx, 17h
    mul ebx
    add eax, xLocation
    dec eax
    mov eax, [offset nodes + eax * 4]
    pop ebx
    ret
_GetNodeAt  endp

;***************************************************************************
; 由传入的X Y 位置查询目标结点的状态(是否激活)
;***************************************************************************
_QueryNodeStat  proc  xLocation, yLocation
    invoke _GetNodeAt, xLocation, yLocation
    and eax, 000000ffh
    ret
_QueryNodeStat  endp

;***************************************************************************
; 激活或压制一个已处于激活状态的结点
; 参数分别是 x 坐标, y坐标, 是否激活的布尔变量
;***************************************************************************
_Activate  proc  xLocation, yLocation, bActivate
    push ebx
    push edi
    mov eax, yLocation
    dec eax
    mov ebx, 17h
    mul ebx
    add eax, xLocation
    dec eax
    mov edi, eax

    mov eax, [offset nodes + eax * 4]
    .if bActivate
        or eax, 0000001h
    .else
        and eax, 0ffffff00h
    .endif
    mov [offset nodes + edi * 4], eax

    pop edi
    pop ebx
    ret
_Activate  endp

;***************************************************************************
; 下一个食物,总是随机出现。并且不会出现在蛇身上
; 这种实现似乎比较2,更好的方法应该是把所有结点串成两个链表,蛇身链表和非
; 蛇身链表。每次随机地从非蛇身链表里取出结点插入蛇身链表中,这样就不会造成
; 随机生成的位置在蛇身上从而重新生成了。
; 重新生巢呗陨在蛇身将要填充满整个区域时性能急剧下降。
;***************************************************************************
_NextFood    proc uses ebx
    invoke _NextInt, 17
    mov foodY, eax
    invoke _NextInt, 23
    mov foodX, eax
    invoke _QueryNodeStat, foodX, foodY
    .if eax
        invoke _NextFood
    .endif
    invoke _CreateNode, foodX, foodY, FOOD, 1
    mov food, eax
    ret
_NextFood    endp

;***************************************************************************
; 往队列头加一个结点,刚吃了一顿或走了一步后都会调用此函数。
; 第一个参数是要加入的结点,第二个是指示是否移除队列尾。正常移动时需要
; 在队列头加结点的同时移除队列尾,以达到好像在移动的假象……
;***************************************************************************
_AddBody  proc  node, bRmvTail
    push edi
    push ebx
    push ecx

    ; 如果是刚吃的,先把类型改成 BODY
    mov eax, node
    or eax, 00000100h
    xor edi, edi
@@:
    mov ebx, [offset nodeList + edi * 4]
    .if ebx != 0
        inc edi
        jmp @B
    .endif
    mov [offset nodeList + edi * 4], eax

    ; 如果要移除队列尾,则先把队列尾的激活状态压下去,然后集体左移一个索引位置
    ; 此时edi里保存着队列应有的长度
    .if bRmvTail
        ; 取第一个元素,这是队列头,先反激活它
        mov eax, [nodeList]
        mov ebx, eax
        and ebx, 0ff000000h
        shr ebx, 24
        mov ecx, eax
        and ecx, 00ff0000h
        shr ecx, 16
        invoke _Activate, ebx, ecx, FALSE

        ; 然后移动这个队列,把后面的都左移
        mov ecx, edi
        xor edi, edi
@@:
        mov eax, [offset nodeList + edi * 4 + 4]
        mov [offset nodeList + edi * 4], eax
        inc edi
        loop @B

        mov [offset nodeList + edi * 4], 0
    .endif

    pop ecx
    pop ebx
    pop edi
    ret
_AddBody  endp

;***************************************************************************
; 吃掉食物,吃掉时候分数增加,并且生成下一个食物
;***************************************************************************
_PieFromSky  proc
    mov eax, score
    add eax, 10
    mov score, eax
    mov eax, eatCounts
    .if eax != 3
        inc eatCounts
    .else
        mov eax, speed
        .if eax > 1
            dec speed
        .endif
        mov eatCounts, 0
    .endif
    invoke _GetNodeAt, foodX, foodY
    invoke _AddBody, eax, FALSE
    invoke _NextFood
    ret
_PieFromSky  endp

;***************************************************************************
; 新游戏,初始化各种状态
;***************************************************************************
_NewGame    proc
    invoke _InitNodes
    invoke _NextFood
    mov bTurnPending, FALSE
    mov isPaused, 0
    mov isAlive, 1
    mov score, 0
    mov speed, 8
    mov timerCounts, 0
    mov eatCounts, 0
    mov curX, 4
    mov curY, 1
    mov direction, RIGHT
    ret
_NewGame    endp

;***************************************************************************
; 蛇身移动
;***************************************************************************
_Move    proc
    local @nextX: DWORD
    local @nextY: DWORD
    push ebx
    push curX
    push curY
    pop @nextY
    pop @nextX
    mov bTurnPending, FALSE
    .if direction == RIGHT
        inc @nextX
    .elseif direction == DOWN
        inc @nextY
    .elseif direction == LEFT
        dec @nextX
    .elseif direction == UP
        dec @nextY
    .endif

    .if (@nextX == 0) || (@nextX > 23) || (@nextY == 0) || (@nextY > 17)
        mov isAlive, 0
        jmp @F
    .endif
    invoke _QueryNodeStat, @nextX, @nextY
    .if eax
        mov isAlive, 0
        jmp @F
    .endif

    mov eax, foodX
    mov ebx, foodY
    .if (@nextX == eax) && (@nextY == ebx)
        invoke _PieFromSky
    .endif

    ; 头的处理,下一个位置即将激活
    invoke _Activate, @nextX, @nextY, 1
    push @nextX
    push @nextY
    pop curY
    pop curX

    ; 把新头加入队列头,并指示移除队列尾
    invoke _GetNodeAt, curX, curY
    invoke _AddBody, eax, TRUE

@@:
    pop ebx
    ret
_Move    endp

#2


按最上面的要求编译并UPX,就可以得到4096字节的贪食蛇(变色龙?)了……

;***************************************************************************
; 窗口过程
;***************************************************************************
_ProcWinMain  proc hWnd, uMsg, wParam, lParam
    local @stPs:PAINTSTRUCT
    local @stRect:RECT
    local @hBmp: HBITMAP
    local @hOldBmp: HBITMAP
    local @memDC: HDC
    local @hDC: HDC

    mov eax, uMsg
    .if eax == WM_PAINT
        invoke BeginPaint, hWnd, addr @stPs
        mov @hDC, eax
        invoke CreateCompatibleBitmap, @hDC, wndWidth, wndHeight
        mov @hBmp, eax
        invoke CreateCompatibleDC, @hDC
        mov @memDC, eax
        invoke SelectObject, @memDC, @hBmp
        mov @hOldBmp, eax
        invoke _DrawBody, @memDC
        invoke _DrawNode, @memDC, food
        invoke _DrawScore, @memDC
        invoke BitBlt, @hDC, rClient.left, rClient.top, wndWidth, wndHeight, @memDC, rClient.left, rClient.top, SRCCOPY
        invoke SelectObject, @memDC, @hOldBmp
        invoke DeleteObject, @hBmp
        invoke DeleteDC, @memDC
        invoke EndPaint, hWnd, addr @stPs
    .elseif eax == WM_TIMER
        .if !isAlive
            invoke  SetWindowText, hWnd, offset szCaptionDie
            jmp @F
        .endif
        .if  isPaused
            jmp @F
        .endif

        mov eax, timerCounts
        .if speed != eax
            inc timerCounts
            jmp @F
        .endif
        mov timerCounts, 0
        invoke _Move
        invoke InvalidateRect, hWnd, addr rClient, FALSE
    .elseif eax == WM_CLOSE
        invoke  KillTimer, hWnd, TIMER
        invoke DestroyWindow, hWinMain
        invoke PostQuitMessage, NULL
    .elseif eax == WM_CREATE
        invoke SetTimer, hWnd, TIMER, 100, NULL
        invoke GetClientRect, hWnd, offset rClient
        mov bRanColor, TRUE
        mov eax, rClient.right
        sub eax, rClient.left
        mov wndWidth, eax
        mov eax, rClient.bottom
        sub eax, rClient.top
        mov wndHeight, eax
        invoke  _NewGame
    .elseif eax == WM_KEYDOWN
        mov eax, wParam
        ; 按P键暂停游戏
        .if eax == 'P'
            .if isPaused
                and  isPaused, 0
                invoke SetWindowText, hWnd, offset szCaptionMain
            .else
                or isPaused, 1
                invoke SetWindowText, hWnd, offset szCaptionPause
            .endif
        ; 按了上键
        .elseif eax == 'W' || eax == VK_UP
            .if isPaused || bTurnPending
                jmp @F
            .endif
            mov eax, direction
            .if eax == RIGHT || eax == LEFT
                push  UP
                pop direction
                invoke SetWindowText, hWnd, offset szUp
                mov bTurnPending, TRUE
            .endif
        ; 按了下键
        .elseif eax == 'S' || eax == VK_DOWN
            .if isPaused || bTurnPending
                jmp @F
            .endif
            mov eax, direction
            .if eax == RIGHT || eax == LEFT
                push DOWN
                pop direction
                invoke SetWindowText, hWnd, offset szDown
                mov bTurnPending, TRUE
            .endif
        ; 按了左键
        .elseif eax == 'A' || eax == VK_LEFT
            .if isPaused || bTurnPending
                jmp @F
            .endif
            mov eax, direction
            .if eax == UP || eax == DOWN
                push LEFT
                pop direction
                invoke SetWindowText, hWnd, offset szLeft
                mov bTurnPending, TRUE
            .endif
        ; 按了右键
        .elseif eax == 'D' || eax == VK_RIGHT
            .if isPaused || bTurnPending
                jmp @F
            .endif
            mov eax, direction
            .if eax == UP || eax == DOWN
                push RIGHT
                pop direction
                invoke SetWindowText, hWnd, offset szRight
                mov bTurnPending, TRUE
            .endif
        .elseif eax == 'C' 
            .if bRanColor
                and bRanColor, 0
            .else
                or bRanColor, 1
            .endif
        ; Q或者ESC退出游戏
        .elseif eax == 'Q' || eax == VK_ESCAPE
            invoke PostQuitMessage, 0
        ; 开始新游戏
        .elseif eax == 'N'
            invoke _NewGame
            invoke SetWindowText, hWnd, offset szCaptionMain
        .endif
    .else
        invoke DefWindowProc, hWnd, uMsg, wParam, lParam
        ret
    .endif

@@:
    xor eax, eax
    ret
_ProcWinMain  endp

;***************************************************************************
; 入口函数
;***************************************************************************
_Main  proc
    local @stWndClass:WNDCLASSEX
    local @stMsg:MSG

    mov food, FALSE
    invoke RtlZeroMemory, addr @stWndClass, sizeof @stWndClass

    invoke GetModuleHandle, NULL
    mov @stWndClass.hInstance, eax
    invoke LoadCursor, NULL, IDC_ARROW
    mov @stWndClass.hCursor, eax
    invoke LoadIcon, NULL, IDI_INFORMATION
    mov @stWndClass.hIcon, eax
    mov @stWndClass.cbSize, sizeof WNDCLASSEX
    mov @stWndClass.style, CS_HREDRAW or CS_VREDRAW
    mov @stWndClass.lpfnWndProc, offset _ProcWinMain
    mov @stWndClass.hbrBackground, COLOR_WINDOW
    mov @stWndClass.lpszClassName, offset szClassName
    invoke RegisterClassEx, addr @stWndClass

    ; 显示窗口
    invoke CreateWindowEx, WS_EX_CLIENTEDGE, offset szClassName, offset szCaptionMain, WS_OVERLAPPEDWINDOW and not WS_MAXIMIZEBOX and not WS_THICKFRAME, 200, 100, 500, 404, NULL, NULL, hInstance, NULL
    mov hWinMain, eax
    invoke ShowWindow, hWinMain, SW_SHOWNORMAL
    invoke UpdateWindow, hWinMain

    ; 消息循环
    .while TRUE
        invoke GetMessage, addr @stMsg, NULL, 0, 0
        .break .if eax == 0
        invoke TranslateMessage, addr @stMsg
        invoke DispatchMessage, addr @stMsg
    .endw
ret
_Main  endp

start:
    call _Main
    invoke ExitProcess, NULL
end start

#3


沙发我来坐,顺便加精

#4


引用 3 楼 mydo 的回复:
沙发我来坐,顺便加精

这也太快了……

#5


很强大 好啊好

#6


 好!!!非常好!

#7


牛XXXX 收藏之~~~

#8


贪食蛇男的贪食蛇

#9


学了一学期汇编的 表示看不懂

#10


变态的牛人

#11


不错。

#12


看不懂

#13


顶起!

#14


学习 膜拜

#15


很想拜你为师

#16


大牛啊!

#17


牛人一个!顶起!

#18


虽然看不懂,但顶一下 贪食蛇男的贪食蛇

#19


牛逼的人啊  

#20


哪里有免费下载的作品呢?、

#21


很好,很强大

#22


很疼啊,各种想法。。。

#23


我去,NB

#24


很好,很好

#25


引用 20 楼 hankanling123 的回复:
哪里有免费下载的作品呢?、

http://dl.dbank.com/c0z04k335p ,传到网盘了,里面包含一个ASM源文件和一个4096字节的可执行文件,可执行文件即游戏本身,由于UPX压过,过小,可能会被报毒。

#26


很想拜你为师

#27


很牛,很牛

#28


该回复于2011-05-26 15:22:32被版主删除

#29


我好久没有来了。。。

#30


残酷 我只用C#写过,还弄了首 纤夫的爱 当背景音乐

#31


  你的实力将会是我前进方向上的一杆标杆,谢啦

#32


收藏,学习!

#33


贪食蛇男的贪食蛇汇编。。。我头都大了。。

#34


该回复于2011-05-26 16:24:31被版主删除

#35


牛人一个!顶起!


牛人一个!顶起!

#36


这个太强了

#37


说实话,看的很头疼,最关键的是没看懂..........

#38


强大,一定要顶!11

#39


该回复于2011-05-26 17:08:16被版主删除

#40


贪食蛇男的贪食蛇

#41


haha 牛逼啊

#42


C++路过,压力很大

#43


好厉害

#44


无法可说了……

#45


又是一个牛叉人

#46


segvsg

#47


牛人啊!在下表示完全看不懂

#48


哪里有免费下载的作品呢?、

#49


又是一个牛叉人

#50


有一种egg疼的感觉。。。

#1



;***************************************************************************
; 画蛇身,其实就是遍历所有结点画,绘画函数会自己根据结点的状态来选择绘
; 制方式。
;***************************************************************************
_DrawBody  proc  uses ecx edi _hDC
    push edi
    push ecx
    push eax
    xor edi, edi
    mov ecx, 187h
@@:
    mov eax, [offset nodeList + edi * 4]
    .if !eax
        jmp @F
    .endif
    invoke _DrawNode, _hDC, eax
    inc edi
    loop @B

@@:
    pop eax
    pop ecx
    pop edi
    ret
_DrawBody  endp

;***************************************************************************
; 绘制分数
;***************************************************************************
_DrawScore  proc  _hDC
    local @oldColor: COLORREF
    local @oldBkMode: DWORD
    local @szBuffer[128]: BYTE
    local @hFont: HFONT
    local @hOldFont: HFONT
    local @hPen: HPEN
    local @hOldPen: HPEN
    local @strLen: DWORD

    push ebx
    invoke wsprintf, offset szScoreBuffer, offset szScore, score
    ; 求字符串长度
    xor eax, eax
@@:
    mov bl, BYTE ptr [offset szScoreBuffer + eax]
    .if bl != 0
        inc eax
        jmp @B
    .endif
    mov @strLen, eax

    invoke SetBkMode, _hDC, TRANSPARENT
    mov @oldBkMode, eax
    invoke SetTextColor, _hDC, 0000ffffh
    mov @oldColor, eax

    invoke CreateFont, 14, 7, 0, 0, 8, FALSE, FALSE, FALSE, GB2312_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, 5, DEFAULT_PITCH, NULL
    mov @hFont, eax
    invoke SelectObject, _hDC, eax
    mov @hOldFont, eax
    invoke CreatePen, PS_SOLID, 1, 00ffffffh
    mov @hPen, eax
    invoke SelectObject, _hDC, eax
    mov @hOldPen, eax

    invoke TextOut, _hDC, 410, 15, offset szScoreBuffer, @strLen
    invoke MoveToEx, _hDC, 10, 10, 0
    invoke LineTo, _hDC, 480, 10
    invoke LineTo, _hDC, 480, 360
    invoke LineTo, _hDC, 10, 360
    invoke LineTo, _hDC, 10, 10

    invoke SetBkMode, _hDC, @oldBkMode
    invoke SetTextColor, _hDC, @oldColor
    invoke SelectObject, _hDC, @hOldFont
    invoke SelectObject, _hDC, @hOldPen
    invoke DeleteObject, @hFont
    invoke DeleteObject, @hPen

    pop ebx
    ret
_DrawScore  endp

;***************************************************************************
; 获得指定位置上的结点
;***************************************************************************
_GetNodeAt  proc  xLocation, yLocation
    push ebx
    mov eax, yLocation
    dec eax
    mov ebx, 17h
    mul ebx
    add eax, xLocation
    dec eax
    mov eax, [offset nodes + eax * 4]
    pop ebx
    ret
_GetNodeAt  endp

;***************************************************************************
; 由传入的X Y 位置查询目标结点的状态(是否激活)
;***************************************************************************
_QueryNodeStat  proc  xLocation, yLocation
    invoke _GetNodeAt, xLocation, yLocation
    and eax, 000000ffh
    ret
_QueryNodeStat  endp

;***************************************************************************
; 激活或压制一个已处于激活状态的结点
; 参数分别是 x 坐标, y坐标, 是否激活的布尔变量
;***************************************************************************
_Activate  proc  xLocation, yLocation, bActivate
    push ebx
    push edi
    mov eax, yLocation
    dec eax
    mov ebx, 17h
    mul ebx
    add eax, xLocation
    dec eax
    mov edi, eax

    mov eax, [offset nodes + eax * 4]
    .if bActivate
        or eax, 0000001h
    .else
        and eax, 0ffffff00h
    .endif
    mov [offset nodes + edi * 4], eax

    pop edi
    pop ebx
    ret
_Activate  endp

;***************************************************************************
; 下一个食物,总是随机出现。并且不会出现在蛇身上
; 这种实现似乎比较2,更好的方法应该是把所有结点串成两个链表,蛇身链表和非
; 蛇身链表。每次随机地从非蛇身链表里取出结点插入蛇身链表中,这样就不会造成
; 随机生成的位置在蛇身上从而重新生成了。
; 重新生巢呗陨在蛇身将要填充满整个区域时性能急剧下降。
;***************************************************************************
_NextFood    proc uses ebx
    invoke _NextInt, 17
    mov foodY, eax
    invoke _NextInt, 23
    mov foodX, eax
    invoke _QueryNodeStat, foodX, foodY
    .if eax
        invoke _NextFood
    .endif
    invoke _CreateNode, foodX, foodY, FOOD, 1
    mov food, eax
    ret
_NextFood    endp

;***************************************************************************
; 往队列头加一个结点,刚吃了一顿或走了一步后都会调用此函数。
; 第一个参数是要加入的结点,第二个是指示是否移除队列尾。正常移动时需要
; 在队列头加结点的同时移除队列尾,以达到好像在移动的假象……
;***************************************************************************
_AddBody  proc  node, bRmvTail
    push edi
    push ebx
    push ecx

    ; 如果是刚吃的,先把类型改成 BODY
    mov eax, node
    or eax, 00000100h
    xor edi, edi
@@:
    mov ebx, [offset nodeList + edi * 4]
    .if ebx != 0
        inc edi
        jmp @B
    .endif
    mov [offset nodeList + edi * 4], eax

    ; 如果要移除队列尾,则先把队列尾的激活状态压下去,然后集体左移一个索引位置
    ; 此时edi里保存着队列应有的长度
    .if bRmvTail
        ; 取第一个元素,这是队列头,先反激活它
        mov eax, [nodeList]
        mov ebx, eax
        and ebx, 0ff000000h
        shr ebx, 24
        mov ecx, eax
        and ecx, 00ff0000h
        shr ecx, 16
        invoke _Activate, ebx, ecx, FALSE

        ; 然后移动这个队列,把后面的都左移
        mov ecx, edi
        xor edi, edi
@@:
        mov eax, [offset nodeList + edi * 4 + 4]
        mov [offset nodeList + edi * 4], eax
        inc edi
        loop @B

        mov [offset nodeList + edi * 4], 0
    .endif

    pop ecx
    pop ebx
    pop edi
    ret
_AddBody  endp

;***************************************************************************
; 吃掉食物,吃掉时候分数增加,并且生成下一个食物
;***************************************************************************
_PieFromSky  proc
    mov eax, score
    add eax, 10
    mov score, eax
    mov eax, eatCounts
    .if eax != 3
        inc eatCounts
    .else
        mov eax, speed
        .if eax > 1
            dec speed
        .endif
        mov eatCounts, 0
    .endif
    invoke _GetNodeAt, foodX, foodY
    invoke _AddBody, eax, FALSE
    invoke _NextFood
    ret
_PieFromSky  endp

;***************************************************************************
; 新游戏,初始化各种状态
;***************************************************************************
_NewGame    proc
    invoke _InitNodes
    invoke _NextFood
    mov bTurnPending, FALSE
    mov isPaused, 0
    mov isAlive, 1
    mov score, 0
    mov speed, 8
    mov timerCounts, 0
    mov eatCounts, 0
    mov curX, 4
    mov curY, 1
    mov direction, RIGHT
    ret
_NewGame    endp

;***************************************************************************
; 蛇身移动
;***************************************************************************
_Move    proc
    local @nextX: DWORD
    local @nextY: DWORD
    push ebx
    push curX
    push curY
    pop @nextY
    pop @nextX
    mov bTurnPending, FALSE
    .if direction == RIGHT
        inc @nextX
    .elseif direction == DOWN
        inc @nextY
    .elseif direction == LEFT
        dec @nextX
    .elseif direction == UP
        dec @nextY
    .endif

    .if (@nextX == 0) || (@nextX > 23) || (@nextY == 0) || (@nextY > 17)
        mov isAlive, 0
        jmp @F
    .endif
    invoke _QueryNodeStat, @nextX, @nextY
    .if eax
        mov isAlive, 0
        jmp @F
    .endif

    mov eax, foodX
    mov ebx, foodY
    .if (@nextX == eax) && (@nextY == ebx)
        invoke _PieFromSky
    .endif

    ; 头的处理,下一个位置即将激活
    invoke _Activate, @nextX, @nextY, 1
    push @nextX
    push @nextY
    pop curY
    pop curX

    ; 把新头加入队列头,并指示移除队列尾
    invoke _GetNodeAt, curX, curY
    invoke _AddBody, eax, TRUE

@@:
    pop ebx
    ret
_Move    endp

#2


按最上面的要求编译并UPX,就可以得到4096字节的贪食蛇(变色龙?)了……

;***************************************************************************
; 窗口过程
;***************************************************************************
_ProcWinMain  proc hWnd, uMsg, wParam, lParam
    local @stPs:PAINTSTRUCT
    local @stRect:RECT
    local @hBmp: HBITMAP
    local @hOldBmp: HBITMAP
    local @memDC: HDC
    local @hDC: HDC

    mov eax, uMsg
    .if eax == WM_PAINT
        invoke BeginPaint, hWnd, addr @stPs
        mov @hDC, eax
        invoke CreateCompatibleBitmap, @hDC, wndWidth, wndHeight
        mov @hBmp, eax
        invoke CreateCompatibleDC, @hDC
        mov @memDC, eax
        invoke SelectObject, @memDC, @hBmp
        mov @hOldBmp, eax
        invoke _DrawBody, @memDC
        invoke _DrawNode, @memDC, food
        invoke _DrawScore, @memDC
        invoke BitBlt, @hDC, rClient.left, rClient.top, wndWidth, wndHeight, @memDC, rClient.left, rClient.top, SRCCOPY
        invoke SelectObject, @memDC, @hOldBmp
        invoke DeleteObject, @hBmp
        invoke DeleteDC, @memDC
        invoke EndPaint, hWnd, addr @stPs
    .elseif eax == WM_TIMER
        .if !isAlive
            invoke  SetWindowText, hWnd, offset szCaptionDie
            jmp @F
        .endif
        .if  isPaused
            jmp @F
        .endif

        mov eax, timerCounts
        .if speed != eax
            inc timerCounts
            jmp @F
        .endif
        mov timerCounts, 0
        invoke _Move
        invoke InvalidateRect, hWnd, addr rClient, FALSE
    .elseif eax == WM_CLOSE
        invoke  KillTimer, hWnd, TIMER
        invoke DestroyWindow, hWinMain
        invoke PostQuitMessage, NULL
    .elseif eax == WM_CREATE
        invoke SetTimer, hWnd, TIMER, 100, NULL
        invoke GetClientRect, hWnd, offset rClient
        mov bRanColor, TRUE
        mov eax, rClient.right
        sub eax, rClient.left
        mov wndWidth, eax
        mov eax, rClient.bottom
        sub eax, rClient.top
        mov wndHeight, eax
        invoke  _NewGame
    .elseif eax == WM_KEYDOWN
        mov eax, wParam
        ; 按P键暂停游戏
        .if eax == 'P'
            .if isPaused
                and  isPaused, 0
                invoke SetWindowText, hWnd, offset szCaptionMain
            .else
                or isPaused, 1
                invoke SetWindowText, hWnd, offset szCaptionPause
            .endif
        ; 按了上键
        .elseif eax == 'W' || eax == VK_UP
            .if isPaused || bTurnPending
                jmp @F
            .endif
            mov eax, direction
            .if eax == RIGHT || eax == LEFT
                push  UP
                pop direction
                invoke SetWindowText, hWnd, offset szUp
                mov bTurnPending, TRUE
            .endif
        ; 按了下键
        .elseif eax == 'S' || eax == VK_DOWN
            .if isPaused || bTurnPending
                jmp @F
            .endif
            mov eax, direction
            .if eax == RIGHT || eax == LEFT
                push DOWN
                pop direction
                invoke SetWindowText, hWnd, offset szDown
                mov bTurnPending, TRUE
            .endif
        ; 按了左键
        .elseif eax == 'A' || eax == VK_LEFT
            .if isPaused || bTurnPending
                jmp @F
            .endif
            mov eax, direction
            .if eax == UP || eax == DOWN
                push LEFT
                pop direction
                invoke SetWindowText, hWnd, offset szLeft
                mov bTurnPending, TRUE
            .endif
        ; 按了右键
        .elseif eax == 'D' || eax == VK_RIGHT
            .if isPaused || bTurnPending
                jmp @F
            .endif
            mov eax, direction
            .if eax == UP || eax == DOWN
                push RIGHT
                pop direction
                invoke SetWindowText, hWnd, offset szRight
                mov bTurnPending, TRUE
            .endif
        .elseif eax == 'C' 
            .if bRanColor
                and bRanColor, 0
            .else
                or bRanColor, 1
            .endif
        ; Q或者ESC退出游戏
        .elseif eax == 'Q' || eax == VK_ESCAPE
            invoke PostQuitMessage, 0
        ; 开始新游戏
        .elseif eax == 'N'
            invoke _NewGame
            invoke SetWindowText, hWnd, offset szCaptionMain
        .endif
    .else
        invoke DefWindowProc, hWnd, uMsg, wParam, lParam
        ret
    .endif

@@:
    xor eax, eax
    ret
_ProcWinMain  endp

;***************************************************************************
; 入口函数
;***************************************************************************
_Main  proc
    local @stWndClass:WNDCLASSEX
    local @stMsg:MSG

    mov food, FALSE
    invoke RtlZeroMemory, addr @stWndClass, sizeof @stWndClass

    invoke GetModuleHandle, NULL
    mov @stWndClass.hInstance, eax
    invoke LoadCursor, NULL, IDC_ARROW
    mov @stWndClass.hCursor, eax
    invoke LoadIcon, NULL, IDI_INFORMATION
    mov @stWndClass.hIcon, eax
    mov @stWndClass.cbSize, sizeof WNDCLASSEX
    mov @stWndClass.style, CS_HREDRAW or CS_VREDRAW
    mov @stWndClass.lpfnWndProc, offset _ProcWinMain
    mov @stWndClass.hbrBackground, COLOR_WINDOW
    mov @stWndClass.lpszClassName, offset szClassName
    invoke RegisterClassEx, addr @stWndClass

    ; 显示窗口
    invoke CreateWindowEx, WS_EX_CLIENTEDGE, offset szClassName, offset szCaptionMain, WS_OVERLAPPEDWINDOW and not WS_MAXIMIZEBOX and not WS_THICKFRAME, 200, 100, 500, 404, NULL, NULL, hInstance, NULL
    mov hWinMain, eax
    invoke ShowWindow, hWinMain, SW_SHOWNORMAL
    invoke UpdateWindow, hWinMain

    ; 消息循环
    .while TRUE
        invoke GetMessage, addr @stMsg, NULL, 0, 0
        .break .if eax == 0
        invoke TranslateMessage, addr @stMsg
        invoke DispatchMessage, addr @stMsg
    .endw
ret
_Main  endp

start:
    call _Main
    invoke ExitProcess, NULL
end start

#3


沙发我来坐,顺便加精

#4


引用 3 楼 mydo 的回复:
沙发我来坐,顺便加精

这也太快了……

#5


很强大 好啊好

#6


 好!!!非常好!

#7


牛XXXX 收藏之~~~

#8


贪食蛇男的贪食蛇

#9


学了一学期汇编的 表示看不懂

#10


变态的牛人

#11


不错。

#12


看不懂

#13


顶起!

#14


学习 膜拜

#15


很想拜你为师

#16


大牛啊!

#17


牛人一个!顶起!

#18


虽然看不懂,但顶一下 贪食蛇男的贪食蛇

#19


牛逼的人啊  

#20


哪里有免费下载的作品呢?、

#21


很好,很强大

#22


很疼啊,各种想法。。。

#23


我去,NB

#24


很好,很好

#25


引用 20 楼 hankanling123 的回复:
哪里有免费下载的作品呢?、

http://dl.dbank.com/c0z04k335p ,传到网盘了,里面包含一个ASM源文件和一个4096字节的可执行文件,可执行文件即游戏本身,由于UPX压过,过小,可能会被报毒。

#26


很想拜你为师

#27


很牛,很牛

#28


该回复于2011-05-26 15:22:32被版主删除

#29


我好久没有来了。。。

#30


残酷 我只用C#写过,还弄了首 纤夫的爱 当背景音乐

#31


  你的实力将会是我前进方向上的一杆标杆,谢啦

#32


收藏,学习!

#33


贪食蛇男的贪食蛇汇编。。。我头都大了。。

#34


该回复于2011-05-26 16:24:31被版主删除

#35


牛人一个!顶起!


牛人一个!顶起!

#36


这个太强了

#37


说实话,看的很头疼,最关键的是没看懂..........

#38


强大,一定要顶!11

#39


该回复于2011-05-26 17:08:16被版主删除

#40


贪食蛇男的贪食蛇

#41


haha 牛逼啊

#42


C++路过,压力很大

#43


好厉害

#44


无法可说了……

#45


又是一个牛叉人

#46


segvsg

#47


牛人啊!在下表示完全看不懂

#48


哪里有免费下载的作品呢?、

#49


又是一个牛叉人

#50


有一种egg疼的感觉。。。