Click here to Skip to main content
15,886,362 members
Articles / Programming Languages / C

Adapted WinMine Source for Teaching Win32 API Programming

Rate me:
Please Sign up or sign in to vote.
4.82/5 (19 votes)
21 Apr 2011CPOL5 min read 35.2K   1.2K   27  
An ideal source package for introducing students to the basics of Win32 programming
/*===========================================================================
  This is the fifth step toward building the WinMine application
  Adapted for educational purpose by Yiping Cheng, 
  Beijing Jiaotong University, Mar. 2011 <ypcheng@bjtu.edu.cn>
  based on the WinMine program written by the ReactOS team

  Original Copyright Notice:

 * WineMine (main.c)
 *
 * Copyright 2000 Joshua Thielen <jt85296@ltu.edu>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
---------------------------------------------------------------------------*/

#include <assert.h>
#include <windowsx.h>
#include "resource.h"
#include "WinMine.h"

// the only global variable
extern BOARD board;

void TranslateMouseMsg(UINT* puMsg, WPARAM wParam)
{
    if (*puMsg == WM_LBUTTONDOWN)
        SetCapture(board.hWnd);
    else if (*puMsg == WM_LBUTTONUP)
        ReleaseCapture();
    else if ((*puMsg == WM_MOUSEMOVE) && (wParam & MK_LBUTTON))
    {
        *puMsg = WM_LBUTTONDOWN;
    }
}

void ProcessMouseMsg(UINT uMsg, LPARAM lParam)
{
    POINT pt;
    int row, col;

    pt.x = GET_X_LPARAM(lParam);
    pt.y = GET_Y_LPARAM(lParam);

    if (PtInRect(&board.FaceRect, pt))
    {
        if (uMsg == WM_LBUTTONDOWN)
        {
            DisplayFace(FACE_DOWN);
        }
        else if (uMsg == WM_LBUTTONUP)
        {
            NewGame();
        }
        return;
    }

    if (board.GameState == GAMEWON)
    {
        DisplayFace(FACE_WIN);
        return;
    }
    else if (board.GameState == GAMELOST)
    {
        DisplayFace(FACE_LOSE);
        return;
    }

    Pt2RowCol(pt, &row, &col);

    if (uMsg == WM_LBUTTONDOWN)
    {
        DisplayFace(FACE_CAUTION);

        if (row != board.rowPressed || col != board.colPressed)
        {
            UnPressBox();
            PressBox(row, col);
        }
    }
    else
    {
        UnPressBox();
        if (row < 0)
        {
            // row<0 means we clicked outside the grid
            DisplayFace(FACE_HAPPY);
            return;
        }

        if (uMsg == WM_LBUTTONUP)
        {
            if (board.GameState == WAITING)
            { 
                LayMines(row, col);
                board.GameState = PLAYING;
            }

            if (StepBox(row, col))
            {
                if (board.uSteps == board.uMaxSteps)
                    GameWon();
                else
                    DisplayFace(FACE_HAPPY);
            }
            else
                GameLost();
        }
        else if (uMsg == WM_RBUTTONUP && board.GameState != WAITING)
        {
            if (board.Box[row][col].State == BS_DICEY)
            {
                SetAndDispBoxState(row, col, BS_INITIAL);
            }
            else if (board.Box[row][col].State == BS_FLAG)
            {
                SetAndDispBoxState(row, col, board.bMark? BS_DICEY : BS_INITIAL);
                IncMinesLeft();
            }
            else if (board.Box[row][col].State == BS_INITIAL
                && (board.uMinesLeft != 0))
            {
                SetAndDispBoxState(row, col, BS_FLAG);
                DecMinesLeft();
            }
        }
    }
}


void Pt2RowCol(POINT pt, int *prow, int *pcol)
{
    if (PtInRect(&board.GridRect, pt))
    {
        *prow = (pt.y - board.GridRect.top) / BOX_HEIGHT;
        *pcol = (pt.x - board.GridRect.left) / BOX_WIDTH;
    }
    else
    {
        *prow = -1;
        *pcol = -1;
    }
}

