0


Visual C++扫雷游戏实战三:核心算法设计与实现(附源码和资源)

需要源码和资源请点赞关注收藏后评论区留言私信~~~

在前面的博客中已经讲解了扫雷游戏的菜单和各种对话框的实现,下面将对扫雷游戏的核心算法设计与实现进行讲解

一、新游戏处理模块的设计与实现

新游戏处理模块主要负责游戏中的游戏初始化以及开始游戏,其设计比较简单,只需要通过如下几个步骤即可实现

1:载入图片资源和配置文件中的数据

2:把所有的游戏参数进行初始化,例如当前消耗时间和状态等

3:初始化表示地雷区域的二维数组

4:让地雷区域图像失效,重新绘制新的图像

实现代码如下

void CMyMine::LoadConfig()
{
    char pszTmp[128] = {0};
    
    GetPrivateProfileString("HERO", "time", "0", 
        pszTmp, 127, ".\\hero.ini");
    m_uHighTime = atoi(pszTmp);

    m_uXNum        = 30;    //X坐标上的方块个数
    m_uYNum        = 16;    //Y坐标上的方块个数
    m_uMineNum    = 10;    //地雷个数

    m_bMarkful  = TRUE;
    m_bColorful = TRUE;
}
/*载入图片资源*/
void CMyMine::LoadBitmap()
{
    if (m_bColorful) {
        m_clrDark = COLOR_DARK_GRAY;
        m_bmpMine.DeleteObject();
        m_bmpMine.LoadBitmap(IDB_MINE_COLOR);
        m_bmpNumber.DeleteObject();
        m_bmpNumber.LoadBitmap(IDB_NUM_COLOR);
        m_bmpButton.DeleteObject();
        m_bmpButton.LoadBitmap(IDB_BTN_COLOR);
    }
    else {
        m_clrDark = COLOR_BLACK;
        m_bmpMine.DeleteObject();
        m_bmpMine.LoadBitmap(IDB_MINE_GRAY);
        m_bmpNumber.DeleteObject();
        m_bmpNumber.LoadBitmap(IDB_NUM_GRAY);
        m_bmpButton.DeleteObject();
        m_bmpButton.LoadBitmap(IDB_BTN_GRAY);
    }
}
/*初始化游戏*/
void CMyMine::InitGame()
{
    LoadBitmap();
    LoadConfig();
    m_nLeaveNum = m_uMineNum;
    m_uSpendTime = 0;
    m_uBtnState = BS_NORMAL;    //设置当前方块状态
    m_uGameState = GS_WAIT;        //设置当前游戏状态
    if (m_uTimer) {
        KillTimer(ID_TIMER_EVENT);
        m_uTimer = 0;
    }
    m_pNewMine = NULL;            //清空当前选中的小方块
    m_pOldMine = NULL;            //清空上次选中的小方块
    //初始化表示雷区的二维数组
    for (UINT i = 0; i<m_uYNum; i++) {
        for (UINT j = 0; j<m_uXNum; j++) {
            m_pMines[i][j].uRow = i;
            m_pMines[i][j].uCol = j;
            m_pMines[i][j].uState = STATE_NORMAL;
            m_pMines[i][j].uAttrib = ATTRIB_EMPTY;
            m_pMines[i][j].uOldState = STATE_NORMAL;
        }
    }
}

void CMyMine::StartGame()
{    
    InitGame();
    Invalidate();
}

二、地雷格子模块的设计与实现

地雷格子的处理是扫雷游戏的核心内容,包括如下几个部分

1:地雷铺设模块

游戏中的地雷是随机铺设的,可以调用随机数发生函数生成随机数,利用随机数去除最大行列树,得到放置地雷行列坐标,然后分别放置地雷到不同的行和列的格子中去

2:自动打开周围不是地雷的格子

在游戏中,当玩家单击的格子周围没有地雷格子时,就需要程序自动把周围的格子自动打开来提高玩家的效率,其实现是通过递归的方法不断的打开当前格子周围地雷个数为0的格子来实现

3:获得周围地雷个数模块

在游戏中当玩家打开一个格子时,如果当前这个格子不是地雷,那么其一定是标明周围的地雷个数的格子,要实现这个功能主要是通过遍历当前格子周围的3×3范围的数组,当找到一个元素状态是地雷时,就把记录增加1,直到9个格子全部找完,这样就可以得到当前格子周围的地雷个数,代码实现如下

