Category : Windows 3.X Files
Archive   : MM.ZIP
Filename : MM.C

 
Output of file : MM.C contained in archive : MM.ZIP

/*
MM.C
The game MasterMind for MS-Windows

Things still to add/fix:
Have each game have a 'board number'. User can select a specific board
number in Options dialog box, or select that a random board number will
be chosen. Internally, the board number will be used as the seed for the
random numbers chosen for the answer (the answer is set in NewGame).
There are too many lines (moves) shown when repainting after user
plays with scrollbar - as opposed to hitting Score button. Would like
it to always look like it does when score button hit.
Enhancement: Point and drag to select a row of pegs (esp nice when working
with 6 or more pegs!)
*/

#include "windows.h"

#define extern // this will actually allocate space for all the
#include "mm.h" // stuff in mm.h (it strips off the 'extern' keywords)
#undef extern

#include "rtns.h"

#define MM_SCORE WM_USER+0




/********************
WinMain
********************/
int PASCAL WinMain (HANDLE hInstance,
HANDLE hPrevInstance,
LPSTR lpszCmdLine,
int cmdShow)
{
MSG msg;
int i,j,index;
RECT Rect;
HMENU hSysMenu;

if (!hPrevInstance)
{
WNDCLASS rClass;

hBkgndBrush = CreateSolidBrush (GetSysColor(COLOR_WINDOW));

rClass.lpszClassName = "MASTERMIND_CLASS";
rClass.hInstance = hInstance;
rClass.lpfnWndProc = MasterMindWinProc;
rClass.hCursor = LoadCursor(NULL, IDC_ARROW);
rClass.hIcon = NULL; /* I'll paint my own */
rClass.lpszMenuName = "MasterMindMenu";
rClass.hbrBackground = hBkgndBrush;
rClass.style = CS_HREDRAW|CS_VREDRAW;
rClass.cbClsExtra = 0;
rClass.cbWndExtra = 0;
RegisterClass((LPWNDCLASS)&rClass);

rClass.lpszClassName = "MMPEGSELECTOR_CLASS";
rClass.hInstance = hInstance;
rClass.lpfnWndProc = PegSelectorWinProc;
rClass.hCursor = LoadCursor(NULL, IDC_ARROW);
rClass.hIcon = NULL;
rClass.lpszMenuName = NULL;
rClass.hbrBackground = GetStockObject(WHITE_BRUSH);
rClass.style = CS_HREDRAW|CS_VREDRAW;
rClass.cbClsExtra = 0;
rClass.cbWndExtra = 0;
RegisterClass((LPWNDCLASS)&rClass);

rClass.lpszClassName = "MMMOVES_CLASS";
rClass.hInstance = hInstance;
rClass.lpfnWndProc = MovesWinProc;
rClass.hCursor = LoadCursor(NULL, IDC_ARROW);
rClass.hIcon = NULL;
rClass.lpszMenuName = NULL;
rClass.hbrBackground = hBkgndBrush;
rClass.style = CS_HREDRAW|CS_VREDRAW;
rClass.cbClsExtra = 0;
rClass.cbWndExtra = 0;
RegisterClass((LPWNDCLASS)&rClass);
}

hInst = hInstance;

/* Create the windows:
hMasterMindWnd, hPegSelectorWnd[], hScoreButtonWnd, and hMovesWnd
*/
NumMoves=0;
LineHeight=10;
if (!MMCreateWindows(hInst,cmdShow))
{
MessageBox (NULL, "Can't create windows", "MasterMind", MB_OK);
exit(0);
}

for (i=0; i NumPegs = 4;
NumPegTypes = 5;
MaxMoves = 15;
bAnswerPegsUnique = FALSE;
bIconic = (cmdShow==SW_MINIMIZE);
NewGame();

/* Add About... selection to system menu */
hSysMenu = GetSystemMenu (hMasterMindWnd, FALSE);
ChangeMenu (hSysMenu, 0, NULL, 999, MF_APPEND | MF_SEPARATOR);
ChangeMenu (hSysMenu, 0, "&About...", SYSMENU_ABOUT, MF_APPEND | MF_STRING);

hPegBrush[PEGTYPE_NULL] = GetStockObject (GRAY_BRUSH);
hPegBrush[PEGTYPE_NULL+1] = CreateSolidBrush (RGB(0xFF,0x00,0x00));
hPegBrush[PEGTYPE_NULL+2] = CreateSolidBrush (RGB(0x00,0xFF,0x00));
hPegBrush[PEGTYPE_NULL+3] = CreateSolidBrush (RGB(0x00,0x00,0xFF));
hPegBrush[PEGTYPE_NULL+4] = CreateSolidBrush (RGB(0xFF,0xFF,0x00));
hPegBrush[PEGTYPE_NULL+5] = CreateSolidBrush (RGB(0x00,0xFF,0xFF));
hPegBrush[PEGTYPE_NULL+6] = CreateSolidBrush (RGB(0xFF,0x00,0xFF));
hPegBrush[PEGTYPE_NULL+7] = CreateSolidBrush (RGB(0xFF,0xFF,0xFF));
hPegBrush[PEGTYPE_NULL+8] = CreateSolidBrush (RGB(0x00,0x00,0x00));
hPegBrush[PEGTYPE_NULL+9] = CreateSolidBrush (RGB(0xC0,0x90,0xFF));

ShowWindow (hMasterMindWnd, cmdShow);

while (GetMessage((LPMSG)&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

exit(msg.wParam);
}


/****************************************************************
MasterMindWinProc
there is one window of this class (hMasterMindWnd)
it is the main window of the app.
****************************************************************/
long FAR PASCAL MasterMindWinProc (HWND hWnd,
unsigned uMsgType,
WORD wParam,
LONG lParam)
{
FARPROC lpprocAbout;
HDC hDC;
PAINTSTRUCT ps;
int i, line;
char str[20];
HPEN hOldPen,hPen;
HBRUSH hOldBrush, hBrush;
RECT Rect;

switch (uMsgType)
{
case WM_SYSCOLORCHANGE:
{/* someone changed the system colors (e.g. with Control.exe)
So need to create a new background brush */
HBRUSH hOldBkgndBrush;
hBkgndBrush = CreateSolidBrush (GetSysColor(COLOR_WINDOW));
hOldBkgndBrush = SetClassWord (hWnd, GCW_HBRBACKGROUND, hBkgndBrush);
DeleteObject (hOldBkgndBrush);
}
break;


case WM_SYSCOMMAND:
switch (wParam)
{
case SYSMENU_ABOUT:
lpprocAbout = MakeProcInstance ((FARPROC)AboutBoxProc, hInst);
DialogBox (hInst, "ABOUTBOX", hWnd, lpprocAbout);
FreeProcInstance (lpprocAbout);
break;
default:
return(DefWindowProc(hWnd, uMsgType, wParam, lParam));
}
break;


case WM_PAINT:
if (bIconic)
{ /* paint our own icon, since it has pretty colors */
GetClientRect (hWnd, &Rect);
hDC = BeginPaint (hWnd, &ps);
Rectangle (hDC, 0,0, Rect.right,Rect.bottom);
PaintMMIcon (hDC, 1,1);
EndPaint (hWnd, &ps);
}
else
{
hDC = BeginPaint (hWnd, &ps);
DrawPegSelectorBorder (hWnd);
DrawAnswer (hWnd, bGameOver);
SetTextColor (hDC, GetSysColor(COLOR_WINDOWTEXT));
SetBkMode (hDC, TRANSPARENT);
MakeStr (str, "Num Moves:", NumMoves);
TextOut (hDC, rectGameInfo.left, rectGameInfo.top, str, lstrlen(str));
MakeStr (str, "(max ", MaxMoves);
lstrcat (str, ")");
TextOut (hDC, rectGameInfo.left, rectGameInfo.top+10, str, lstrlen(str));
EndPaint (hWnd, &ps);
}
break;


case MM_SCORE:
NumMoves++;
for (i=0; i PrevMoves[NumMoves-1][i] = PegSelection[i];
bGameWon = CalcScore (NumMoves-1);
InvalidateRect (hWnd, &rectGameInfo, TRUE);
SendMessage (hMovesWnd, MM_SCORE, (WORD)0, (LONG)0);
if (NumMoves==MaxMoves || bGameWon)
{
bGameOver = TRUE;
DrawAnswer (hWnd, bGameOver);
ShowWindow (hScoreButtonWnd, SW_HIDE);
}
else
return(DefWindowProc(hWnd, uMsgType, wParam, lParam));
break;


case WM_COMMAND:
switch (wParam)
{
case MMMENU_NEWGAME:
NewGame();
ShowScrollBar (hMovesWnd, SB_VERT, FALSE);
break;

case MMMENU_OPTIONS:
{
FARPROC lpfnMMDialog;
BOOL bRet;
lpfnMMDialog = MakeProcInstance (MMOptionsDialogProc, hInst);
bRet = DialogBox (hInst, "MasterMindDialog", hWnd, lpfnMMDialog);
FreeProcInstance (lpfnMMDialog);
if (bRet)
{
InvalidateRect (hMasterMindWnd, NULL, TRUE);
for (i=0; i if (PegSelection[i] >= NumPegTypes)
{
PegSelection[i] = PEGTYPE_NULL;
InvalidateRect (hPegSelectorWnd[i*MAX_PEGTYPES+PEGTYPE_NULL],
NULL, TRUE);
}
NewGame();
}
}
break;

case CHILID_SCOREBUTTON:
if (HIWORD(lParam)==BN_CLICKED)
{
SetFocus (hWnd);
PostMessage (hWnd, MM_SCORE, (WORD)0, (LONG)0);
}
break;
}
break;


/* FOR DEBUGGING USE - DISPLAYS THE ANSWER */
case WM_RBUTTONUP:
{
char s[50],s1[50];
lstrcpy (s1,"");
for (i=0; i {
MakeStr (s," ",Answer[i]);
lstrcat (s1,s);
}
MessageBox (hWnd, s1, "", MB_OK);
}
break;

case WM_SIZE:
if (wParam==SIZEICONIC)
bIconic = TRUE;
else
{
bIconic = FALSE;
GetClientRect (hWnd, &Rect);
rectMoves.bottom = Rect.bottom;
MoveWindow (hMovesWnd, rectMoves.left, rectMoves.top,
rectMoves.right-rectMoves.left, rectMoves.bottom-rectMoves.top, TRUE);
}
/* still have Windows process this message! */
return(DefWindowProc(hWnd, uMsgType, wParam, lParam));
break;


case WM_DESTROY:
DeleteObject (hBkgndBrush);
for (i=1; i DeleteObject (hPegBrush[i]);
PostQuitMessage(0);
break;


default:
return(DefWindowProc(hWnd, uMsgType, wParam, lParam));
break;
}

return(0L);
}


/****************************************************************
PegSelectorWinProc
There is an array of windows of this class (hPegSelectorWnd[])
The parent of all these windows is the main window (hMasterMindWnd).
Each window is one peg in the peg selection box.
When the window is clicked on, it figures out which peg it is;
position (which column) and peg type (which row). It uses this
information to update the global PegSelection[]. (when the user
presses the Score button, PegSelection[] is his guess).
Also updates the appearances of the old selection for this position
and this new selection.
****************************************************************/
long FAR PASCAL PegSelectorWinProc (HWND hWnd,
unsigned uMsgType,
WORD wParam,
LONG lParam)
{
HDC hDC;
PAINTSTRUCT ps;
RECT Rect;
int ChildId, PegType, Position, OldSelection;
HPEN hHighlightPen, hOldPen;
HBRUSH hOldBrush;

switch (uMsgType)
{
case WM_PAINT:
hDC = BeginPaint (hWnd, &ps);
GetClientRect (hWnd, &Rect);
GetPegSelectorInfo (hWnd, &ChildId, &PegType, &Position);
if (PegType == PegSelection[Position])
/* This peg is currently selected; make its appearance say so */
Rectangle (hDC, Rect.left, Rect.top, Rect.right-1, Rect.bottom-1);
DrawPeg (hDC, Rect.left+2, Rect.top+2, PegType);
EndPaint (hWnd, &ps);
break;

case WM_LBUTTONUP:
GetPegSelectorInfo (hWnd, &ChildId, &PegType, &Position);
OldSelection = Position*MAX_PEGTYPES + PegSelection[Position];
PegSelection[Position] = PegType;
InvalidateRect (hPegSelectorWnd[OldSelection], NULL, TRUE);
InvalidateRect (hPegSelectorWnd[ChildId], NULL, TRUE);
break;

default:
return(DefWindowProc(hWnd, uMsgType, wParam, lParam));
break;
}

return(0L);
}

/****************************************************************
MovesWinProc
There is one window of this class (hMovesWnd)
The parent for this window is the main window (hMasterMindWnd)
This window contains all of the scored moves (both the guesses and
the scores for the guesses). It is purely for displaying the
move information, so does not hit test.
If there are too many moves to fit, the scrollbar is displayed to
allow the user to look at any of his previous guesses.
****************************************************************/
long FAR PASCAL MovesWinProc (HWND hWnd,
unsigned uMsgType,
WORD wParam,
LONG lParam)
{
HDC hDC;
PAINTSTRUCT ps;
RECT Rect;
int i,line;
static int NumLinesPerPage=1; //num lines (of moves) that fit on window

switch (uMsgType)
{
case WM_PAINT:
hDC = BeginPaint (hWnd, &ps);
GetClientRect (hWnd, &Rect);
if (NumMoves > 0)
for (line=0,i=TopIndex; line=0; line++,i--)
DrawScoredMove (hDC, i, line);
EndPaint (hWnd, &ps);
break;

case MM_SCORE:
SendMessage (hWnd, WM_VSCROLL, SB_TOP, 0L); // make sure scrolled to top
TopIndex++; // scroll down to be ready for next move
if (NumMoves == NumLinesPerPage+1)
ShowScrollBar (hWnd, SB_VERT, TRUE);
if (NumMoves > NumLinesPerPage)
{
SetScrollRange (hWnd, SB_VERT, 0, NumMoves-1, FALSE);
SetScrollPos (hWnd, SB_VERT, NumMoves-1-TopIndex, TRUE);
}
InvalidateRect (hWnd, NULL, TRUE);
break;

case WM_SIZE:
GetClientRect (hWnd, &Rect);
NumLinesPerPage = Rect.bottom / LineHeight -1;
if (NumMoves > NumLinesPerPage)
{
SetScrollRange (hWnd, SB_VERT, 0, NumMoves-1, FALSE);
SetScrollPos (hWnd, SB_VERT, NumMoves-1-TopIndex, FALSE);
ShowScrollBar (hWnd, SB_VERT, TRUE);
}
else
/* the window is now large enough to show all the moves,
but if the user has it scrolled (so the most recent move is
not at the top) then we can't remove the scrollbar! */
if (TopIndex == NumMoves-1)
ShowScrollBar (hWnd, SB_VERT, FALSE);
break;

case WM_VSCROLL:
if (! ScrollMoves(hWnd, wParam, LOWORD(lParam), NumLinesPerPage))
return(DefWindowProc(hWnd, uMsgType, wParam, lParam));
break;

default:
return(DefWindowProc(hWnd, uMsgType, wParam, lParam));
break;
}

return(0L);
}

//------------------------------------
// GetPegSelectorInfo
//------------------------------------
void FAR PASCAL GetPegSelectorInfo (HWND hWnd,
LPINT lpChildId, LPINT lpPegType, LPINT lpPosition)
{
int ChildId;
ChildId = *lpChildId = GetWindowWord (hWnd, GWW_ID);
*lpPegType = ChildId % MAX_PEGTYPES;
*lpPosition = ChildId / MAX_PEGTYPES;
}


//----------------
// MakeStr
//----------------
void FAR PASCAL MakeStr (LPSTR szStr, LPSTR szLabel, int n)
{
char s[10];
lstrcpy (szStr, szLabel);
itoa (n, s, 10);
lstrcat (szStr, s);
}

//-----------------
// ScrollMoves
//-----------------
BOOL FAR PASCAL ScrollMoves (HWND hWnd,
WORD ScrollType, // the wParam from scroll msg
WORD ThumbPos, // comes in lParam for SB_THUMBPOS
int NumLinesPerPage)
{
int NewTopIndex=TopIndex;

switch (ScrollType)
{
case SB_LINEUP:
if (TopIndex < (NumMoves-1))
NewTopIndex++;
break;
case SB_PAGEUP:
if ((TopIndex+NumLinesPerPage) <= (NumMoves-1))
NewTopIndex += NumLinesPerPage;
else
NewTopIndex = NumMoves-1;
break;
case SB_TOP:
NewTopIndex = NumMoves-1;
break;
case SB_LINEDOWN:
if (TopIndex > 0)
NewTopIndex--;
break;
case SB_PAGEDOWN:
if (TopIndex-NumLinesPerPage >= 0)
NewTopIndex -= NumLinesPerPage;
break;
case SB_BOTTOM:
/* Note: scroll bar wouldn't be up unless NumMoves > NumLinesPerPage */
NewTopIndex = NumLinesPerPage-1;
break;
case SB_THUMBPOSITION:
/* user has dragged thumb to a specific position */
NewTopIndex = NumMoves-1- ThumbPos;
break;
default:
return (FALSE);
}

if (NewTopIndex != TopIndex)
{
TopIndex = NewTopIndex;
SetScrollPos (hMovesWnd, SB_VERT, NumMoves-1-TopIndex, TRUE);
InvalidateRect (hMovesWnd, NULL, TRUE);
}
return (TRUE);
}


/***********************************************************************
CalcScore

Score the user's guess. Each correct color peg in the correct position
earns a black score peg. Each correct color peg in the wrong position
earns a white peg.

Does not draw on the display, just figures the score and puts it into
the global array of scores (PrevScores).

From the score the user can't tell which pegs in his guess yielded
black or white score pegs (that would make it much too easy!).
So in the score all the black score pegs are listed first,
followed by all the white score pegs.

Parameters:
MoveIndex is used to index into the global array 'PrevMoves' (to find
the user's guess) and to index the global array 'PrevScores'
(to record the score for this guess)

Returns: T if the guess is exactly right (if the score is all black pegs)
F otherwise
***************************************************************************/
BOOL FAR PASCAL CalcScore (int MoveIndex)
{
int i,j;
int ScoreIndex;
BOOL AnsPegUsed[MAX_PEGS], MovePegUsed[MAX_PEGS];

for (i=0; i {
PrevScores[MoveIndex][i] = SCORE_NULL;
AnsPegUsed[i] = MovePegUsed[i] = FALSE;
}
ScoreIndex = 0;

for (i=0; i if (Answer[i]==PrevMoves[MoveIndex][i])
{
/* right-o Give the man a black score peg! */
PrevScores[MoveIndex][ScoreIndex++] = SCORE_CORRECTPOS;
MovePegUsed[i] = AnsPegUsed[i] = TRUE;
}
for (i=0; i for (j=0; j if (!AnsPegUsed[j] && PrevMoves[MoveIndex][i]==Answer[j])
{
/* close Give him a white score peg (right color, wrong position) */
PrevScores[MoveIndex][ScoreIndex++] = SCORE_CORRECTCOLOR;
MovePegUsed[i] = AnsPegUsed[j] = TRUE;
}

return (PrevScores[MoveIndex][NumPegs-1]==SCORE_CORRECTPOS);
}