void PressBox(int row, int col)
{
    if ( (row >= 0) && 
        (board.Box[row][col].State == BS_INITIAL ||
        board.Box[row][col].State == BS_DICEY))
    {
        assert(col >= 0);

        board.rowPressed = row;
        board.colPressed = col;
        
        SetAndDispBoxState(row, col,
            (board.Box[row][col].State == BS_INITIAL)? BS_DOWN: BS_DICEY_DOWN);
    }
}


void UnPressBox()
{
    if (board.rowPressed >= 0)
    {
        BOX_STATE state = board.Box[board.rowPressed][board.colPressed].State;
        assert(state == BS_DOWN || state == BS_DICEY_DOWN);

        SetAndDispBoxState(board.rowPressed, board.colPressed, 
            (state == BS_DOWN)? BS_INITIAL : BS_DICEY);

        board.rowPressed = -1; 
        board.colPressed = -1;
    }
}

void LayMines(int row, int col)
{
    UINT cMines = 0;
    UINT r, c;

    // temporarily mark this box as mine
    board.Box[row][col].fMine = TRUE;
    srand(GetTickCount());

    while (cMines < board.uMines)
    {
        r = ((UINT) rand()) % board.uRows;
        c = ((UINT) rand()) % board.uCols;

        if (!board.Box[r][c].fMine)
        {
            board.Box[r][c].fMine = TRUE;
            cMines++;
        }
    }

    // remove the mine attribute of the box
    board.Box[row][col].fMine = FALSE;
}

void GameWon()
{
    UINT r, c;

    board.GameState = GAMEWON;
    ZeroMinesLeft();
    DisplayFace(FACE_WIN);

    for (r = 0; r < board.uRows; r++)
        for (c = 0; c < board.uCols; c++)
        {
            if (board.Box[r][c].fMine && board.Box[r][c].State != BS_FLAG)
            {
                SetAndDispBoxState(r, c, BS_FLAG);
            }
        }
        
}

void GameLost()
{
    UINT r, c;

    board.GameState = GAMELOST;
    DisplayFace(FACE_LOSE);

    for (r = 0; r < board.uRows; r++)
        for (c = 0; c < board.uCols; c++)
        {
            if (board.Box[r][c].fMine && board.Box[r][c].State != BS_BLAST)
            {
                SetAndDispBoxState(r, c, BS_MINE);
            }
            else if (board.Box[r][c].State == BS_FLAG)
            {
                SetAndDispBoxState(r, c, BS_WRONG);
            }
        }
}


#define WITHIN_GRID(r, c) \
    ( (UINT) r < board.uRows && (UINT) c < board.uCols )

// Count the mines surrounding the box
UINT CountMines(int row, int col)
{
    UINT cMines = 0;
    int r, c;

    for (r = row-1; r <= row+1; r++)
        for (c = col-1; c <= col+1; c++)
        {
            if (WITHIN_GRID(r, c) && (r != row || c != col) && 
                board.Box[r][c].fMine)
            {
                cMines++;
            }
        }

    return cMines;
}

// steps on this box, return value indicates if it is safe
BOOL StepBox(int row, int col)
{
    UINT cMinesSurround;

    if (board.Box[row][col].State != BS_INITIAL && 
        board.Box[row][col].State != BS_DICEY)
    {
        // previously stepped, must be safe, and no need to step second time
        return TRUE;
    }

    if (board.Box[row][col].fMine)
    {
        // stepped on a mine!
        SetAndDispBoxState(row, col, BS_BLAST);
        return FALSE;
    }

    board.uSteps++;
    cMinesSurround = CountMines(row, col);
    SetAndDispBoxState(row, col, BS_DOWN - cMinesSurround);

    if (cMinesSurround == 0)
    {
        int r, c;

        for (r = row-1; r <= row+1; r++)
            for (c = col-1; c <= col+1; c++)
            {
                if (WITHIN_GRID(r, c) && (r != row || c != col))
                {
                    StepBox(r, c);
                }
            }
    }

    return TRUE;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
China China
Yiping Cheng is both a software developer and a researcher. He has been programming in C/C++ for over 15 years. His favorite platform is Windows and he loves using MFC to build full-fledged Windows applications. When he is not programming or researching, he enjoys listening to traditional opera and music.

Comments and Discussions