void CMyMine::ExpandMines(UINT row, UINT col)
{
    UINT i, j;
    UINT minRow = (row == 0) ? 0 : row - 1;
    UINT maxRow = row + 2;
    UINT minCol = (col == 0) ? 0 : col - 1;
    UINT maxCol = col + 2;
    UINT around = GetAroundNum(row, col);

    m_pMines[row][col].uState = 15 - around;
    m_pMines[row][col].uOldState = 15 - around;
    //在指定位置画出地雷
    DrawSpecialMine(row, col);
    if (around == 0) {
        for (i = minRow; i < maxRow; i++) {
            for (j = minCol; j < maxCol; j++) {
                if (!(i == row && j == col) && 
                    m_pMines[i][j].uState == STATE_NORMAL
                    && m_pMines[i][j].uAttrib != ATTRIB_MINE) {
                    if (!IsInMineArea(i, j)) continue;
                    ExpandMines(i, j);
                }
            }
        }
    }
}
/*获得周围地雷个数*/
UINT CMyMine::GetAroundNum(UINT row, UINT col)
{
    UINT i, j;
    UINT around = 0;
    UINT minRow = (row == 0) ? 0 : row - 1;
    UINT maxRow = row + 2;
    UINT minCol = (col == 0) ? 0 : col - 1;
    UINT maxCol = col + 2;
    
    for (i = minRow; i < maxRow; i++) {
        for (j = minCol; j < maxCol; j++) {
            if (!IsInMineArea(i, j)) continue;
            if (m_pMines[i][j].uAttrib == ATTRIB_MINE) around++;
        }
    }
    return around;
}

/*得到周围格子状态*/
UINT CMyMine::GetAroundFlags(UINT row, UINT col)
{
    UINT i, j;
    UINT flags = 0;
    UINT minRow = (row == 0) ? 0 : row - 1;
    UINT maxRow = row + 2;
    UINT minCol = (col == 0) ? 0 : col - 1;
    UINT maxCol = col + 2;
    
    for (i = minRow; i < maxRow; i++) {
        for (j = minCol; j < maxCol; j++) {
            if (!IsInMineArea(i, j)) continue;
            if (m_pMines[i][j].uState == STATE_FLAG) flags++;
        }
    }
    return flags;
}

/*地雷判断*/
BOOL CMyMine::IsMine(UINT row, UINT col)
{
    return (m_pMines[row][col].uAttrib == ATTRIB_MINE);
}

/*雷区判断*/
BOOL CMyMine::IsInMineArea(UINT row, UINT col)
{
    return (row >= 0 && row < m_uYNum && col >= 0 && col < m_uXNum);
}
/*游戏结束*/
void CMyMine::Dead(UINT row, UINT col)
{
    CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
    CRect rcMineArea(MINE_AREA_LEFT, MINE_AREA_TOP, 
        MINE_AREA_LEFT + m_uXNum * MINE_WIDTH, MINE_AREA_TOP + m_uYNum * MINE_HEIGHT);
    
    UINT i, j;
    if (m_pMines[row][col].uAttrib == ATTRIB_MINE) {//打开了是地雷的格子
        for (i = 0; i < m_uYNum; i++) {
            for (j = 0; j < m_uXNum; j++) {
                m_pMines[row][col].uState = STATE_BLAST;
                m_pMines[row][col].uOldState = STATE_BLAST;
                if (m_pMines[i][j].uAttrib == ATTRIB_MINE
                    && m_pMines[i][j].uState != STATE_FLAG) {
                    m_pMines[i][j].uState = STATE_MINE;
                    m_pMines[i][j].uOldState = STATE_MINE;
                }
            }
        }
    }
    else {                                            //打开了判断错误的格子
        for (i = 0; i < m_uYNum; i++) {
            for (j = 0; j < m_uXNum; j++) {
                m_pMines[row][col].uState = STATE_ERROR;
                m_pMines[row][col].uOldState = STATE_ERROR;
                if (m_pMines[i][j].uAttrib == ATTRIB_MINE
                    && m_pMines[i][j].uState != STATE_FLAG) {
                    m_pMines[i][j].uState = STATE_MINE;
                    m_pMines[i][j].uOldState = STATE_MINE;
                }
            }
        }
    }
    
    InvalidateRect(rcMineArea);
    m_uBtnState = BS_DEAD;
    InvalidateRect(rcBtn);    
    m_uGameState = GS_DEAD;
    if (m_uTimer != 0) {
        KillTimer(ID_TIMER_EVENT);
        m_uTimer = 0;
    }

    AfxMessageBox("您踩到地雷了,游戏结束");

}

