/*---------------------------------------- MynSweet.c an emulation of the venerable Minesweeper program. Written by Keith Oxenrider 03/13/2004 Public Domain Software, use at your own risk ----------------------------------------*/ #include #include #include #include "resource.h" enum { enuBomb, enuOne, enuTwo, enuThree, enuFour, enuFive, enuSix, enuSeven, enuEight, enuBlank, enuUP, enuFlag, enuMissed, enuLAST }; struct MINELOCDATA{ int bitMap; int count; int isMine; int isMarked; int isExposed; int isDown; }; struct MINELOCDATA *m_mldarrMines, **m_pmldMines; HBITMAP m_hwndBitMapArr[enuLAST]; HWND m_btnNew, m_lblMines, m_lblTimer; int m_intPossibleMines, m_intMines, m_intMinesLeft; int m_intRows, m_intCols, m_intSeconds; int m_boolGameOver, m_boolIsGameRunning; const int c_TOP_SIZE = 30; const int c_BORDER_SIZE = 5; void Init(int rows, int cols, int mines); void freeMem(); void ClearArround(int row, int col, BOOLEAN ClearMarkedMines); void setMineState(int row, int col, BOOLEAN ClearMarkedMines); BOOLEAN checkWin(); LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName [] = TEXT ("MynSweet") ; HWND hwnd ; MSG msg ; WNDCLASS wndclass ; HMENU hMenu; wndclass.style = CS_HREDRAW | CS_VREDRAW ; wndclass.lpfnWndProc = WndProc ; wndclass.cbClsExtra = 0 ; wndclass.cbWndExtra = 0 ; wndclass.hInstance = hInstance ; wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ; wndclass.lpszClassName = szAppName ; if (!RegisterClass (&wndclass)) { MessageBox (NULL, TEXT ("This program requires Windows NT!"), szAppName, MB_ICONERROR) ; return 0 ; } hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1)); hwnd = CreateWindow (szAppName, TEXT ("MynSweet"), WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_BORDER|WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, hMenu, hInstance, NULL) ; ShowWindow (hwnd, iCmdShow) ; UpdateWindow (hwnd) ; while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } return msg.wParam ; } LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static int cxClient, cyClient, cxSource, cySource, cxWindow, cyWindow ; BITMAP bitmap ; HDC hdc, hdcMem ; HINSTANCE hInstance ; int i, x, y, row, col, mineCnt; int lastRow=0, lastCol=0; PAINTSTRUCT ps ; POINTS pts; static int cxChar, cyChar; TCHAR szBuffer[10] ; RECT rect ; HBRUSH hBrush ; switch (message) { case WM_CREATE: hInstance = ((LPCREATESTRUCT) lParam)->hInstance ; /* I don't know why it insists on having the quotes */ m_hwndBitMapArr[enuBomb] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_bmb")) ; m_hwndBitMapArr[enuOne] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_1")) ; m_hwndBitMapArr[enuTwo] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_2")) ; m_hwndBitMapArr[enuThree] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_3")) ; m_hwndBitMapArr[enuFour] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_4")) ; m_hwndBitMapArr[enuFive] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_5")) ; m_hwndBitMapArr[enuSix] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_6")) ; m_hwndBitMapArr[enuSeven] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_7")) ; m_hwndBitMapArr[enuEight] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_8")) ; m_hwndBitMapArr[enuBlank] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_blnk")) ; m_hwndBitMapArr[enuUP] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_BUP")) ; m_hwndBitMapArr[enuFlag] = LoadBitmap (hInstance, TEXT("IDB_BITMAP_flg")) ; m_hwndBitMapArr[enuMissed]= LoadBitmap (hInstance, TEXT("IDB_BITMAP_msd")) ; GetObject (m_hwndBitMapArr[0], sizeof (BITMAP), &bitmap) ; cxSource = bitmap.bmWidth ; cySource = bitmap.bmHeight ; cxChar = LOWORD (GetDialogBaseUnits()); cyChar = HIWORD (GetDialogBaseUnits()); m_btnNew = CreateWindow ( TEXT("button"), TEXT("NEW"), WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0, 0, 6 * cxChar, 6 * cyChar / 4, hwnd, (HMENU) ID_BTN_NEW, hInstance, NULL) ; m_lblMines = CreateWindow ( TEXT("static"), TEXT("mines"), WS_CHILD | WS_VISIBLE | SS_CENTER | WS_BORDER, 0, 0, 6 * cxChar, 6 * cyChar / 4, hwnd, (HMENU) ID_LBL_MINES, hInstance, NULL) ; m_lblTimer = CreateWindow ( TEXT("static"), TEXT("000"), WS_CHILD | WS_VISIBLE | SS_CENTER | WS_BORDER, 0, 0, 6 * cxChar, 6 * cyChar / 4, hwnd, (HMENU) ID_LBL_TIMER, hInstance, NULL) ; Init(9,9,10); SetTimer(hwnd, 1, 1000, 0); return 0 ; case WM_TIMER: if (m_boolIsGameRunning){ wsprintf (szBuffer, TEXT ("%03d"), m_intSeconds++) ; SetWindowText (m_lblTimer, szBuffer) ; } return 0; case WM_MOVE: cxWindow = LOWORD (lParam) ; cyWindow = HIWORD (lParam) ; return 0 ; case WM_SIZE: cxClient = LOWORD (lParam) ; cyClient = HIWORD (lParam) ; MoveWindow(m_btnNew, (cxClient / 2) - (3 * cxChar), 0, 6 * cxChar, 6 * cyChar / 4, TRUE); MoveWindow(m_lblTimer, (cxClient) - (6 * cxChar), 0, 6 * cxChar, 6 * cyChar / 4, TRUE); return 0 ; case WM_RBUTTONDOWN: case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: /* haven't sorted this out yet */ return 0; case WM_RBUTTONUP: case WM_LBUTTONUP: case WM_MBUTTONUP: if (!m_boolGameOver){ m_boolIsGameRunning = 1; pts = MAKEPOINTS(lParam); row = ((pts.y - c_TOP_SIZE) / cySource) + 1; col = ((pts.x - c_BORDER_SIZE) / cxSource) + 1; if (row <= m_intRows && col <= m_intCols) { if (message == WM_RBUTTONUP){ if (!m_pmldMines[row][col].isExposed){ if (m_pmldMines[row][col].isMarked){ m_intMinesLeft++; m_pmldMines[row][col].isMarked = 0; }else{ m_intMinesLeft--; m_pmldMines[row][col].isMarked = 1; } wsprintf (szBuffer, TEXT ("%d"), m_intMinesLeft) ; SetWindowText (m_lblMines, szBuffer) ; if (m_intMinesLeft == 0){ if (checkWin()){ m_boolIsGameRunning = 0; m_boolGameOver = 1; SetWindowText (m_btnNew, TEXT("WIN!")) ; } } } }else if (message == WM_MBUTTONUP){ if (m_pmldMines[row][col].isExposed){ mineCnt=0; if (m_pmldMines[row ][col+1].isMarked) mineCnt++; if (m_pmldMines[row ][col-1].isMarked) mineCnt++; if (m_pmldMines[row-1][col+1].isMarked) mineCnt++; if (m_pmldMines[row-1][col ].isMarked) mineCnt++; if (m_pmldMines[row-1][col-1].isMarked) mineCnt++; if (m_pmldMines[row+1][col+1].isMarked) mineCnt++; if (m_pmldMines[row+1][col ].isMarked) mineCnt++; if (m_pmldMines[row+1][col-1].isMarked) mineCnt++; if (mineCnt == m_pmldMines[row][col].count){ setMineState(row , col+1, TRUE); setMineState(row , col-1, TRUE); setMineState(row-1, col+1, TRUE); setMineState(row-1, col , TRUE); setMineState(row-1, col-1, TRUE); setMineState(row+1, col+1, TRUE); setMineState(row+1, col , TRUE); setMineState(row+1, col-1, TRUE); } } }else{ if (!m_pmldMines[row][col].isMarked) setMineState(row, col, FALSE); } } } InvalidateRect(hwnd, NULL, TRUE); return 1 ; case WM_PAINT: hdc = BeginPaint (hwnd, &ps) ; hdcMem = CreateCompatibleDC (hdc) ; /* set background color */ rect.left = 0 ; rect.top = 0 ; rect.right = cxClient ; rect.bottom = cyClient ; hBrush = CreateSolidBrush (RGB (192,192,192)) ; FillRect (hdc, &rect, hBrush) ; DeleteObject (hBrush) ; /* set mine bitmaps */ for (y=1; y<=m_intRows; y++) { for (x=1; x<=m_intCols; x++) { if (m_pmldMines[y][x].isExposed || m_pmldMines[y][x].isMarked){ if (m_pmldMines[y][x].isMine && !m_pmldMines[y][x].isMarked){ SelectObject (hdcMem, m_hwndBitMapArr[enuBomb]) ; }else if (m_pmldMines[y][x].isMarked){ SelectObject (hdcMem, m_hwndBitMapArr[enuFlag]) ; }else{ if (m_pmldMines[y][x].count){ SelectObject (hdcMem, m_hwndBitMapArr[m_pmldMines[y][x].count]) ; }else{ SelectObject (hdcMem, m_hwndBitMapArr[enuBlank]) ; } } }else{ SelectObject (hdcMem, m_hwndBitMapArr[enuUP]) ; } BitBlt (hdc, ((x-1)*cxSource)+c_BORDER_SIZE, ((y-1)*cySource)+c_TOP_SIZE, cxSource, cySource, hdcMem, 0, 0, SRCCOPY) ; } } DeleteDC (hdcMem) ; EndPaint (hwnd, &ps) ; return 1 ; case WM_COMMAND : switch (wParam){ case ID_MNU_BEG: Init(9,9,10); break; case ID_MNU_INTER: Init(16,16,40); break; case ID_MNU_EXP: Init(16,30,99); break; case ID_BTN_NEW: case ID_MNU_NEW: Init(m_intRows,m_intCols,m_intMines); break; case ID_MNU_EXIT: SendMessage(hwnd, WM_DESTROY, 0, 0); break; } MoveWindow(hwnd, cxWindow-3, cyWindow-41, /* this extra bit is for borders */ (c_BORDER_SIZE*4)+(cySource * m_intCols), (c_BORDER_SIZE*4) + (c_TOP_SIZE*2) + (cxSource * m_intRows), TRUE); InvalidateRect(hwnd, NULL, TRUE); return 1; case WM_DESTROY: freeMem(); for (i=0; im_intCols || row>m_intRows) return; if (m_pmldMines[row][col].isExposed) return; m_pmldMines[row][col].isExposed = 1; if (m_pmldMines[row][col].isMine && !ClearMarkedMines){ m_boolGameOver=1; m_boolIsGameRunning = 0; SetWindowText (m_btnNew, TEXT("DED!")) ; for (i=1; i<=m_intRows; i++){ for (j=1; j<=m_intCols; j++){ m_pmldMines[i][j].isExposed = 1; } } }else{ if (m_pmldMines[row][col].count == 0){ ClearArround(row, col, ClearMarkedMines); } } } BOOLEAN checkWin(){ int i,j,cnt=0; for (i=1; i<=m_intRows; i++){ for (j=1; j<=m_intCols; j++){ if (m_pmldMines[i][j].isMine && m_pmldMines[i][j].isMarked){ cnt++; } } } if (cnt == m_intMines) return TRUE; return FALSE; } void Init(int rows, int cols, int mines) { int i,j,x,y, mineCount=0, surroundCnt; TCHAR szBuffer[10] ; freeMem(); m_boolGameOver = 0; m_intSeconds = 0; m_boolIsGameRunning = 0; srand(time(NULL)); SetWindowText (m_btnNew, TEXT("NEW")) ; /* just a little sanity checking */ if (rows < 9 || cols < 9) return; if (rows > 100 || cols > 100) return; if (mines < 10 || mines > 100) return; m_intRows = rows; m_intCols = cols; m_intMinesLeft = m_intMines = mines; SetWindowText (m_lblTimer, TEXT("000")) ; wsprintf (szBuffer, TEXT ("%d"), m_intMinesLeft) ; SetWindowText (m_lblMines, szBuffer) ; m_intPossibleMines = m_intRows*m_intCols; /* this '+2' biznez is to leave a buffer zone to GREATLY simply checking for mine status */ m_mldarrMines = (struct MINELOCDATA *) calloc((m_intRows+2)*(m_intCols+2), sizeof(struct MINELOCDATA)); m_pmldMines = (struct MINELOCDATA **) calloc(m_intRows+2, sizeof(struct MINELOCDATA *)); for (i=0; i