/*获得胜利*/
BOOL CMyMine::Victory()
{
    UINT i, j;
    CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
    
    for (i = 0; i < m_uYNum; i++) {
        for (j = 0; j < m_uXNum; j++) {
            if (m_pMines[i][j].uState == STATE_NORMAL) return FALSE;
            if (m_pMines[i][j].uState == STATE_DICEY) return FALSE;
        }
    }
    
    m_uBtnState = BS_VICTORY;
    m_uGameState = GS_VICTORY;
    Invalidate();
    if (m_uTimer != 0) {
        KillTimer(ID_TIMER_EVENT);
        m_uTimer = 0;
    }

    AfxMessageBox("恭喜您胜利了,游戏结束");

    if(m_uSpendTime < m_uHighTime)
    {
        CHeroDlg dlg;

        dlg.m_time = m_uSpendTime;

        dlg.SetWriteFlg(TRUE);

        dlg.DoModal();
    }

    return TRUE;
}
/*打开指定行列周围格子*/
void CMyMine::OpenAround(UINT row, UINT col)
{
    if (GetAroundFlags(row, col) != GetAroundNum(row, col)) return;
    UINT i, j;
    UINT around = 0;
    UINT minRow = (row == 0) ? 0 : row - 1;
    UINT maxRow = row + 2;
    UINT minCol = (col == 0) ? 0 : col - 1;
    UINT maxCol = col + 2;
    
    for (i = minRow; i < maxRow; i++) {
        for (j = minCol; j < maxCol; j++) {
            if (!IsInMineArea(i, j)) continue;
            if (m_pMines[i][j].uState == STATE_NORMAL) {
                ExpandMines(i, j);
                around = GetAroundNum(i, j);
                m_pMines[i][j].uState = 15 - around;
                m_pMines[i][j].uOldState = 15 - around;
            }
        }
    }
    // 检查是否胜利
    if (Victory()) {
        for (i = 0; i < m_uYNum; i++) {
            for (j = 0; j < m_uXNum; j++) {
                if (m_pMines[i][j].uAttrib == ATTRIB_MINE) {
                    m_pMines[i][j].uState = STATE_FLAG;
                    m_pMines[i][j].uOldState = STATE_FLAG;
                }
            }
        }
        m_nLeaveNum = 0;
        Invalidate();
    }
}

BOOL CMyMine::ErrorAroundFlag(UINT row, UINT col)
{
    if (GetAroundFlags(row, col) != GetAroundNum(row, col)) return FALSE;
    UINT i, j;
    UINT minRow = (row == 0) ? 0 : row - 1;
    UINT maxRow = row + 2;
    UINT minCol = (col == 0) ? 0 : col - 1;
    UINT maxCol = col + 2;
    
    for (i = minRow; i < maxRow; i++) {
        for (j = minCol; j < maxCol; j++) {
            if (!IsInMineArea(i, j)) continue;
            if (m_pMines[i][j].uState == STATE_FLAG) {
                if (m_pMines[i][j].uAttrib != ATTRIB_MINE) {
                    Dead(i, j);
                    return TRUE;
                }
            }
        }
    }
    
    return FALSE;
}

void CMyMine::OnLRBtnDown(UINT row, UINT col)
{
    UINT i, j;
    UINT minRow = (row == 0) ? 0 : row - 1;
    UINT maxRow = row + 2;
    UINT minCol = (col == 0) ? 0 : col - 1;
    UINT maxCol = col + 2;
    
    for (i = minRow; i < maxRow; i++) {
        for (j = minCol; j < maxCol; j++) {
            if (!IsInMineArea(i, j)) continue;
            //            if (i == row && j == col) continue;
            if (m_pMines[i][j].uState == STATE_NORMAL) {
                m_pMines[i][j].uState = STATE_EMPTY;
            }
            else if (m_pMines[i][j].uState == STATE_DICEY) {
                m_pMines[i][j].uState = STATE_DICEY_DOWN;
            }
        }
    }
}

void CMyMine::OnLRBtnUp(UINT row, UINT col)
{
    UINT i, j;
    UINT minRow = (row == 0) ? 0 : row - 1;
    UINT maxRow = row + 2;
    UINT minCol = (col == 0) ? 0 : col - 1;
    UINT maxCol = col + 2;
    
    for (i = minRow; i < maxRow; i++) {
        for (j = minCol; j < maxCol; j++) {
            if (!IsInMineArea(i, j)) continue;
            //            if (i == row && j == col) continue;
            if (m_pMines[i][j].uOldState == STATE_NORMAL) {
                m_pMines[i][j].uState = STATE_NORMAL;
            }
            else if (m_pMines[i][j].uOldState == STATE_DICEY) {
                m_pMines[i][j].uState = STATE_DICEY;
            }
        }
    }
    //    Invalidate();

三、游戏规则模块设计与实现

游戏规则模块的实现,主要由游戏结束和游戏胜利判断函数组成,通过对游戏的结果进行判断,实现扫雷游戏的规则

BOOL CMyMine::Victory()
{
    UINT i, j;
    CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
    
    for (i = 0; i < m_uYNum; i++) {
        for (j = 0; j < m_uXNum; j++) {
            if (m_pMines[i][j].uState == STATE_NORMAL) return FALSE;
            if (m_pMines[i][j].uState == STATE_DICEY) return FALSE;
        }
    }
    
    m_uBtnState = BS_VICTORY;
    m_uGameState = GS_VICTORY;
    Invalidate();
    if (m_uTimer != 0) {
        KillTimer(ID_TIMER_EVENT);
        m_uTimer = 0;
    }

    AfxMessageBox("恭喜您胜利了,游戏结束");

    if(m_uSpendTime < m_uHighTime)
    {
        CHeroDlg dlg;

        dlg.m_time = m_uSpendTime;

        dlg.SetWriteFlg(TRUE);

        dlg.DoModal();
    }

    return TRUE;
}
/*打开指定行列周围格子*/

四、游戏绘图模块的设计与实现

在扫雷游戏中,通过绘图模块来实现地雷,格子,地雷个数,当前时间以及控制按钮等图片和信息的显示,由如下几个函数组成

void CMyMine::DrawButton(CPaintDC &dc)
{
    CDC cdc;    
    cdc.CreateCompatibleDC(&dc);
    cdc.SelectObject(m_bmpButton);    
    dc.StretchBlt(m_uBtnRect[0], 16, 24, 24, &cdc, 0, 24 * m_uBtnState, 24, 24, SRCCOPY);
    
    dc.Draw3dRect(m_uBtnRect[1], 15, 26, 26, m_clrDark, m_clrDark);
}

void CMyMine::DrawNumber(CPaintDC &dc)
{
    CDC cdc;    
    cdc.CreateCompatibleDC(&dc);
    cdc.SelectObject(m_bmpNumber);
    
    dc.Draw3dRect(16, 15, 41, 25, m_clrDark, COLOR_WHITE);
    dc.Draw3dRect(m_uNumRect[0], 15, 41, 25, m_clrDark, COLOR_WHITE);
    int num;
    // draw remaining mine numbers
    num = (m_nLeaveNum < 0) ? 11 : m_nLeaveNum / 100;
    dc.StretchBlt(17, 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
    num = (m_nLeaveNum < 0) ? -(m_nLeaveNum - num * 100) / 10 : (m_nLeaveNum - num * 100) / 10;
    dc.StretchBlt(30, 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
    num = (m_nLeaveNum < 0) ? -m_nLeaveNum % 10 : m_nLeaveNum % 10;
    dc.StretchBlt(43, 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
    // draw take seconds
    num = m_uSpendTime / 100;
    dc.StretchBlt(m_uNumRect[0], 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
    num = (m_uSpendTime - num * 100) / 10;
    dc.StretchBlt(m_uNumRect[0] + 13, 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
    num = m_uSpendTime % 10;
    dc.StretchBlt(m_uNumRect[0] + 26, 16, 13, 23, &cdc, 0, 276 - 23 * (num+1), 13, 23, SRCCOPY);
}

/*void CMyMine::DrawShell(CPaintDC &dc)
{
    // draw side
    dc.FillSolidRect(0, 0, m_uShellRcX[0], LINE_WIDTH_0, COLOR_WHITE);
    dc.FillSolidRect(0, 0, LINE_WIDTH_0, m_uShellRcY[0], COLOR_WHITE);
    
    // draw small shell
    dc.Draw3dRect(SHELL_S_START_X, SHELL_S_START_Y, 
        m_uShellRcX[1], SHELL_S_H, m_clrDark, COLOR_WHITE);
    dc.Draw3dRect(SHELL_S_START_X + 1, SHELL_S_START_Y + 1, 
        m_uShellRcX[1] - 2, SHELL_S_H - 2, m_clrDark, COLOR_WHITE);
    
    // draw large shell
    dc.Draw3dRect(SHELL_L_START_X, SHELL_L_START_Y,
        m_uShellRcX[1], m_uShellRcY[1], m_clrDark, COLOR_WHITE);
    dc.Draw3dRect(SHELL_L_START_X + 1, SHELL_L_START_Y + 1,
        m_uShellRcX[1] - 2, m_uShellRcY[1] - 2, m_clrDark, COLOR_WHITE);
    dc.Draw3dRect(SHELL_L_START_X + 2, SHELL_L_START_Y + 2, 
        m_uShellRcX[1] - 4, m_uShellRcY[1] - 4, m_clrDark, COLOR_WHITE);

}*/

void CMyMine::DrawMineArea(CPaintDC &dc)
{
    CDC cdc;
    cdc.CreateCompatibleDC(&dc);
    cdc.SelectObject(m_bmpMine);
    
    for (UINT i = 0; i<m_uYNum; i++) {
        for (UINT j = 0; j<m_uXNum; j++) {
            dc.StretchBlt(MINEAREA_FRAME_X + 16 * j, MINEAREA_FRAME_Y + 16 * i, 
                16, 16, &cdc, 0, 16 * m_pMines[i][j].uState, 16, 16, SRCCOPY);
        }
    }
}

void CMyMine::DrawDownNum(MINEWND* mine, UINT num)
{
    mine->uState = 15 - num;
    mine->uOldState = 15 - num;
    CRect rcMine(mine->uCol * 16, mine->uRow * 16, (mine->uCol+1) *16, (mine->uRow+1) * 16);
    InvalidateRect(rcMine);
}

void CMyMine::DrawSpecialMine(UINT row, UINT col)
{
    CRect rcMine(col * 16, row * 16, (col+1) * 16, (row+1) * 16);
    InvalidateRect(rcMine);
}

五、玩家输入模块的设计与实现

在扫雷游戏中,用的最多的就是鼠标的输入,而鼠标输入又分为鼠标左键单击和右键单击处理两种了类型

void CMyMine::OnLButtonDown(UINT nFlags, CPoint point) 
{
    CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
    CRect rcMineArea(MINE_AREA_LEFT, MINE_AREA_TOP, 
        MINE_AREA_LEFT + m_uXNum * MINE_WIDTH, MINE_AREA_TOP + m_uYNum * MINE_HEIGHT);
    
    SetCapture();            // capture the mouse cursor
    m_bClickBtn = FALSE;
    m_bLRBtnDown = FALSE;
    if (rcBtn.PtInRect(point)) {                    // click in the button area
        m_bClickBtn = TRUE;
        m_uBtnState = BS_DOWN;
        InvalidateRect(rcBtn);
    }
    else if (rcMineArea.PtInRect(point)) {            // click in the mine area
        // change mine state by gamestate
        switch(m_uGameState) {                        
        case GS_WAIT: 
        case GS_RUN:
            m_pNewMine = GetMine(point.x, point.y);
            if (!m_pNewMine) return;
            if (m_pNewMine->uState == STATE_NORMAL) {
                m_pNewMine->uState = STATE_EMPTY;
            }
            if (m_pNewMine->uState == STATE_DICEY) {
                m_pNewMine->uState = STATE_DICEY_DOWN;
            }
            m_pOldMine = m_pNewMine;
            break;
        case GS_DEAD: 
        case GS_VICTORY:
            return;
        }
        m_uBtnState = BS_CLICK;
        InvalidateRect(rcBtn);
        // both of the left button and the right button are pushing down 
        if (nFlags == (MK_LBUTTON | MK_RBUTTON)) {
            m_bLRBtnDown = TRUE;
            OnLRBtnDown(m_pOldMine->uRow, m_pOldMine->uCol);
        }
        InvalidateRect(rcMineArea);
    }
    else {                                            // click in other area
        if (m_uGameState == GS_WAIT || m_uGameState == GS_RUN) {
            m_uBtnState = BS_CLICK;
            InvalidateRect(rcBtn);
        }
    }

    CWnd::OnLButtonDown(nFlags, point);
}

void CMyMine::OnLButtonUp(UINT nFlags, CPoint point) 
{
    CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
    CRect rcMineArea(MINE_AREA_LEFT, MINE_AREA_TOP, 
        MINE_AREA_LEFT + m_uXNum * MINE_WIDTH, MINE_AREA_TOP + m_uYNum * MINE_HEIGHT);
    
    if (rcBtn.PtInRect(point)) {                    // click in the button area
        if (m_bClickBtn) {
            Invalidate();
            InitGame();
        }
        else {
            if (m_uGameState == GS_WAIT || m_uGameState == GS_RUN) {
                m_uBtnState = BS_NORMAL;
                InvalidateRect(rcBtn);
            }
        }
    }
    else if (rcMineArea.PtInRect(point)) {            // click in the mine area
        CString value;
        // different process with different gamestate
        UINT around = 0;
        switch(m_uGameState) {
        case GS_WAIT: case GS_RUN:
            // first get the MINEWND which if pushing down
            m_pOldMine = GetMine(point.x, point.y);
            if (!m_pOldMine) {
                ReleaseCapture();
                return;
            }
            // do normal process
            // judge whether the lr button are both pushed down
            if (m_bLRBtnDown) {
                m_bLRBtnDown = FALSE;
                OnLRBtnUp(m_pOldMine->uRow, m_pOldMine->uCol);
                if (m_uGameState == GS_WAIT) {
                    m_uBtnState = BS_NORMAL;
                    Invalidate();
                    ReleaseCapture();
                    return;
                }
                // if the around flags number equal to the around mines number, expand.
                if (m_pOldMine->uState != STATE_FLAG) {
                    OpenAround(m_pOldMine->uRow, m_pOldMine->uCol);
                }
                // check whether the MINEWND around the special MINEWND is a mine, if it is then dead.
                if (ErrorAroundFlag(m_pOldMine->uRow, m_pOldMine->uCol)) {
//                    Dead(m_pOldMine->uRow, m_pOldMine->uCol);
                    ReleaseCapture();
                    return;
                }
            }
            else {
                WritePrivateProfileSection("ERROR", "", "E:\\log.txt");
                // start the game, init the mines area
                if (m_uGameState == GS_WAIT) {
                    if (m_uTimer) {
                        KillTimer(ID_TIMER_EVENT);
                        m_uTimer = 0;
                    }
                    // the following five lines refresh the remining mine num rect immediately 
                    // when click in the mine area at the first time
                    m_uSpendTime = 1;
                    Invalidate();
                    m_uTimer = SetTimer(ID_TIMER_EVENT, 1000, NULL);
                    LayMines(m_pOldMine->uRow, m_pOldMine->uCol);        // lay all the mines down 
                    m_uGameState = GS_RUN;
                }
                if (m_pOldMine->uOldState == STATE_NORMAL) {
                    // out log
                    value.Format("%d", m_pOldMine);
                    WritePrivateProfileString("ERROR", "ADD", value, "E:\\log.txt");
                    value.Format("%d", m_pOldMine->uRow);
                    WritePrivateProfileString("ERROR", "ROW", value, "E:\\log.txt");
                    value.Format("%d", m_pOldMine->uCol);
                    WritePrivateProfileString("ERROR", "COL", value, "E:\\log.txt");
                    // end
                    // first judge if the special MINEWND is a mine
                    if (IsMine(m_pOldMine->uRow, m_pOldMine->uCol)) {
                        Dead(m_pOldMine->uRow, m_pOldMine->uCol);
                        ReleaseCapture();
                        return;
                    }
                    // the special MINEWND is not a mine 
                    around = GetAroundNum(m_pOldMine->uRow, m_pOldMine->uCol);
                    // out log
                    value.Format("%d", around);
                    WritePrivateProfileString("ERROR", "AROUND", value, "E:\\log.txt");
                    // end
                    if (around == 0) ExpandMines(m_pOldMine->uRow, m_pOldMine->uCol);
                    else DrawDownNum(m_pOldMine, around);
                }
                else if (m_pOldMine->uOldState == STATE_DICEY) {
                    m_pOldMine->uState = STATE_DICEY;
                }
                if (Victory()) {
                    Invalidate();
                    ReleaseCapture();
                    return;
                }
            }
            break;
        case GS_VICTORY: case GS_DEAD:
            ReleaseCapture();        // release the cursor
            return;
        }
        m_uBtnState = BS_NORMAL;
        Invalidate();
    }
    else {                                            // click in other area
        if (m_uGameState == GS_WAIT || m_uGameState == GS_RUN) {
            m_uBtnState = BS_NORMAL;
            InvalidateRect(rcBtn);
        }
    }
    
    ReleaseCapture();        // release the cursor
    CWnd::OnLButtonUp(nFlags, point);
}

void CMyMine::OnRButtonDown(UINT nFlags, CPoint point) 
{
    CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
    CRect rcMineArea(MINE_AREA_LEFT, MINE_AREA_TOP, 
        MINE_AREA_LEFT + m_uXNum * MINE_WIDTH, MINE_AREA_TOP + m_uYNum * MINE_HEIGHT);
    
    m_bLRBtnDown = FALSE;
    if (rcMineArea.PtInRect(point)) {                // point in mine area
        if (m_uGameState == GS_WAIT || m_uGameState == GS_RUN) {
            m_pNewMine = GetMine(point.x, point.y);
            if (!m_pNewMine) return;
            // both of the left button and the right button are pushing down 
            if (nFlags == (MK_LBUTTON | MK_RBUTTON)) {
                m_bLRBtnDown = TRUE;
                OnLRBtnDown(m_pNewMine->uRow, m_pNewMine->uCol);
            }
            else {
                switch(m_pNewMine->uState) {
                case STATE_NORMAL:
                    m_pNewMine->uState = STATE_FLAG;
                    m_pNewMine->uOldState = STATE_FLAG;
                    m_nLeaveNum--;
                    break;
                case STATE_FLAG:
                    m_pNewMine->uState = STATE_DICEY;
                    m_pNewMine->uOldState = STATE_DICEY;
                    m_nLeaveNum++;
                    break;
                case STATE_DICEY:
                    m_pNewMine->uState = STATE_NORMAL;
                    m_pNewMine->uOldState = STATE_NORMAL;
                    break;
                }
            }
            Invalidate();
            // check if victory
//            Victory();
        }
    }
    
    CWnd::OnRButtonDown(nFlags, point);
}

void CMyMine::OnRButtonUp(UINT nFlags, CPoint point) 
{
    CRect rcBtn(m_uBtnRect[1], 15, m_uBtnRect[2], 39);
    CRect rcMineArea(MINE_AREA_LEFT, MINE_AREA_TOP, 
        MINE_AREA_LEFT + m_uXNum * MINE_WIDTH, MINE_AREA_TOP + m_uYNum * MINE_HEIGHT);
    
    m_pOldMine = GetMine(point.x, point.y);
    if (!m_pOldMine) return;
    // judge whether the lr button are both pushed down
    if (m_bLRBtnDown) {
        m_bLRBtnDown = FALSE;
        OnLRBtnUp(m_pOldMine->uRow, m_pOldMine->uCol);
        if (m_uGameState == GS_WAIT) {
            m_uBtnState = BS_NORMAL;
            Invalidate();
            return;
        }
        // if the around flags number equal to the around mines number, expand.
        if (m_pOldMine->uState != STATE_FLAG) {
            OpenAround(m_pOldMine->uRow, m_pOldMine->uCol);
        }
        // check whether the MINEWND around the special MINEWND is a mine, if it is then dead.
        if (ErrorAroundFlag(m_pOldMine->uRow, m_pOldMine->uCol)) {
//            Dead(m_pOldMine->uRow, m_pOldMine->uCol);
//            ReleaseCapture();
            return;
        }
    }
    else {
        Victory();
    }
                
    CWnd::OnRButtonUp(nFlags, point);
}

创作不易 觉得有帮助请点赞关注收藏~~~

标签: 游戏 c++ 算法

本文转载自: https://blog.csdn.net/jiebaoshayebuhui/article/details/129462112
版权归原作者 showswoller 所有, 如有侵权,请联系我们删除。

“Visual C++扫雷游戏实战三:核心算法设计与实现(附源码和资源)”的评论:

还没有评论