WCHECK.CPP
/* -------------------------------------------------------------------------- 
 
Basic Forms example of a custom sendable form.  It is an EXE server 
rather than a DLL.  It implements the minimum form interface required 
to launch and send a form. 
 
Copyright (C) 1995 Microsoft Corporation 
 
-------------------------------------------------------------------------- */ 
 
#define WINDOWS 
 
#include <windows.h> 
#include <windowsx.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <process.h> 
#include <malloc.h> 
#include <memory.h> 
#include <conio.h> 
#include <time.h> 
#include <ctype.h> 
#include <search.h> 
 
#ifdef _WIN32 
#include <winver.h> 
#else 
#include <ver.h> 
#endif 
 
//$FORM 
 
//---[ Form specific includes ]-------------------------------------------- 
 
#include <ole2.h> 
#include <mapiform.h> 
#include <mapix.h> 
#include <mapiutil.h> 
 
//$FORM 
 
//---[ Various platform defines ]------------------------------------------ 
 
#ifndef APIENTRY                // Windows NT defines APIENTRY, 
#define APIENTRY far pascal             // but 3.x doesn't. 
#endif 
 
#ifndef _WIN32                   // Windows 3.x uses a FARPROC 
#define DLGPROC FARPROC                 // for dialogs 
#endif 
 
unsigned int _charmax; 
 
#ifdef _WIN32 
#define MoveTo(_1, _2, _3) MoveToEx(_1,_2,_3,NULL) 
#endif 
 
//---[ Define Debug Trace Tags ]------------------------------------------- 
 
#include "dbugit.h" 
 
#ifdef DEBUG 
ASSERTDATA 
#endif 
 
//---[ Local header dependencies ]----------------------------------------- 
 
#include "check.h" 
#include "dlg.h" 
#include "wcheck.h" 
#include "checkdta.h" 
#include "movelist.h" 
#include "form.h" //$FORM 
 
//---[ Globally Accessible Config Info ]----------------------------------- 
 
struct  rCheckConfigRec rConfig;                // Config struct 
struct  rGameStateRec rGameState;               // Game state 
class   CMoveList CMoves;                               // Move list 
SQUARE  b[SQRS_MAX];                                    // Board structure 
 
static  POINT       pntStart;                   // For buttonup/down processing. 
 
char szAppName[] = APP_NAME;                    // The name of this application 
 
#ifdef DEBUG 
char szTitle[]   = "Checkers (debug)"; 
#else 
#ifdef TEST 
char szTitle[]   = "Checkers (test)"; 
#else 
char szTitle[]   = "Checkers"; 
#endif 
#endif 
 
//---[ Globally Accessible Handles ]--------------------------------------- 
 
HINSTANCE       hInst;          // current instance handle 
 
HWND    hMainWnd        = NULL;         // Main window handle 
HWND    hClientWnd      = NULL;         // Client window handle 
HBRUSH  hbrBlack;                        
HBRUSH  hbrWhite;                        
HBRUSH  hbrRed;                          
HBRUSH  hbrBckgrnd;                      
HBRUSH  hbrBlue;                         
HBRUSH  hbrHuman;                        
HBRUSH  hbrComputer;             
HBRUSH  hbrDkgrey;                       
HPEN    hpenDkgrey;                      
HBITMAP hBtmaps;                        // Piece bitmaps 
 
HCURSOR curNormal;                      // Standard cursor 
HCURSOR curThink;                       // Computer is thinking cursor 
HCURSOR curPiece;                       // Piece cursor (move in progress) 
HCURSOR curResize;                      // Resize cursor 
 
#define SQR_XSIZE       rConfig.iSquareSize 
#define SQR_YSIZE       rConfig.iSquareSize 
 
#define OFST_INCREMENT      rConfig.iSquareSize         // OFFSET into bitmap 
#define OFST_BLANK          (0*OFST_INCREMENT)          // OFFSET into bitmap 
#define OFST_BLANK_RED      (1*OFST_INCREMENT)          // OFFSET into bitmap 
#define OFST_COMPUTER       (2*OFST_INCREMENT)          // OFFSET into bitmap 
#define OFST_COMPUTER_KING  (3*OFST_INCREMENT)          // OFFSET into bitmap 
#define OFST_HUMAN_KING     (4*OFST_INCREMENT)          // OFFSET into bitmap 
#define OFST_HUMAN          (5*OFST_INCREMENT)          // OFFSET into bitmap 
 
FRM *pfrm; //$FORM 
 
//---[ Inline code includes ]---------------------------------------------- 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
CMoveList::CMoveList() 
{ 
    m_iNumBoards = 0; 
    m_fClassInitialized = FALSE; 
    m_prFirstBoard = NULL; 
    m_prCurrentBoard = m_prFirstBoard; 
    m_prCurrentSearchBoard = m_prFirstBoard; 
} 
 
CMoveList::~CMoveList() 
{ 
    m_prCurrentBoard = m_prFirstBoard; 
    PurgeBoards(); 
} 
 
BOARD CMoveList::GetFirstBoard(BOARD b, int *piPlayerTurn) 
{ 
    AssertSz(m_fClassInitialized, "MoveList class not initialized in GetFirstBoard()"); 
 
    m_prCurrentSearchBoard = m_prFirstBoard; 
    if (NULL == m_prCurrentSearchBoard) 
    return NULL; 
    else 
    { 
    AssertSz((BLACK == m_prCurrentSearchBoard->iPlayerTurn) || 
             (RED == m_prCurrentSearchBoard->iPlayerTurn),  
             "EBr rocks!"); 
     
        *piPlayerTurn = m_prCurrentSearchBoard->iPlayerTurn; 
    CopyBoard(m_prCurrentSearchBoard->b, b); 
         
    m_prCurrentSearchBoard = m_prCurrentSearchBoard->prNextBoard; 
    return b; 
    } 
} 
 
BOARD CMoveList::GetNextBoard(BOARD b, int *piPlayerTurn) 
{ 
    AssertSz(m_fClassInitialized, "MoveList class not initialized in GetNextBoard()"); 
 
    if (NULL == m_prCurrentSearchBoard) 
    { 
    return NULL; 
    } 
    else 
    { 
    AssertSz((BLACK == m_prCurrentSearchBoard->iPlayerTurn) || 
             (RED == m_prCurrentSearchBoard->iPlayerTurn),  
             "Kt C abayn  04/07/94"); 
     
        *piPlayerTurn = m_prCurrentSearchBoard->iPlayerTurn;         
    CopyBoard(m_prCurrentSearchBoard->b, b); 
    m_prCurrentSearchBoard = m_prCurrentSearchBoard->prNextBoard; 
    return b; 
    } 
} 
 
BOOL CMoveList::Init() 
{ 
    AssertSz(!m_fClassInitialized, "Class already initialized"); 
 
    m_fClassInitialized = TRUE; 
    return TRUE; 
} 
 
BOOL CMoveList::BackMove(BOARD b, int *piPlayerTurn) 
{ 
    AssertSz(m_fClassInitialized, "MoveList class not initialized in BackMove()"); 
 
    if (NULL == m_prFirstBoard) 
    return FALSE; 
 
    AssertSz(m_prCurrentBoard, "Current Board hosed"); 
 
    if (m_prCurrentBoard == m_prFirstBoard) 
    { 
    return FALSE; 
    } 
    else 
    { 
    m_prCurrentBoard = m_prCurrentBoard->prPrevBoard; 
     
    AssertSz((BLACK == m_prCurrentBoard->iPlayerTurn) || 
             (RED == m_prCurrentBoard->iPlayerTurn),  
             "Meatloaf"); 
 
        *piPlayerTurn = m_prCurrentBoard->iPlayerTurn; 
    CopyBoard(m_prCurrentBoard->b, b); 
    return TRUE; 
    } 
} 
 
BOOL CMoveList::ForwardMove(BOARD b, int *piPlayerTurn) 
{ 
    AssertSz(m_fClassInitialized, "MoveList class not initialized in ForwardMove()"); 
 
    if (NULL == m_prCurrentBoard) 
    return FALSE; 
 
    if (NULL == m_prCurrentBoard->prNextBoard) 
    return FALSE; 
    else 
    { 
    m_prCurrentBoard = m_prCurrentBoard->prNextBoard; 
 
    AssertSz((BLACK == m_prCurrentBoard->iPlayerTurn) || 
             (RED == m_prCurrentBoard->iPlayerTurn),  
             "Just say no to yes."); 
 
        *piPlayerTurn = m_prCurrentBoard->iPlayerTurn; 
    CopyBoard(m_prCurrentBoard->b, b); 
    return TRUE; 
    } 
} 
 
BOOL CMoveList::NewMove(BOARD b, int iPlayerTurn) 
{ 
    struct rBoardNode *prNewBoard = NULL; 
 
    AssertSz(m_fClassInitialized, "MoveList class not initialized in NewMove()"); 
 
    // Remove any old boards that would get overwritten. 
 
    if (NULL != m_prCurrentBoard) 
    { 
    PurgeBoards(); 
    } 
     
    prNewBoard = (struct rBoardNode *)malloc(sizeof(struct rBoardNode)); 
    if (!prNewBoard) 
    { 
    MessageBox(NULL, "Out of memory.", "Checkers", MB_ICONEXCLAMATION | MB_OK); 
    return FALSE; 
    } 
    else 
    { 
    m_iNumBoards++; 
 
    prNewBoard->prNextBoard = NULL; 
    prNewBoard->prPrevBoard = NULL; 
    CopyBoard(b, prNewBoard->b); 
 
    if (NULL == m_prFirstBoard) 
    { 
        m_prFirstBoard = prNewBoard; 
    } 
    else 
    { 
        m_prCurrentBoard->prNextBoard = prNewBoard; 
    } 
 
    prNewBoard->prPrevBoard = m_prCurrentBoard; 
    prNewBoard->iPlayerTurn = iPlayerTurn; 
    m_prCurrentBoard = prNewBoard; 
    return TRUE; 
    } 
} 
 
BOOL CMoveList::ClearBoards() 
{ 
    m_prCurrentBoard = m_prFirstBoard; 
    return (PurgeBoards()); 
} 
 
BOOL CMoveList::PurgeBoards() 
{ 
    struct rBoardNode *prDeleteBoard; 
     
    if (NULL == m_prCurrentBoard || 0 == m_fClassInitialized) 
    return TRUE; 
    else 
    { 
    prDeleteBoard = m_prCurrentBoard->prNextBoard; 
    while (prDeleteBoard != NULL) 
    { 
        m_prCurrentBoard->prNextBoard = prDeleteBoard->prNextBoard; 
        free (prDeleteBoard); 
        m_iNumBoards--; 
        prDeleteBoard = m_prCurrentBoard->prNextBoard; 
    } 
    return TRUE; 
    } 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
BOOL WritePrivateProfileLong(LPCSTR lpszSection, LPCSTR lpszEntry, long lTheLong, LPCSTR lpszFilename); 
BOOL WritePrivateProfileInt(LPCSTR lpszSection, LPCSTR lpszEntry, int iTheInt, LPCSTR lpszFilename); 
long GetDlgItemLong(HWND hDlg, WORD id); 
int  GetDlgItemInt(HWND hDlg, WORD id); 
void SetDlgItemInt(HWND hDlg, WORD id, int iValue); 
void SetDlgItemLong(HWND hDlg, WORD id, long lValue); 
BOOL ValidateNumeric(char *pszText); 
LONG GetPrivateProfileLong(LPCSTR lpszSection, LPCSTR lpszEntry, long lDefault, LPCSTR lpszFileName); 
 
BOOL WritePrivateProfileInt(LPCSTR lpszSection, LPCSTR lpszEntry, int iTheInt, LPCSTR lpszFilename) 
{ 
    char szINIString[32]; 
 
    AssertSz(lpszSection, "lpszSection == NULL"); 
    AssertSz(lpszEntry, "lpszEntry == NULL"); 
    AssertSz(lpszFilename, "lpszFilename == NULL"); 
 
    _itoa(iTheInt, szINIString, 10);   // Convert the int to a string. Base10 
     return WritePrivateProfileString(lpszSection, lpszEntry, (LPSTR)szINIString, lpszFilename); 
} 
 
long GetDlgItemLong(HWND hDlg, WORD id) 
{ 
    char szTemp[20];    // Can't imagine a number longer than this. 
    long lTemp; 
 
    GetDlgItemText (hDlg, id, (LPSTR) szTemp, sizeof(szTemp)); 
    lTemp = atol(szTemp); 
 
    return lTemp; 
} 
 
int GetDlgItemInt(HWND hDlg, WORD id) 
{ 
    char szTemp[20];    // Can't imagine a number longer than this. 
    int iTemp; 
 
    GetDlgItemText (hDlg, id, (LPSTR) szTemp, sizeof(szTemp)); 
    iTemp = atoi(szTemp); 
 
    return iTemp; 
} 
 
void SetDlgItemInt(HWND hDlg, WORD id, int iValue) 
{ 
    char szTemp[20];     // Can't imagine a number longer than this. 
     
    _itoa(iValue, szTemp, 10); 
    SetDlgItemText (hDlg, id, szTemp); 
} 
 
void SetDlgItemLong(HWND hDlg, WORD id, long lValue) 
{ 
    char szTemp[20];     // Can't imagine a number longer than this. 
     
    _ltoa(lValue, szTemp, 10); 
    SetDlgItemText (hDlg, id, szTemp); 
} 
 
LONG GetPrivateProfileLong(LPCSTR lpszSection, LPCSTR lpszEntry, long lDefault, LPCSTR lpszFileName) 
{ 
    LONG l; 
    char szTemp[20]; 
 
    GetPrivateProfileString(lpszSection, lpszEntry, "~~", szTemp, 20, lpszFileName); 
    if (lstrcmp(szTemp, "~~") == 0) 
    l = lDefault; 
    else 
    l = atol(szTemp); 
    return l; 
} 
 
BOOL WritePrivateProfileLong(LPCSTR lpszSection, LPCSTR lpszEntry, long lTheLong, LPCSTR lpszFilename) 
{ 
    char szINIString[32]; 
 
    AssertSz(lpszSection, "lpszSection == NULL"); 
    AssertSz(lpszEntry, "lpszEntry == NULL"); 
    AssertSz(lpszFilename, "lpszFilename == NULL"); 
 
    _ltoa(lTheLong, szINIString, 10);   // Convert the int to a string. Base10 
     return WritePrivateProfileString(lpszSection, lpszEntry, (LPSTR)szINIString, lpszFilename); 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
int OpenCheckers(HINSTANCE hInstance, int nCmdShow) 
{ 
    MSG msg; 
    HACCEL hAccelTable = NULL;          // Accelerator table handle. 
 
    // Perform initializations that apply to a specific instance 
 
    if (!InitInstance(hInstance, nCmdShow))  
    { 
    AssertSz(0,"InitInst failed"); 
        return (FALSE); 
    } 
 
    hAccelTable = LoadAccelerators (hInstance, (LPSTR)"WCheck"); 
    AssertSz(hAccelTable,"no accelerators"); 
 
    // Acquire and dispatch messages until a WM_QUIT message is received. 
 
    while (GetMessage(&msg,         // message structure 
        NULL,                                   // handle of window receiving the message 
        0,                                      // lowest message to examine 
        0))                                     // highest message to examine 
    { 
        if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg))  
        { 
            TranslateMessage(&msg); // Translates virtual key codes 
            DispatchMessage(&msg);  // Dispatches message to window 
        } 
    } 
 
    return 0; 
 
} /* end open checkers */ 
 
/**************************************************************************** 
 
    FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int) 
 
    PURPOSE: calls initialization function, processes message loop 
 
    COMMENTS: 
 
        Windows recognizes this function by name as the initial entry point 
        for the program.  This function calls the application initialization 
        routine, if no other instance of the program is running, and always 
        calls the instance initialization routine.  It then executes a message 
        retrieval and dispatch loop that is the top-level control structure 
        for the remainder of execution.  The loop is terminated when a WM_QUIT 
        message is received, at which time this function exits the application 
        instance by returning the value passed by PostQuitMessage(). 
 
        If this function must abort before entering the message loop, it 
        returns the conventional value NULL. 
 
****************************************************************************/ 
int APIENTRY WinMain( 
    HINSTANCE hInstance, 
    HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, 
    int nCmdShow) 
{ 
 
    AssertSz(((RED == PLAYER1) && (BLACK == PLAYER2)) ||  
         ((BLACK == PLAYER1) && (RED == PLAYER2)), "Players are schizoid"); 
 
    hInst = hInstance;                              // Set global Instance handle 
 
    if (!hPrevInstance)                                              
    { 
        if (!InitApplication(hInstance))         
        { 
            return (FALSE);      
        } 
    } 
 
    // Check for the -Embedding switch 
 
    if (lpCmdLine && (lpCmdLine[0] == '/' || lpCmdLine[0] == '-')) 
    { 
 
        // Attempt to be a form 
 
    if (NOERROR == HrStartOleAndRegisterClassFactory()) 
        { 
        rGameState.fPlayAsForm = TRUE; // we are acting as a form 
 
        #ifdef DEBUG 
        nCmdShow = SW_SHOWMINIMIZED; 
        #else 
        nCmdShow = SW_HIDE; 
        #endif 
 
        // Open the checkers form window .. but do not show until we're DoVerb'd 
 
        OpenCheckers(hInstance, nCmdShow); 
 
        // Call this function if ..StartForms succeeded 
 
        HrStopForms(); 
 
        return 0; 
        } 
 
        if (IDYES == MessageBox(GetFocus(), "Unable to initialize messaging subsystem.\nWould you like to play against the computer?", "Checkers", MB_YESNO )) 
        { 
        OpenCheckers(hInstance, nCmdShow); 
        return 0; 
        } 
 
    } 
 
    OpenCheckers(hInstance, nCmdShow); 
    return 0; 
} 
 
VOID ReadPlayerInfo(int iPlayerNum, char *pszPlayerName) 
{ 
    AssertSz((iPlayerNum == BLACK) || (iPlayerNum == RED), "Invalid Player Num"); 
    AssertSz(pszPlayerName, "Playername can't be NULL"); 
 
    if (rGameState.fPlayAsForm) 
    { 
    rConfig.rPlayer[iPlayerNum].iPlayerType = HUMAN_PLAYER; 
    if (22871 == GetPrivateProfileInt ("Computer", "Config", HUMAN_PLAYER, INI_NAME)) 
        { 
        rConfig.rPlayer[iPlayerNum].iPlayerType = COMPUTER_PLAYER; 
        } 
    } 
    else 
    { 
    if (PLAYER1 == iPlayerNum) 
        rConfig.rPlayer[iPlayerNum].iPlayerType = HUMAN_PLAYER; 
    else 
        rConfig.rPlayer[iPlayerNum].iPlayerType = COMPUTER_PLAYER; 
    } 
        
    // Get computer config options. 
 
    rConfig.rPlayer[iPlayerNum].iMemPositions = GetPrivateProfileInt (pszPlayerName, "MemPositions", 0, INI_NAME); 
    rConfig.rPlayer[iPlayerNum].iUseMoveTheory = GetPrivateProfileInt (pszPlayerName, "UseMoveTheory", 1, INI_NAME); 
    rConfig.rPlayer[iPlayerNum].iUseMoveShuffling = GetPrivateProfileInt (pszPlayerName, "UseMoveShuffling", 1, INI_NAME); 
    rConfig.rPlayer[iPlayerNum].iUseEqualMoveSkipping = GetPrivateProfileInt (pszPlayerName, "UseEqualMoveSkipping", 1, INI_NAME); 
    rConfig.rPlayer[iPlayerNum].iUseOpeningBook = GetPrivateProfileInt (pszPlayerName, "UseOpeningBook", 1, INI_NAME); 
    rConfig.rPlayer[iPlayerNum].lUseGoodMoveSkipping = GetPrivateProfileLong (pszPlayerName, "UseGoodMoveSkipping", 0, INI_NAME); 
    rConfig.rPlayer[iPlayerNum].iMaxRecursionDepth = GetPrivateProfileInt (pszPlayerName, "MaxRecursionDepth", 5, INI_NAME); 
    rConfig.rPlayer[iPlayerNum].iUseAlphaBetaPruning = GetPrivateProfileInt (pszPlayerName, "UseAlphaBetaPruning", 1, INI_NAME); 
    rConfig.rPlayer[iPlayerNum].lAlphaPruningOriginalDepth = GetPrivateProfileLong (pszPlayerName, "AlphaPruningOriginalDepth", 2, INI_NAME); 
    rConfig.rPlayer[iPlayerNum].lAlphaPruningWidth = GetPrivateProfileLong (pszPlayerName, "AlphaPruningWidth", 35, INI_NAME); 
 
} 
 
VOID    ReadINI () 
{ 
    TraceTag(tagUI,"ReadINI()"); 
 
    // Read general config info 
 
    rConfig.iGameType = GetPrivateProfileInt ("Config", "GameType", GAME_CHECKERS, INI_NAME); 
    rConfig.iMustJump = GetPrivateProfileInt ("Config", "MustJump", 1, INI_NAME); 
    rConfig.iBoardFlipped = 1; 
    rConfig.iSquareSize = GetPrivateProfileInt ("Config", "SquareSize", 30, INI_NAME); 
    rConfig.iMaxMoves = GetPrivateProfileInt ("Config", "MaxMoves", 200, INI_NAME); 
 
    // Read specific player config for both (potential) computer players. 
 
    ReadPlayerInfo(PLAYER1, "Player1"); 
    ReadPlayerInfo(PLAYER2, "Player2"); 
} 
 
VOID WritePlayerInfo(int iPlayerNum, char *pszPlayerName) 
{ 
    AssertSz((iPlayerNum == RED) || (iPlayerNum == BLACK), "Invalid Player Num"); 
    AssertSz(pszPlayerName, "Playername can't be NULL"); 
 
    // Player specific config items 
 
    WritePrivateProfileInt (pszPlayerName, "PlayerType", rConfig.rPlayer[iPlayerNum].iPlayerType, INI_NAME); 
    WritePrivateProfileInt (pszPlayerName, "MemPositions", rConfig.rPlayer[iPlayerNum].iMemPositions, INI_NAME); 
    WritePrivateProfileInt (pszPlayerName, "UseMoveTheory", rConfig.rPlayer[iPlayerNum].iUseMoveTheory, INI_NAME); 
    WritePrivateProfileInt (pszPlayerName, "UseMoveShuffling", rConfig.rPlayer[iPlayerNum].iUseMoveShuffling, INI_NAME); 
    WritePrivateProfileInt (pszPlayerName, "UseEqualMoveSkipping", rConfig.rPlayer[iPlayerNum].iUseEqualMoveSkipping, INI_NAME); 
    WritePrivateProfileInt (pszPlayerName, "UseOpeningBook", rConfig.rPlayer[iPlayerNum].iUseOpeningBook, INI_NAME); 
    WritePrivateProfileLong(pszPlayerName, "UseGoodMoveSkipping", rConfig.rPlayer[iPlayerNum].lUseGoodMoveSkipping, INI_NAME); 
    WritePrivateProfileInt (pszPlayerName, "MaxRecursionDepth", rConfig.rPlayer[iPlayerNum].iMaxRecursionDepth, INI_NAME); 
    WritePrivateProfileInt (pszPlayerName, "UseAlphaBetaPruning", rConfig.rPlayer[iPlayerNum].iUseAlphaBetaPruning, INI_NAME); 
    WritePrivateProfileLong(pszPlayerName, "AlphaPruningOriginalDepth", rConfig.rPlayer[iPlayerNum].lAlphaPruningOriginalDepth, INI_NAME); 
    WritePrivateProfileLong(pszPlayerName, "AlphaPruningWidth", rConfig.rPlayer[iPlayerNum].lAlphaPruningWidth, INI_NAME); 
 
} 
 
VOID    WriteINI () 
{ 
    RECT    rect; 
    char    szTempStr[10];                       // For rect.value translation 
 
    TraceTag(tagUI,"WriteINI()"); 
 
    // Store state of config radio buttons 
 
    WritePrivateProfileInt ("Config", "GameType", rConfig.iGameType, INI_NAME); 
    WritePrivateProfileInt ("Config", "MustJump", rConfig.iMustJump, INI_NAME); 
    WritePrivateProfileInt ("Config", "SquareSize", rConfig.iSquareSize, INI_NAME); 
    WritePrivateProfileInt ("Config", "MaxMoves", rConfig.iMaxMoves, INI_NAME); 
 
    // Write player info 
 
    WritePlayerInfo(PLAYER1, "Player1"); 
    WritePlayerInfo(PLAYER2, "Player2"); 
 
    // Store state of Window 
 
    GetWindowRect (hMainWnd, &rect); 
    if (!IsIconic(hMainWnd)) 
    { 
    wsprintf (szTempStr, "%d", rect.left); 
    WritePrivateProfileString ("Window", "Left", szTempStr, INI_NAME); 
    wsprintf (szTempStr, "%d", rect.top); 
    WritePrivateProfileString ("Window", "Top", szTempStr, INI_NAME); 
    wsprintf (szTempStr, "%d", rect.right - rect.left); 
    WritePrivateProfileString ("Window", "Width", szTempStr, INI_NAME); 
    wsprintf (szTempStr, "%d", rect.bottom - rect.top); 
    WritePrivateProfileString ("Window", "Height", szTempStr, INI_NAME); 
    } 
 
} 
 
BOOL InitApplication(HINSTANCE hInstance) 
{ 
    WNDCLASS        wc; 
 
    // Fill in window class structure with parameters that describe the 
    // main window. 
 
    wc.style         = CS_HREDRAW | CS_VREDRAW;             // Class style(s). 
    wc.lpfnWndProc   = (WNDPROC)CheckersWndProc;    // Window Procedure 
    wc.cbClsExtra    = 0;                                   // No per-class extra data. 
    wc.cbWndExtra    = 0;                                   // No per-window extra data. 
    wc.hInstance     = hInstance;                           // Owner of this class 
    wc.hIcon         = LoadIcon (hInstance, "WCheckIcon");  // Icon name from .RC 
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);                 // Cursor 
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);                    // Default color 
    wc.lpszMenuName  = (LPSTR)"WCheck_NORMAL";              // Menu name from .RC 
    wc.lpszClassName = szAppName;                                           // Name to register as 
 
    // Register the window class and return success/failure code. 
    return (RegisterClass(&wc)); 
} 
 
 
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 
{ 
    HWND    hWnd; // Main window handle. 
    HDC hdc = NULL; 
 
    // Save the instance handle in static variable, which will be used in 
    // many subsequence calls from this application to Windows. 
 
    // need to set this here because we may get call from somewhere besides winmain 
    hInst = hInstance; // Store instance handle in our global variable 
 
    hbrWhite = (HBRUSH)GetStockObject(WHITE_BRUSH); 
    hbrBlack = (HBRUSH)GetStockObject(BLACK_BRUSH); 
 
    hbrRed        = CreateSolidBrush(RGB(0xFF,0,0)); 
    hbrBckgrnd    = CreateSolidBrush(GetNearestColor(hdc, RGB(0xAA,0xAA,0xAA))); 
    hbrBlue       = CreateSolidBrush(RGB(0,0,0xFF)); 
    hpenDkgrey    = CreatePen( PS_SOLID, 1, RGB( 0x55, 0x55, 0x55 ) ); 
 
    #ifdef _WIN32 
        hbrDkgrey     = CreateSolidBrush(RGB(0x55,0x55,0x55)); 
    #else 
        hbrDkgrey     = CreateSolidBrush(GetNearestColor(hdc, RGB(0x55,0x55,0x55))); 
    #endif 
 
    // Fail initialization if we can't create brushs/pens/etc.. 
 
    if (!hbrRed ||  
        !hbrBckgrnd ||  
        !hbrBlue || 
        !hbrWhite ||  
        !hbrBlack ||  
        !hpenDkgrey ||  
        !hbrDkgrey) 
    return(FALSE); 
 
    curNormal   = LoadCursor(NULL, IDC_ARROW); 
    curThink    = LoadCursor(NULL, IDC_WAIT); 
    curResize       = LoadCursor(NULL, IDC_SIZENWSE); 
 
    if (!curNormal || !curThink || !curResize) 
    return(FALSE); 
 
    // Create a main window for this application instance. 
    hWnd = CreateWindow( 
        szAppName,           // See RegisterClass() call. 
        szTitle,             // Text for window title bar. 
        WS_OVERLAPPEDWINDOW, // Window style. 
    GetPrivateProfileInt ("Window", "Left",  13  /* CW_USEDEFAULT */, INI_NAME), 
    GetPrivateProfileInt ("Window", "Top",   8   /* CW_USEDEFAULT */, INI_NAME), 
    GetPrivateProfileInt ("Window", "Width", 356 /* CW_USEDEFAULT */, INI_NAME), 
    GetPrivateProfileInt ("Window", "Height",306 /* CW_USEDEFAULT */, INI_NAME), 
        NULL,                // Overlapped windows have no parent. 
        NULL,                // Use the window class menu. 
        hInstance,           // This instance owns this window. 
        NULL                 // We don't use any data in our WM_CREATE 
    ); 
 
    // If window could not be created, return "failure" 
    if (!hWnd) { 
        return (FALSE); 
    } 
 
    while (!SetTimer (hWnd, ID_TIMER, TIMER_GRANULARITY, NULL)) 
    { 
    if (IDCANCEL == MessageBox (hWnd, "Too many clocks or timers!", szAppName, 
        MB_ICONEXCLAMATION | MB_RETRYCANCEL)) 
        { 
        return FALSE; 
        } 
    } 
 
    // Make the window visible; update its client area; and return "success" 
 
    ShowWindow(hWnd, nCmdShow); // Show the window // SW_SHOWNORMAL // SW_HIDE 
    UpdateWindow(hWnd);         // Sends WM_PAINT message 
 
    SendMessage(hWnd, WM_OTHERINIT, 0, 0L); 
 
    return (TRUE);              // We succeeded... 
 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
// FillBoard() puts the pieces on the board (draws them). 
 
void NEAR PASCAL FillBoard(BOARD b, HDC hDC) 
 
{ 
    int         i                       = 0;            // Loop (for pieces) variable 
    HDC         hTmpDC          = NULL;         // Temp work DC 
    HBITMAP hOldbm              = NULL;         // Store old Bitmap handle 
    int         yBm             = 0;            // Bitmap to use (depending on piece) 
    int         iOffset         = 0;            // Squares alternate starting postion.. 
    int         iX                      = 0;            // Location (x) to place square 
    int             iY                      = 0;            // Location (y) to place square 
    int     iLocation       = 0;            // For figuring real location after swap.. 
 
    hTmpDC = CreateCompatibleDC( hDC ); 
    hOldbm = (HBITMAP)SelectObject( hTmpDC, hBtmaps ); 
 
    for (i=1; i <= 32; i++) 
    { 
        if (rConfig.iBoardFlipped) 
            iLocation = 33-i; 
        else 
            iLocation = i; 
             
    AssertSz(iLocation > 0, "Bee-Gees r a bit"); 
    AssertSz(iLocation <= SQRS_MAX, "Jello: The environmentally safe defoliant"); 
 
    switch(b[iLocation]) 
    { 
        case 0: 
        yBm = 0; 
        break; 
        case (RED | KING): 
        yBm = OFST_COMPUTER_KING; 
        break; 
        case RED: 
        yBm = OFST_COMPUTER; 
        break; 
        case (BLACK | KING): 
        yBm = OFST_HUMAN_KING; 
        break; 
        case BLACK: 
        yBm = OFST_HUMAN; 
        break; 
        default: 
        { 
        AssertSz(FALSE, "Piece value invalid"); 
        TraceTag(tagUI, "Invalid piece value of piece %d is: %d", i, b[i]); 
        } 
        break; 
    } 
 
        // Calculate location of square on board. (offset?) 
 
        iY = (i-1) / 4; 
        iX = (i-1) % 4; 
    if ((iY % 2) == 1) 
            iOffset = 0; 
        else 
            iOffset = 1; 
 
        // Place the square on the board 
 
        BitBlt( hDC, BOARD_LEFT + (rConfig.iSquareSize*(2*iX+iOffset)), BOARD_TOP + (rConfig.iSquareSize*iY), 
        rConfig.iSquareSize, rConfig.iSquareSize, hTmpDC, 0, yBm, SRCCOPY ); 
 
        // All squares have an accompanying blank square. Depending on the  
        // offset, it can be on either side of the filled square. 
 
        if (iOffset == 1) 
            BitBlt( hDC, BOARD_LEFT + (rConfig.iSquareSize*(2*iX)), BOARD_TOP + (rConfig.iSquareSize*iY), 
                rConfig.iSquareSize, rConfig.iSquareSize, hTmpDC, 0, OFST_BLANK_RED, SRCCOPY); 
        else 
            BitBlt( hDC, BOARD_LEFT + (rConfig.iSquareSize*(2*iX+1)), BOARD_TOP + (rConfig.iSquareSize*iY), 
                rConfig.iSquareSize, rConfig.iSquareSize, hTmpDC, 0, OFST_BLANK_RED, SRCCOPY); 
 
    } 
 
    // Draw setup rack if the user is in setup mode. 
 
    if (rGameState.fSetupMode) 
    { 
 
        BitBlt( hDC, BOARD_LEFT + (rConfig.iSquareSize * RACK_LEFT),  
            BOARD_TOP + (rConfig.iSquareSize * (RACK_TOP)),  
            rConfig.iSquareSize, rConfig.iSquareSize,  
            hTmpDC, 0, OFST_COMPUTER, SRCCOPY); 
 
        BitBlt( hDC, BOARD_LEFT + (rConfig.iSquareSize * RACK_LEFT),  
            BOARD_TOP + (rConfig.iSquareSize * (RACK_TOP + 1)),  
            rConfig.iSquareSize, rConfig.iSquareSize,  
hTmpDC, 0, OFST_HUMAN, SRCCOPY); 
 
        BitBlt( hDC, BOARD_LEFT + (rConfig.iSquareSize * RACK_LEFT),  
            BOARD_TOP + (rConfig.iSquareSize * (RACK_TOP + 2)),  
            rConfig.iSquareSize, rConfig.iSquareSize,  
            hTmpDC, 0, OFST_COMPUTER_KING, SRCCOPY); 
 
        BitBlt( hDC, BOARD_LEFT + (rConfig.iSquareSize * RACK_LEFT),  
            BOARD_TOP + (rConfig.iSquareSize * (RACK_TOP + 3)),  
            rConfig.iSquareSize, rConfig.iSquareSize,  
            hTmpDC, 0, OFST_HUMAN_KING, SRCCOPY); 
 
        BitBlt( hDC, BOARD_LEFT + (rConfig.iSquareSize * RACK_LEFT),  
            BOARD_TOP + (rConfig.iSquareSize * (RACK_TOP + 4)),  
            rConfig.iSquareSize, rConfig.iSquareSize,  
            hTmpDC, 0, OFST_BLANK, SRCCOPY); 
    } 
 
    SelectObject( hTmpDC, hOldbm ); 
    DeleteDC( hTmpDC ); 
} 
 
// Removes the piece that's moving, and replaces with a blank square. 
 
void vClearMovingPiece(HDC hDC, int iX, int iY) 
{ 
    HDC   hTmpDC; 
    HBITMAP hOldbm; 
    int   yBm; 
 
    hTmpDC = CreateCompatibleDC( hDC ); 
    hOldbm = (HBITMAP)SelectObject( hTmpDC, hBtmaps ); 
 
    yBm = 0; 
 
    BitBlt( hDC, BOARD_LEFT + (rConfig.iSquareSize*iX), BOARD_TOP + (rConfig.iSquareSize*iY), 
    rConfig.iSquareSize, rConfig.iSquareSize, hTmpDC, 0, yBm, SRCCOPY ); 
 
    SelectObject( hTmpDC, hOldbm ); 
    DeleteDC( hTmpDC ); 
} 
 
// Reset the board to starting positions. 
void ClearBoard(BOARD b) 
{ 
    Assert(b); 
 
    b[0]  = 0;          b[1]  = BLACK;  b[2]  = BLACK;  b[3] = BLACK; 
    b[4]  = BLACK;  b[5]  = BLACK;  b[6]  = BLACK;      b[7] = BLACK; 
    b[8]  = BLACK;  b[9]  = BLACK;  b[10] = BLACK;      b[11] = BLACK; 
    b[12] = BLACK;  b[13] = EMPTY;      b[14] = EMPTY;  b[15] = EMPTY; 
    b[16] = EMPTY;  b[17] = EMPTY;      b[18] = EMPTY;  b[19] = EMPTY; 
    b[20] = EMPTY;  b[21] = RED;        b[22] = RED;    b[23] = RED; 
    b[24] = RED;        b[25] = RED;    b[26] = RED;    b[27] = RED; 
    b[28] = RED;        b[29] = RED;    b[30] = RED;    b[31] = RED; 
    b[32] = RED; 
} 
 
void DrawBoard(BOARD b) 
{ 
    HDC hDC = GetDC(hMainWnd); 
     
    Assert(b); 
 
    FillBoard(b, hDC); 
    ReleaseDC(hMainWnd, hDC); 
 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
void DisplayGameOver(HWND hWnd, int iPlayerTurn) 
{ 
    int iWinner = BLACK; 
 
    AssertSz(hWnd, "hWnd hosed, homey."); 
    AssertSz((rGameState.iPlayerTurn == BLACK) ||  
             (rGameState.iPlayerTurn == RED), "Very low sodium"); 
 
    // NOTE: Caller is passing in the losing player. The turn has already 
    // switched before this gets called. This makes iPlayerTurn  
    // equivalent to the winner in a give-away game. If the game type is  
    // standard checkers, I want to invert winner. 
 
    iWinner = iPlayerTurn; 
    if (rConfig.iGameType == GAME_CHECKERS) 
    iWinner = next(iWinner); 
     
    if (RED == iWinner) 
        MessageBox(hWnd, "Game over. Red wins!", "Checkers", MB_OK ); 
    else 
        MessageBox(hWnd, "Game over. Black wins!", "Checkers", MB_OK ); 
 
} 
 
void DisplayGameDrawn(HWND hWnd, int iPlayerTurn) 
{ 
    AssertSz(hWnd, "hWnd hosed (in DisplayGameDrawn), homey."); 
    AssertSz((rGameState.iPlayerTurn == BLACK) ||  
             (rGameState.iPlayerTurn == RED), "Vl bites"); 
 
    MessageBox(hWnd, "Game drawn. (Game over)", "Checkers", MB_OK ); 
} 
 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
void NEAR PASCAL CheckGenPieces(HDC hDC) 
{ 
    HDC     hTmpDC  = NULL; 
    HBITMAP hOldbm  = NULL; 
    HBRUSH  hOldbr  = NULL; 
    int     i               = 0; 
 
    AssertSz(hDC, "Null hDC in CheckGenPieces"); 
 
    if( hBtmaps != NULL ) 
        DeleteObject( hBtmaps ); 
 
    // We have 6 different pieces (including the blank). So we just fill 
    // a region big enough for 6 squares. 
    hBtmaps = CreateCompatibleBitmap( hDC, SQR_XSIZE, 6*SQR_YSIZE ); 
 
    /* allocate an off-screen DC, so we can paint the ellipses now */ 
    hTmpDC = CreateCompatibleDC( hDC ); 
    hOldbm = (HBITMAP)SelectObject( hTmpDC, hBtmaps ); 
 
    /* blank out the off-screen bitmap, ie: paint the background into it */ 
    hOldbr = (HBRUSH)SelectObject( hTmpDC, GetStockObject( LTGRAY_BRUSH )); 
    PatBlt( hTmpDC, 0, 0, SQR_XSIZE, SQR_YSIZE, PATCOPY ); 
    SetBkMode( hTmpDC, TRANSPARENT ); 
 
    /* draw the square's "grooved" borders */ 
    SelectObject( hTmpDC, hpenDkgrey ); 
    MoveTo( hTmpDC, 2, SQR_YSIZE-2 ); 
    LineTo( hTmpDC, SQR_XSIZE-2, SQR_YSIZE-2 ); 
    LineTo( hTmpDC, SQR_XSIZE-2, 1 ); 
    SelectObject( hTmpDC, GetStockObject( WHITE_PEN ) ); 
    LineTo( hTmpDC, 1, 1 ); 
    LineTo( hTmpDC, 1, SQR_YSIZE ); 
    SelectObject( hTmpDC, GetStockObject( BLACK_PEN ) ); 
    MoveTo( hTmpDC, 2, SQR_YSIZE-1 ); 
    LineTo( hTmpDC, SQR_XSIZE-1, SQR_YSIZE-1 ); 
    LineTo( hTmpDC, SQR_XSIZE-1, 0 ); 
 
    /* copy the borders to the other bitmaps */ 
    for( i=OFST_INCREMENT; i <= OFST_HUMAN; i+=OFST_INCREMENT ) 
    BitBlt(hTmpDC, 0, i, SQR_XSIZE, SQR_YSIZE, hTmpDC, 0, 0, SRCCOPY); 
 
    /* Attempt at RED SQUARE*/ 
    SelectObject( hTmpDC, hbrDkgrey ); 
    hOldbr = (HBRUSH)SelectObject( hTmpDC, GetStockObject( LTGRAY_BRUSH )); 
    SelectObject( hTmpDC, hbrRed); 
    PatBlt( hTmpDC, 0+2, OFST_BLANK_RED+2, SQR_XSIZE-4, SQR_YSIZE-4, PATCOPY ); 
 
    /* first piece: computer */ 
    SelectObject(hTmpDC, GetStockObject( WHITE_PEN ) ); 
    SelectObject(hTmpDC, GetStockObject( WHITE_BRUSH ) ); 
    Ellipse( hTmpDC, 3, OFST_COMPUTER+3, SQR_XSIZE-6, OFST_COMPUTER+SQR_YSIZE-6 ); 
    SelectObject(hTmpDC, hpenDkgrey ); 
    SelectObject(hTmpDC, hbrDkgrey ); 
    Ellipse( hTmpDC, 6, OFST_COMPUTER+6, SQR_XSIZE-3, OFST_COMPUTER+SQR_YSIZE-3 ); 
    SelectObject(hTmpDC, GetStockObject( BLACK_PEN ) ); 
    SelectObject(hTmpDC, hbrRed); 
    Ellipse( hTmpDC, 4, OFST_COMPUTER+4, SQR_XSIZE-5, OFST_COMPUTER+SQR_YSIZE-5 ); 
 
    /* computer king */ 
    BitBlt( hTmpDC, 0, OFST_COMPUTER_KING, SQR_XSIZE, OFST_COMPUTER_KING+SQR_YSIZE, hTmpDC, 
        0, OFST_COMPUTER, SRCCOPY ); 
    SelectObject( hTmpDC, GetStockObject( WHITE_PEN ) ); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 - 4), OFST_COMPUTER_KING + (SQR_YSIZE / 2)); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 + 4), OFST_COMPUTER_KING + (SQR_YSIZE / 2)); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 - 4), OFST_COMPUTER_KING + (SQR_YSIZE / 2) + 1); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 + 4), OFST_COMPUTER_KING + (SQR_YSIZE / 2) + 1); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 - 2), OFST_COMPUTER_KING + (SQR_YSIZE / 2) - 1); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 + 2), OFST_COMPUTER_KING + (SQR_YSIZE / 2) - 1); 
 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 - 4), OFST_COMPUTER_KING + (SQR_YSIZE / 2) - 1); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 - 6), OFST_COMPUTER_KING + (SQR_YSIZE / 2) - 3); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 + 3), OFST_COMPUTER_KING + (SQR_YSIZE / 2) - 1); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 + 5), OFST_COMPUTER_KING + (SQR_YSIZE / 2) - 3); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 - 2), OFST_COMPUTER_KING + (SQR_YSIZE / 2) - 2); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 - 3), OFST_COMPUTER_KING + (SQR_YSIZE / 2) - 4); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 + 1), OFST_COMPUTER_KING + (SQR_YSIZE / 2) - 2); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 + 3), OFST_COMPUTER_KING + (SQR_YSIZE / 2) - 4); 
 
    /* human king */ 
    BitBlt( hTmpDC, 0, OFST_HUMAN_KING, SQR_XSIZE, OFST_HUMAN_KING+SQR_YSIZE, hTmpDC, 
    0, OFST_COMPUTER, SRCCOPY ); 
    SelectObject(hTmpDC, hbrBlack); 
    SelectObject(hTmpDC, GetStockObject( BLACK_PEN ) ); 
    Ellipse( hTmpDC, 4, OFST_HUMAN_KING+4, SQR_XSIZE-5, OFST_HUMAN_KING+SQR_YSIZE-5 ); 
    SelectObject( hTmpDC, GetStockObject( WHITE_PEN ) ); 
    SelectObject( hTmpDC, GetStockObject( WHITE_PEN ) ); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 - 4), OFST_HUMAN_KING + (SQR_YSIZE / 2)); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 + 4), OFST_HUMAN_KING + (SQR_YSIZE / 2)); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 - 4), OFST_HUMAN_KING + (SQR_YSIZE / 2) + 1); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 + 4), OFST_HUMAN_KING + (SQR_YSIZE / 2) + 1); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 - 2), OFST_HUMAN_KING + (SQR_YSIZE / 2) - 1); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 + 2), OFST_HUMAN_KING + (SQR_YSIZE / 2) - 1); 
 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 - 4), OFST_HUMAN_KING + (SQR_YSIZE / 2) - 1); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 - 6), OFST_HUMAN_KING + (SQR_YSIZE / 2) - 3); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 + 3), OFST_HUMAN_KING + (SQR_YSIZE / 2) - 1); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 + 5), OFST_HUMAN_KING + (SQR_YSIZE / 2) - 3); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 - 2), OFST_HUMAN_KING + (SQR_YSIZE / 2) - 2); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 - 3), OFST_HUMAN_KING + (SQR_YSIZE / 2) - 4); 
    MoveTo(hTmpDC, (SQR_XSIZE / 2 + 1), OFST_HUMAN_KING + (SQR_YSIZE / 2) - 2); 
    LineTo(hTmpDC, (SQR_XSIZE / 2 + 3), OFST_HUMAN_KING + (SQR_YSIZE / 2) - 4); 
     
    /* human */ 
    BitBlt( hTmpDC, 0, OFST_HUMAN, SQR_XSIZE, SQR_YSIZE, hTmpDC, 
    0, OFST_COMPUTER, SRCCOPY ); 
    SelectObject(hTmpDC, GetStockObject( BLACK_PEN ) ); 
    SelectObject(hTmpDC, hbrBlack); 
    Ellipse( hTmpDC, 4, OFST_HUMAN+4, SQR_XSIZE-5, OFST_HUMAN+SQR_YSIZE-5 ); 
 
    SelectObject( hTmpDC, hOldbr ); 
    SelectObject( hTmpDC, hOldbm ); 
    DeleteDC( hTmpDC ); 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
void NEAR PASCAL CheckCreate() 
 
{ 
    HDC  hDC; 
    TEXTMETRIC    charsize;           /* characteristics of the characters */ 
    int COLOR = 1; 
 
    hDC = GetDC(hMainWnd); 
    GetTextMetrics(hDC, (LPTEXTMETRIC)&charsize); 
 
    ReleaseDC(hMainWnd, hDC); 
 
    COLOR = GetDeviceCaps(hDC, NUMCOLORS) > 2; 
 
    if (COLOR == TRUE) 
    { 
    hbrComputer = hbrBlue; 
    hbrHuman = hbrRed; 
    } 
    else 
    { 
    hbrComputer = hbrBlack; 
    hbrHuman = hbrWhite; 
    } 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
/* Called on WM_PAINT messages. */ 
 
void NEAR PASCAL CheckPaint(BOARD b, HDC hDC) 
 
{ 
    CheckGenPieces(hDC); 
    SetBkMode(hDC, OPAQUE); 
 
    DrawBoard(b); 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
void inline vTranslateCurPosToSquare(POINT pntCur, int *pix, int *piy) 
{ 
    Assert(pix); 
    Assert(piy); 
    Assert(rConfig.iSquareSize != 0); 
 
    *pix = (pntCur.x - BOARD_LEFT) / rConfig.iSquareSize; 
    *piy = (pntCur.y - BOARD_TOP) / rConfig.iSquareSize; 
 
    if ((*pix < 0) || (*pix > 7) || (*piy < 0) || (*piy > 7)) 
    { 
        if ((!rGameState.fSetupMode) || (*pix != 10) || (*piy < 2) || (*piy > 6)) 
        { 
            *pix = -1;              // Return invalid x/y locations 
            *piy = -1;              // Return invalid x/y locations 
        } 
    } 
} 
 
void inline vTranslateUIToEngineSquare(int *piEngine, int iX, int iY) 
{ 
    AssertSz(piEngine, "piEngine == NULL"); 
 
    if (((iY + iX) % 2 == 0) || 
    (iY < 0) || (iY > 7) || (iX < 0) || (iX > 7)) 
    { 
    *piEngine = -1;         // Return invalid engine ID 
        return; 
    } 
    else 
    { 
    *piEngine = iY * 4 + (iX / 2) + 1; 
    } 
 
    if (rConfig.iBoardFlipped) 
        *piEngine = (33 - *piEngine); 
} 
 
void inline vTranslateEngineToUISquare(int iEngine, int *piX, int *piY) 
{ 
    Assert(iEngine > 0); 
    Assert(piX); 
    Assert(piY); 
 
    if (rConfig.iBoardFlipped) 
        iEngine = (33 - iEngine); 
     
    *piY = (iEngine - 1) / 4; 
    *piX = ((iEngine - 1) % 4) * 2 + ((*piY + 1) % 2); 
}                            
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
BOOL fValidSetupPiece(BOARD b, int ix, int iy) 
{ 
    int iEngine = -1; 
    int iPiece  = -1; 
    BOOL fRackPiece = FALSE; 
 
    AssertSz(b, "Where's the board"); 
 
    if ((ix < 0) || (ix > 7) || (iy < 0) || (iy > 7)) 
    { 
        // Check for Setup Rack pieces 
 
        if ((ix != 10) || (iy < 2) || (iy > 6)) 
            return FALSE; 
        else 
            fRackPiece = TRUE; 
    } 
     
    vTranslateUIToEngineSquare(&iEngine, ix, iy); 
 
    AssertSz((iEngine == -1) ||  
         ((iEngine > 0) && (iEngine <= SQRS_MAX)),  
             "iEngine needs counseling"); 
     
    if (iEngine != -1) 
    { 
        iPiece = (int) b[iEngine]; 
 
        if ((iPiece & RED) || (iPiece & BLACK) || (iPiece == EMPTY)) 
            return TRUE; 
        else 
            return FALSE; 
    } 
    else 
    { 
        if (!fRackPiece) 
            return FALSE; 
        else 
            return TRUE; 
    } 
 
} 
 
BOOL fValidPlayerPiece(BOARD b, int ix, int iy) 
{ 
    int iEngine = 0; 
    int iPiece = 0; 
 
    AssertSz(b, "Where's the board?"); 
    AssertSz(((rGameState.iPlayerTurn == RED) ||  
          (rGameState.iPlayerTurn == BLACK)), "Harrison"); 
     
    if ((ix < 0) || (ix > 7) || (iy < 0) || (iy > 7)) 
    return FALSE; 
 
    vTranslateUIToEngineSquare(&iEngine, ix, iy); 
 
    // Assert that the engine return is either a "normal" invalid, or 
    // a normal engine square. Should NOT be 0. 
 
    AssertSz((iEngine == -1) ||  
         ((iEngine > 0) && (iEngine <= SQRS_MAX)),  
             "Abort, Retry, Fail?"); 
     
    if (iEngine != -1) 
    iPiece = (int) b[iEngine]; 
    else 
        return FALSE; 
 
    if (RED == rGameState.iPlayerTurn) 
    { 
    if (iPiece & RED) 
        return TRUE; 
    else 
        return FALSE; 
    } 
    else  
    { 
        if (BLACK == rGameState.iPlayerTurn) 
    { 
        if (iPiece & BLACK) 
        return TRUE; 
        else 
        return FALSE; 
        } 
    else 
        { 
        AssertSz(FALSE, "rGameState.iPlayerTurn invalid!"); 
        return FALSE; 
    } 
    } 
} 
 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
void OnGamePlayableAfterMove(void) 
{ 
    if (pfrm) 
    { 
    AssertSz(pfrm->pMessage,"no message to send"); 
 
    // ----- Update our forms information based on user interaction 
    pfrm->SetCheckersData(b,rGameState.iPlayerTurn,0,QualityOfBoard(b,rGameState.iPlayerTurn)); 
 
    // ----- Send this baby, and shut her down 
    SetWindowText(hMainWnd,"Sending ..."); 
    pfrm->SendForm(); 
    pfrm->ShutdownForm(OLECLOSE_NOSAVE); 
    } 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
// Handle WM_INITMENU message. 
 
VOID vHandle_WM_INITMENU(HWND hWnd, HMENU hMenu) 
{ 
    hMenu = GetMenu(hWnd); 
    if (rGameState.fThinking) 
    { 
    EnableMenuItem(hMenu, 0, MF_DISABLED | MF_BYPOSITION); 
    EnableMenuItem(hMenu, 1, MF_DISABLED | MF_BYPOSITION); 
    } 
    else 
    { 
    EnableMenuItem(hMenu, 0, MF_ENABLED | MF_BYPOSITION); 
    EnableMenuItem(hMenu, 1, MF_ENABLED | MF_BYPOSITION); 
    } 
} 
 
BOOL fHandle_WM_CREATE(HWND hWnd, CREATESTRUCT FAR* lpCreateStruct) 
{ 
    HDC         hDC     = NULL; 
    HMENU       hm              = NULL; 
 
    hMainWnd = hWnd; 
    ReadINI(); 
 
    // Set game state variables. 
 
    rGameState.fSetupMode = FALSE;          // Game in setup mode? Need for button processing 
    rGameState.fThinking = FALSE;           // Is the computer thinking? 
    rGameState.fPaused = FALSE;             // Is the game paused? 
    rGameState.fGameInProgress = FALSE;     // Did the game start? How to react to "File:New" 
    rGameState.fLMouseButtonDown = FALSE;   // Is the left mouse-button currently down? 
    rGameState.fRMouseButtonDown = FALSE;   // Is the left mouse-button currently down? 
    rGameState.fMoveInProgress = FALSE;     // Is the user in the middle of a move? 
    rGameState.fComputerBusy = FALSE;               // Is the computer thinking? 
    rGameState.fGameOver = FALSE;                   // Is the game over? 
    rGameState.fInitDone = FALSE;                   // Is initialization done? 
    rGameState.iPlayerTurn = BLACK; 
    rGameState.iCursorState = NORMAL_CURSOR;        // What state is the cursor in? 
    rGameState.fMouseResizeInProgress = FALSE;      // Is the user resizing right now? 
    rConfig.iSetupPurgeBoards = TRUE;               // Purge boards when exit setup? 
     
    CMoves.Init(); 
    CheckCreate(); 
    hDC = GetDC(hWnd); 
    ClearBoard(b); 
    CMoves.NewMove(b, rGameState.iPlayerTurn); 
    FillBoard(b, hDC); 
 
    hm = GetMenu(hWnd); 
    if (!rConfig.iBoardFlipped) 
    { 
        CheckMenuItem(hm, IDM_FLIP, MF_CHECKED); 
    } 
 
    CheckMenuItem(hm, IDM_BASE_LEVEL + rConfig.rPlayer[RED].iMaxRecursionDepth, MF_CHECKED); 
 
    switch(rConfig.iGameType) 
    { 
    case GAME_CHECKERS: 
        CheckMenuItem(hm, IDM_GAME_CHECKERS, MF_CHECKED); 
        CheckMenuItem(hm, IDM_GAME_GIVEAWAY, MF_UNCHECKED); 
        break; 
    case GAME_GIVEAWAY: 
        CheckMenuItem(hm, IDM_GAME_GIVEAWAY, MF_CHECKED); 
        CheckMenuItem(hm, IDM_GAME_CHECKERS, MF_UNCHECKED); 
        break; 
    default: 
        // Set to checkers, assert if debug 
        CheckMenuItem(hm, IDM_GAME_CHECKERS, MF_CHECKED); 
        CheckMenuItem(hm, IDM_GAME_GIVEAWAY, MF_UNCHECKED); 
        MessageBox(hWnd, "Game type invalid. Setting to checkers", "Checkers", MB_OK); 
        rConfig.iGameType = GAME_CHECKERS; 
        break; 
    } 
 
    // Load "Piece" cursor. 
 
    curPiece   = LoadCursor(hInst, "PieceCur"); 
    if (curPiece) 
    TraceTag(tagUI, "Cur Piece is valid"); 
    else 
    TraceTag(tagUI, "Cur Piece is invalid"); 
 
    return TRUE; 
 
} 
 
VOID vHandle_WM_DESTROY(HWND hWnd) 
{ 
    WriteINI(); 
 
    if (pfrm) 
    { 
        FRM *pfrml; //$FORM 
        pfrml = pfrm; 
        pfrm = NULL; 
        pfrml->ShutdownForm(OLECLOSE_NOSAVE); 
        pfrml->Release(); 
    } 
 
    DeleteObject(hbrBckgrnd); 
    DeleteObject(hbrRed); 
    DeleteObject(hbrBlue); 
    DeleteObject(hbrDkgrey); 
    DeleteObject(hpenDkgrey); 
    DeleteObject(hBtmaps); 
 
    // WinHelp(hWnd, (LPSTR)szHelpFile, HELP_QUIT, 0L); 
 
    PostQuitMessage(0); 
} 
 
VOID vHandle_WM_TIMER(HWND hWnd, UINT id) 
{ 
    #ifdef DEBUG 
    static int x=0; 
    if (0==x) 
    { 
    if (!((BLACK == rGameState.iPlayerTurn) || 
      (RED == rGameState.iPlayerTurn))) x++; 
    AssertSz(((BLACK == rGameState.iPlayerTurn) ||  
          (RED == rGameState.iPlayerTurn)),  
          "Caffeine-Free? What's the point?"); 
    } 
    #endif 
     
    if ((rConfig.rPlayer[rGameState.iPlayerTurn].iPlayerType == COMPUTER_PLAYER) && 
        (rGameState.fInitDone) &&  
        (!rGameState.fPaused) &&  
        (!rGameState.fComputerBusy) && 
        (!rGameState.fGameOver)) 
    { 
    ComputerMove(); 
    } 
} 
 
VOID vHandle_WM_RBUTTONDOWN(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) 
{ 
    AssertSz(!rGameState.fRMouseButtonDown, "David Copperfield with a mouse"); 
    TraceTag(tagMouse, "in vHandle_WM_RBUTTONDOWN"); 
    TraceTag(tagMouse, "rGameState.fRMouseButtonDown: %d", rGameState.fRMouseButtonDown); 
 
    if (!rGameState.fLMouseButtonDown) 
    { 
        SetCapture(hWnd); 
 
        if (rGameState.fSetupMode) 
            SetupRButtonDown(hWnd, keyFlags, x, y); 
        else 
        { 
        // No-op. 
        }     
    } 
    else 
    { 
    // No-op. 
    } 
     
 
} 
 
VOID vHandle_WM_LBUTTONDOWN(HWND hWnd, BOOL fDoubleClick, int x, int y, UINT keyFlags) 
{ 
    AssertSz(!rGameState.fLMouseButtonDown, "How'd you do that?"); 
 
    TraceTag(tagMouse, "in vHandle_WM_LBUTTONDOWN"); 
    TraceTag(tagMouse, "rGameState.fLMouseButtonDown: %d", rGameState.fLMouseButtonDown); 
    TraceTag(tagMouse, "rGameState.fRMouseButtonDown: %d", rGameState.fRMouseButtonDown); 
 
    SetCapture(hWnd); 
 
    switch (rGameState.iCursorState) 
    { 
        case NORMAL_CURSOR: 
            TraceTag(tagMouse, "Move LButtonDown"); 
            if (!rGameState.fRMouseButtonDown) 
            { 
                SetCapture(hWnd); 
 
                if (rGameState.fSetupMode) 
                SetupLButtonDown(hWnd, keyFlags, x, y); 
                else 
                { 
                    AssertSz(((BLACK == rGameState.iPlayerTurn) ||  
                        (RED == rGameState.iPlayerTurn)),  
                        "Just how often am I going to check this?"); 
 
                    if ((!rGameState.fGameOver) && 
                    (HUMAN_PLAYER == rConfig.rPlayer[rGameState.iPlayerTurn].iPlayerType)) 
                    NormalLButtonDown(hWnd, keyFlags, x, y); 
                }     
            } 
            else 
            { 
                // NO-OP if other button already down 
            } 
            break; 
 
        case RESIZE_CURSOR: 
        { 
            TraceTag(tagMouse, "Resize LButtonDown"); 
            rGameState.fMouseResizeInProgress = TRUE; 
        } 
    }               // end switch 
         
} 
 
 
VOID vHandle_WM_LBUTTONUP(HWND hWnd, int x, int y, UINT keyFlags) 
{ 
    TraceTag(tagMouse, "in vHandle_WM_LBUTTONUP"); 
 
    TraceTag(tagMouse, "rGameState.fLMouseButtonDown: %d", rGameState.fLMouseButtonDown); 
    TraceTag(tagMouse, "rGameState.fRMouseButtonDown: %d", rGameState.fRMouseButtonDown); 
 
    ReleaseCapture(); 
     
    if (rGameState.fMouseResizeInProgress) 
    { 
        rGameState.fMouseResizeInProgress = FALSE; 
    } 
    else 
    { 
        if (rGameState.fSetupMode) 
            SetupLButtonUp(hWnd, keyFlags, x, y); 
        else 
            if (!rGameState.fGameOver) 
            NormalLButtonUp(hWnd, keyFlags, x, y); 
    } 
} 
 
VOID vHandle_WM_RBUTTONUP(HWND hWnd, int x, int y, UINT keyFlags) 
{ 
 
    TraceTag(tagMouse, "in vHandle_WM_RBUTTONUP"); 
 
    TraceTag(tagMouse, "rGameState.fLMouseButtonDown: %d", rGameState.fLMouseButtonDown); 
    TraceTag(tagMouse, "rGameState.fRMouseButtonDown: %d", rGameState.fRMouseButtonDown); 
 
    ReleaseCapture(); 
    if (rGameState.fSetupMode) 
    SetupRButtonUp(hWnd, keyFlags, x, y); 
    else 
    { 
    // NO-OP 
    } 
} 
 
VOID vHandle_WM_MOUSEMOVE(HWND hWnd, int x, int y, UINT keyFlags) 
{ 
 
    TraceTag(tagMouse, "in vHandle_WM_MOUSEMOVE. Mouse position: x:%d  y:%d", x, y); 
 
    if (rGameState.fRMouseButtonDown) 
    { 
        // NO-OP 
    } 
    else 
    { 
        if (rGameState.fLMouseButtonDown) 
        { 
            // NO-OP: Should be the actual mouse resize code. 
        } 
        else 
        { 
            if (rGameState.fMouseResizeInProgress) 
            { 
                int iNewBoardSize = 0; 
                int iNewSquareSize = 0; 
                HDC hDC = 0; 
                RECT rectInvalidate; 
 
                TraceTag(tagMouse, "End of mouse resize"); 
         
                iNewBoardSize = ((x - BOARD_LEFT) + (y - BOARD_TOP)) / 2; 
                TraceTag(tagMouse, "iNewBoardSize: %d", iNewBoardSize); 
 
                iNewSquareSize = iNewBoardSize / 8; 
 
                AssertSz(MAX_SQUARE_SIZE >= MIN_SQUARE_SIZE, "Max < Min. Duh."); 
         
                if (iNewSquareSize < MIN_SQUARE_SIZE) 
                    iNewSquareSize = MIN_SQUARE_SIZE; 
 
                if (iNewSquareSize > MAX_SQUARE_SIZE) 
                    iNewSquareSize = MAX_SQUARE_SIZE; 
 
                if (iNewSquareSize != rConfig.iSquareSize) 
                { 
                    rectInvalidate.left             = BOARD_LEFT; 
                    rectInvalidate.right    = BOARD_LEFT + rConfig.iSquareSize*13; 
                    rectInvalidate.top              = BOARD_TOP; 
                    rectInvalidate.bottom   = BOARD_TOP + rConfig.iSquareSize*8; 
 
                    InvalidateRect(hWnd, &rectInvalidate, TRUE); 
                    hDC = GetDC(hWnd); 
                    rConfig.iSquareSize = iNewSquareSize; 
                CheckGenPieces(hDC); 
                    UpdateWindow(hWnd); 
                DrawBoard(b);     
                } 
                 
                SetCursor(curResize); 
                TraceTag(tagMouse, "Resize now: x: %d, y: %d", x, y); 
            } 
            else 
            { 
                // User is in normal play/setup mode. No mouse down. We want to  
                // change the cursor based on whether we're scanning over pieces 
                // or over the "resize" area. 
 
                switch(rGameState.iCursorState) 
                { 
                    case NORMAL_CURSOR: 
                        if ((x > BOARD_LEFT + rConfig.iSquareSize*8 - RESIZE_RANGE) &&  
                        (x < BOARD_LEFT + rConfig.iSquareSize*8 + RESIZE_RANGE) && 
                            (y > BOARD_TOP + rConfig.iSquareSize*8 - RESIZE_RANGE) && 
                            (y < BOARD_TOP + rConfig.iSquareSize*8 + RESIZE_RANGE)) 
                        { 
                            TraceTag(tagMouse, "Setting RESIZE_CURSOR"); 
                            SetCursor(curResize); 
                            rGameState.iCursorState = RESIZE_CURSOR; 
                        } 
                        else 
                        { 
                            SetCursor(curNormal); 
                        } 
                        break; 
     
                    case RESIZE_CURSOR: 
                        if ((x < BOARD_LEFT + rConfig.iSquareSize*8 - RESIZE_RANGE) || 
                        (x > BOARD_LEFT + rConfig.iSquareSize*8 + RESIZE_RANGE) || 
                            (y < BOARD_TOP + rConfig.iSquareSize*8 - RESIZE_RANGE) || 
                            (y > BOARD_TOP + rConfig.iSquareSize*8 + RESIZE_RANGE)) 
                        { 
                            TraceTag(tagMouse, "Setting NORMAL_CURSOR"); 
                            SetCursor(curNormal); 
                            rGameState.iCursorState = NORMAL_CURSOR; 
                        } 
                        else 
                        { 
                            SetCursor(curResize); 
                        } 
                        break; 
                    default: 
                        AssertSz(FALSE, "Forest AND the trees"); 
                        break; 
                }                                                       // end switch 
            }                                                               // end else fMouseResizeInProgress 
        }                                                                       // end else fLMouseButtonDown 
    }                                                                               // end else fRMouseButtonDown 
} 
 
VOID vHandle_WM_PAINT(HWND hWnd) 
{ 
    PAINTSTRUCT ps; 
 
    BeginPaint(hWnd, (LPPAINTSTRUCT)&ps); 
    CheckPaint(b, ps.hdc); 
    EndPaint(hWnd, (LPPAINTSTRUCT)&ps); 
} 
 
VOID vHandle_WM_VSCROLL(HWND hWnd, HWND hWndCtl, UINT code, int pos) 
{ 
    AssertSz(FALSE, "When did I start getting VSCROLL messages?"); 
} 
 
VOID vHandle_WM_HSCROLL(HWND hWnd, HWND hWndCtl, UINT code, int pos) 
{ 
    AssertSz(FALSE, "When did I start getting HSCROLL messages?"); 
} 
 
VOID vHandle_WM_GETMINMAXINFO(HWND hWnd, MINMAXINFO FAR * lpMinMaxInfo) 
{ 
    // NO-OP 
} 
 
VOID vHandle_WM_COMMAND(HWND hWnd, int wmId, HWND hwndCtl, UINT wmEvent) 
{ 
    // Used by various commands 
 
    FARPROC lpProcDialog = NULL; 
    int iDialogBoxResult = -1; 
    RECT rectInvalidate; 
    HDC hDC = NULL; 
    HMENU hm = NULL; 
 
    switch (wmId) 
    { 
    #ifdef NEVER 
    case IDM_FILENEW: 
    { 
        int iResponse; 
 
        iResponse = MessageBox(hWnd, "You have asked to start " \ 
        "a new game. Note, this will end your current game. " \ 
"Would you like to save your game first?", "New Game?", 
        MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONQUESTION); 
 
        switch (iResponse) 
        { 
        case IDYES: 
            SaveAsBoard(b); 
            // We mean to fall through to the IDNO case below 
 
        case IDNO: 
            ClearBoard(b); 
            CMoves.ClearBoards(); 
                    rGameState.fGameOver = FALSE; 
                    rGameState.iPlayerTurn = BLACK; 
                    CMoves.NewMove(b, rGameState.iPlayerTurn); 
            DrawBoard(b); 
            break; 
 
        case IDCANCEL: 
                    DrawBoard(b); 
            break; 
        } 
        break; 
    } 
    #endif //never 
 
    case IDM_SAVE: 
        NYI("IDM_SAVE"); 
        break; 
 
    case IDM_GAME_CHECKERS: 
        if (rConfig.iGameType != GAME_CHECKERS) 
        { 
        hm = GetMenu(hWnd); 
        CheckMenuItem(hm, IDM_GAME_CHECKERS, MF_CHECKED); 
        CheckMenuItem(hm, IDM_GAME_GIVEAWAY, MF_UNCHECKED); 
        rConfig.iGameType = GAME_CHECKERS; 
        } 
        break; 
 
    case IDM_GAME_GIVEAWAY: 
        if (rConfig.iGameType != GAME_GIVEAWAY) 
        { 
        hm = GetMenu(hWnd); 
        CheckMenuItem(hm,IDM_GAME_CHECKERS,MF_UNCHECKED); 
        CheckMenuItem(hm,IDM_GAME_GIVEAWAY,MF_CHECKED); 
        rConfig.iGameType = GAME_GIVEAWAY; 
        } 
        break; 
 
    case IDM_SETUP_BOARD: 
 
        SetMenu(hWnd, LoadMenu(hInst, "WCheck_SETUP")); 
        rGameState.fSetupMode = TRUE; 
 
            rectInvalidate.left= BOARD_LEFT + rConfig.iSquareSize*10; 
            rectInvalidate.right = rectInvalidate.left + rConfig.iSquareSize; 
            rectInvalidate.top = BOARD_TOP + rConfig.iSquareSize*2; 
            rectInvalidate.bottom = BOARD_TOP + rConfig.iSquareSize*7; 
            InvalidateRect(hMainWnd, &rectInvalidate, TRUE); 
         
            //$$ () game over state 
            break; 
 
    case IDM_SETUP_EXIT: 
 
        lpProcDialog = MakeProcInstance ((FARPROC) PlayerTurnDialogProc, hInst); 
        iDialogBoxResult = DialogBox(hInst, "PLAYER_TURN",  
        hWnd, (DLGPROC) lpProcDialog); 
        if (iDialogBoxResult != -1)         
            { 
 
            AssertSz((BLACK == iDialogBoxResult) || 
                     (RED == iDialogBoxResult),  
                     "Darn tootin!"); 
         
        SetMenu(hWnd, LoadMenu(hInst, "WCheck_NORMAL")); 
                rGameState.iPlayerTurn = iDialogBoxResult; 
        rGameState.fSetupMode = FALSE; 
                rectInvalidate.left= BOARD_LEFT + rConfig.iSquareSize*10; 
                rectInvalidate.right = rectInvalidate.left + rConfig.iSquareSize; 
                rectInvalidate.top = BOARD_TOP + rConfig.iSquareSize*2; 
                rectInvalidate.bottom = BOARD_TOP + rConfig.iSquareSize*7; 
                InvalidateRect(hMainWnd, &rectInvalidate, TRUE); 
                if (rConfig.iSetupPurgeBoards) 
                    CMoves.ClearBoards(); 
                else 
                    CMoves.PurgeBoards(); 
                CMoves.NewMove(b, rGameState.iPlayerTurn); 
        } 
        break; 
 
    case IDM_PLAYERS: 
        lpProcDialog = MakeProcInstance ((FARPROC) PlayersDialogProc, hInst); 
        DialogBox (hInst, "PLAYERS", hWnd, (DLGPROC) lpProcDialog); 
        FreeProcInstance (lpProcDialog); 
        break; 
 
    case IDM_BACK: 
        { 
        // Change menu to say "Continue" 
        rGameState.fPaused = TRUE; 
                rGameState.fGameOver = FALSE; 
 
                // $$ () game over state 
 
        TraceTag(tagUI, "Calling CMoves.BackMove(b)"); 
        if (FALSE == CMoves.BackMove(b, &rGameState.iPlayerTurn)) 
                { 
                    TraceTag(tagUI, "BEEP: Can't back up."); 
            MessageBeep(MB_OK); 
                } 
        else 
            DrawBoard(b); 
        } 
        break; 
 
    case IDM_FORWARD: 
    { 
        // Change menu to say "Continue" 
        rGameState.fPaused = TRUE; 
 
            // $$ () game over state 
        TraceTag(tagUI, "Calling CMoves.ForwardMove(b)"); 
        if (FALSE == CMoves.ForwardMove(b, &rGameState.iPlayerTurn)) 
            { 
                TraceTag(tagUI, "BEEP: Can't go forward."); 
                MessageBeep(MB_OK); 
            } 
        else 
        DrawBoard(b); 
    } 
    break; 
 
        case IDM_FLIP: 
            hm = GetMenu(hWnd); 
            if (FALSE == rConfig.iBoardFlipped) 
            { 
                rConfig.iBoardFlipped = TRUE; 
                CheckMenuItem(hm, IDM_FLIP, MF_UNCHECKED); 
            } 
            else 
            { 
                rConfig.iBoardFlipped = FALSE; 
                CheckMenuItem(hm, IDM_FLIP, MF_CHECKED); 
            } 
 
            DrawBoard(b); 
            break; 
 
        case IDM_CONTINUE: 
 
        rGameState.fPaused = FALSE; 
        CMoves.PurgeBoards(); 
 
            // $$ () game over state 
 
        AssertSz(((BLACK == rGameState.iPlayerTurn) ||  
                (RED == rGameState.iPlayerTurn)),  
                "Who flicked that?"); 
 
        if (COMPUTER_PLAYER == rConfig.rPlayer[rGameState.iPlayerTurn].iPlayerType) 
        { 
        ComputerMove(); 
        } 
        break; 
 
        case IDM_SETDRAWMOVES: 
        lpProcDialog = MakeProcInstance ((FARPROC) DebugConfigDialogProc, hInst); 
        DialogBox (hInst, "DEBUG_CONFIG", hWnd, (DLGPROC) lpProcDialog); 
        FreeProcInstance (lpProcDialog); 
            break; 
 
 
    case IDM_BASE_LEVEL: 
    case IDM_BASE_LEVEL + 1: 
    case IDM_BASE_LEVEL + 2: 
    case IDM_BASE_LEVEL + 3: 
    case IDM_BASE_LEVEL + 4: 
    case IDM_BASE_LEVEL + 5: 
    case IDM_BASE_LEVEL + 6: 
    case IDM_BASE_LEVEL + 7: 
    case IDM_BASE_LEVEL + 8: 
    case IDM_BASE_LEVEL + 9: 
    case IDM_BASE_LEVEL + 10: 
    case IDM_BASE_LEVEL + 11: 
    case IDM_BASE_LEVEL + 12: 
    case IDM_BASE_LEVEL + 13: 
    case IDM_BASE_LEVEL + 14: 
    case IDM_BASE_LEVEL + 15: 
    case IDM_BASE_LEVEL + 16: 
    case IDM_BASE_LEVEL + 17: 
    case IDM_BASE_LEVEL + 18: 
    case IDM_BASE_LEVEL + 19: 
    case IDM_BASE_LEVEL + 20: 
        HMENU hm; 
        int i; 
 
        hm = GetMenu(hWnd); 
        i=20; 
        for (;i;i--) 
            CheckMenuItem(hm, IDM_BASE_LEVEL + i, MF_UNCHECKED); 
        CheckMenuItem(hm, wmId, MF_CHECKED); 
        rConfig.rPlayer[BLACK].iMaxRecursionDepth = 
            rConfig.rPlayer[RED].iMaxRecursionDepth = wmId - IDM_BASE_LEVEL; 
        break; 
 
    case IDM_HINT: 
        { 
        SQUARE      db[SQRS_MAX]; 
        int i; 
 
        SetCursor(curThink); 
        CopyBoard(b,db); 
        PlayBestMove(db, rGameState.iPlayerTurn, 0, 0, 
            (int)rConfig.rPlayer[rGameState.iPlayerTurn].lAlphaPruningOriginalDepth,    /* prune depth */ 
            (int)rConfig.rPlayer[rGameState.iPlayerTurn].lAlphaPruningWidth,    /* prune size */ 
            (int)rConfig.rPlayer[rGameState.iPlayerTurn].iMaxRecursionDepth   /* max depth */ 
            ); 
        SetCursor(curNormal); 
        for (i=0; i<3; ++i) 
        { 
        #define WAIT_TICKS 250 
        #ifndef _WIN32 
        DWORD startTick; 
        #endif 
        DrawBoard(db); 
        #ifdef _WIN32 
        Sleep(WAIT_TICKS); 
        #else 
        startTick = GetTickCount() + WAIT_TICKS; 
        while (GetTickCount() < startTick) /* do nothing */; 
        #endif 
        DrawBoard(b); 
        #ifdef _WIN32 
        Sleep(WAIT_TICKS); 
        #else 
        startTick = GetTickCount() + WAIT_TICKS; 
        while (GetTickCount() < startTick) /* do nothing */; 
        #endif 
        } 
 
         
        } 
        break; 
 
    case IDM_ADDRESS: 
        if (pfrm) // $ FORM 
        pfrm->AddressForm(hWnd,(BOOL) wmEvent); 
        else 
        MessageBeep(MB_OK); 
        break; 
 
        case IDM_ABOUT: 
             
            lpProcDialog = MakeProcInstance((FARPROC)About, hInst); 
 
            DialogBox(hInst,           // current instance 
                "AboutBox",            // dlg resource to use 
                hWnd,                  // parent handle 
                (DLGPROC)lpProcDialog); // About() instance address 
 
            FreeProcInstance(lpProcDialog); 
            break; 
 
        case IDM_EXIT: 
            DestroyWindow (hWnd); 
            break; 
    } 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
LRESULT CALLBACK CheckersWndProc( 
        HWND hWnd,         // window handle 
        UINT message,      // type of message 
        WPARAM wParam,     // additional information 
        LPARAM lParam)     // additional information 
 
{ 
    switch (message) 
    { 
    HANDLE_MSG(hWnd, WM_CREATE, fHandle_WM_CREATE); 
        HANDLE_MSG(hWnd, WM_INITMENU, vHandle_WM_INITMENU); 
        HANDLE_MSG(hWnd, WM_DESTROY, vHandle_WM_DESTROY); 
        HANDLE_MSG(hWnd, WM_TIMER, vHandle_WM_TIMER); 
        HANDLE_MSG(hWnd, WM_LBUTTONDOWN, vHandle_WM_LBUTTONDOWN); 
        HANDLE_MSG(hWnd, WM_LBUTTONUP, vHandle_WM_LBUTTONUP); 
        HANDLE_MSG(hWnd, WM_RBUTTONDOWN, vHandle_WM_RBUTTONDOWN); 
        HANDLE_MSG(hWnd, WM_RBUTTONUP, vHandle_WM_RBUTTONUP); 
        HANDLE_MSG(hWnd, WM_MOUSEMOVE, vHandle_WM_MOUSEMOVE); 
        HANDLE_MSG(hWnd, WM_PAINT, vHandle_WM_PAINT); 
        HANDLE_MSG(hWnd, WM_VSCROLL, vHandle_WM_VSCROLL); 
        HANDLE_MSG(hWnd, WM_HSCROLL, vHandle_WM_HSCROLL); 
        HANDLE_MSG(hWnd, WM_GETMINMAXINFO, vHandle_WM_GETMINMAXINFO); 
        HANDLE_MSG(hWnd, WM_COMMAND, vHandle_WM_COMMAND); 
 
        case EM_GIVEFORMTOHWND: 
 
        ShowWindow(hWnd, SW_SHOWNORMAL); 
 
        // ----- params valid 
        AssertSz(NULL == pfrm,"two forms?"); 
        AssertSz(lParam,"no forms?"); 
        pfrm = (FRM*) lParam; 
        TraceTag(tagForm,"wcheck pfrm->AddRef"); 
        pfrm->AddRef(); 
        Assert(pfrm->pMessage); 
 
        pfrm->GetCheckersData(b,&rGameState.iPlayerTurn,NULL,NULL); 
        AssertSz(rGameState.iPlayerTurn == RED || rGameState.iPlayerTurn == BLACK,"cool: neither red or blacks turn according to mapi"); 
        rConfig.iBoardFlipped = 1; 
        if (RED == rGameState.iPlayerTurn) 
        rConfig.iBoardFlipped = 0; 
        rGameState.fPaused = FALSE; 
 
 
        DrawBoard(b); 
 
        break; 
 
        case WM_OTHERINIT: 
 
        if (!rGameState.fPlayAsForm) 
        { 
        if (COMPUTER_PLAYER == rConfig.rPlayer[PLAYER1].iPlayerType) 
            { 
            MessageBox(hWnd, "Computer is configured to move first. The " \ 
            "game will start out paused. Select 'Continue' from the " \ 
            "main menu to start.", "Checkers", MB_OK); 
            rGameState.fPaused = TRUE; 
            } 
        } 
        else 
        { 
        rGameState.fPaused = TRUE; 
        } 
            rGameState.fInitDone = TRUE; 
        break; 
 
    case WM_CLOSE: 
        if (pfrm) 
        { 
        FRM *pfrml; //$FORM 
        pfrml = pfrm; 
        pfrm = NULL; 
        pfrml->ShutdownForm(OLECLOSE_NOSAVE); 
        pfrml->Release(); 
        } 
        return(DefWindowProc(hWnd, message, wParam, lParam)); 
 
        default:          // Passes it on if unproccessed 
            return (DefWindowProc(hWnd, message, wParam, lParam)); 
    } 
    return (0); 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
long WINAPI ComputerMove() 
{ 
    int iGameState = GAME_PLAYABLE; 
     
    rGameState.fComputerBusy = TRUE; 
    rGameState.fGameInProgress = TRUE; 
 
    TraceTag(tagUI, "Calling PlayBestMove()"); 
    SetCursor(curThink); 
 
    AssertSz(((BLACK == rGameState.iPlayerTurn) ||  
              (RED == rGameState.iPlayerTurn)),  
              "This one says x!"); 
 
        PlayBestMove(b, rGameState.iPlayerTurn, 0, 0, 
        (int)rConfig.rPlayer[rGameState.iPlayerTurn].lAlphaPruningOriginalDepth,    /* prune depth */ 
        (int)rConfig.rPlayer[rGameState.iPlayerTurn].lAlphaPruningWidth,    /* prune size */ 
        (int)rConfig.rPlayer[rGameState.iPlayerTurn].iMaxRecursionDepth   /* max depth */ 
        ); 
         
    SetCursor(curNormal); 
    TraceTag(tagUI, "Done with PlayBestMove()"); 
    DrawBoard(b); 
 
    if (rGameState.iPlayerTurn == BLACK) 
    rGameState.iPlayerTurn = RED; 
    else 
    rGameState.iPlayerTurn = BLACK; 
 
    CMoves.NewMove(b, rGameState.iPlayerTurn); 
 
    iGameState = GameOver(b, rGameState.iPlayerTurn); 
    switch (iGameState) 
    { 
    case GAME_WON: 
            rGameState.fGameOver = TRUE; 
            DisplayGameOver(hMainWnd, rGameState.iPlayerTurn); 
        break; 
        case GAME_DRAWN: 
        rGameState.fGameOver = TRUE; 
        DisplayGameDrawn(hMainWnd, rGameState.iPlayerTurn); 
            break;             
        case GAME_PLAYABLE: 
        OnGamePlayableAfterMove(); 
            break;          
        default: 
            AssertSz(FALSE, "STILL dead");                      
    } 
 
    rGameState.fComputerBusy = FALSE; 
     
    return(0); 
     
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
void inline SetupLButtonDown(HWND hWnd, UINT keyFlags, int x, int y) 
{ 
    int iSX = 0; 
    int iSY = 0; 
 
    (pntStart).x = x; 
    (pntStart).y = y; 
 
    vTranslateCurPosToSquare(pntStart, &iSX, &iSY); 
 
    if (fValidSetupPiece(b, iSX, iSY)) 
    { 
    HDC hDC; 
 
    SetCursor(curPiece); 
    hDC = GetDC(hWnd); 
    AssertSz((iSX >= 0) && ((iSX <= 7) || (iSX == 10)), "iSX out of range on ButtonDown"); 
    AssertSz((iSY >= 0) && (iSY <= 7), "iSY out of range on ButtonDown"); 
        if ((iSX != 10) && (!(GetKeyState(VK_CONTROL) & 0x8000))) 
            vClearMovingPiece(hDC, iSX, iSY); 
    rGameState.fMoveInProgress = TRUE; 
    } 
    else 
    { 
        TraceTag(tagUI, "BEEP: Not a valid setup piece"); 
    MessageBeep(MB_OK); 
    rGameState.fMoveInProgress = FALSE; 
    } 
    rGameState.fLMouseButtonDown = TRUE; 
    TraceTag(tagMouse, "rGameState.fLMouseButtonDown: %d", rGameState.fLMouseButtonDown); 
     
} 
 
void inline SetupRButtonDown(HWND hWnd, UINT keyFlags, int x, int y) 
{ 
    int iSX = 0; 
    int iSY = 0; 
 
    TraceTag(tagMouse, "in SetupRButtonDown()"); 
 
    (pntStart).x = x; 
    (pntStart).y = y; 
 
    vTranslateCurPosToSquare(pntStart, &iSX, &iSY); 
 
    if (fValidSetupPiece(b, iSX, iSY)) 
    { 
    HDC hDC; 
 
    SetCursor(curPiece); 
    hDC = GetDC(hWnd); 
    AssertSz((iSX >= 0) && ((iSX <= 7) || (iSX == 10)), "iSX out of range on ButtonDown"); 
    AssertSz((iSY >= 0) && (iSY <= 7), "iSY out of range on ButtonDown"); 
        vClearMovingPiece(hDC, iSX, iSY); 
    rGameState.fMoveInProgress = TRUE; 
    } 
    else 
    { 
        TraceTag(tagUI, "BEEP: Not a valid setup piece (in RButtonDown)"); 
    MessageBeep(MB_OK); 
    rGameState.fMoveInProgress = FALSE; 
    } 
    rGameState.fRMouseButtonDown = TRUE; 
    TraceTag(tagMouse, "rGameState.fRMouseButtonDown: %d", rGameState.fRMouseButtonDown); 
     
} 
 
void inline NormalLButtonDown(HWND hWnd, UINT keyFlags, int x, int y) 
{ 
    int iSX = 0; 
    int iSY = 0; 
 
    AssertSz((rGameState.iPlayerTurn == RED) || 
       (rGameState.iPlayerTurn == BLACK), "Player turn is bogus."); 
 
    TraceTag(tagMouse, "in NormalLButtonDown()"); 
 
    if ((HUMAN_PLAYER == rConfig.rPlayer[rGameState.iPlayerTurn].iPlayerType) &&  
    (!rGameState.fPaused)) 
    { 
    TraceTag(tagUI, "Button down at x:%d - y:%d", x, y); 
    (pntStart).x = x; 
    (pntStart).y = y; 
 
    vTranslateCurPosToSquare(pntStart, &iSX, &iSY); 
    if (fValidPlayerPiece(b, iSX, iSY)) 
    { 
        HDC hDC; 
 
        SetCursor(curPiece); 
        hDC = GetDC(hWnd); 
        AssertSz((iSX >= 0) && (iSX <= 7), "iSX out of range on ButtonDown"); 
        AssertSz((iSY >= 0) && (iSY <= 7), "iSY out of range on ButtonDown"); 
        vClearMovingPiece(hDC, iSX, iSY); 
        rGameState.fMoveInProgress = TRUE; 
    } 
    else 
    { 
            TraceTag(tagUI, "BEEP: Not a valid piece in LButtonDown"); 
        MessageBeep(MB_OK); 
        rGameState.fMoveInProgress = FALSE; 
    } 
    } 
    else 
    { 
        TraceTag(tagUI, "BEEP: Wrong player in LButtonDown"); 
    MessageBeep(MB_OK); 
    } 
 
    rGameState.fLMouseButtonDown = TRUE; 
    TraceTag(tagMouse, "rGameState.fLMouseButtonDown: %d", rGameState.fLMouseButtonDown); 
     
} 
 
void inline SetupLButtonUp(HWND hWnd, WPARAM uParam, int x, int y) 
{ 
 
    TraceTag(tagMouse, "in SetupLButtonUp()"); 
 
    if (rGameState.fMoveInProgress) 
    { 
    int     iEngineStart=0; 
    int     iEngineEnd=0; 
    int     iSX = 0; 
    int     iSY = 0; 
    int     iEX = 0; 
    int     iEY = 0; 
    BOOL    fRackSelect = FALSE; 
        POINT   pntEnd; 
 
    SetCursor(curNormal); 
 
    rGameState.fLMouseButtonDown = FALSE; 
    pntEnd.x = x; 
    pntEnd.y = y; 
     
    vTranslateCurPosToSquare(pntStart, &iSX, &iSY); 
        if ((iSX == 10) && (iSY >=2) && (iSY <= 6)) 
            fRackSelect = TRUE; 
        else 
        { 
            fRackSelect = FALSE; 
            vTranslateUIToEngineSquare(&iEngineStart, iSX, iSY); 
        } 
 
    vTranslateCurPosToSquare(pntEnd, &iEX, &iEY); 
    vTranslateUIToEngineSquare(&iEngineEnd, iEX, iEY); 
 
    TraceTag(tagUI, "Button up at x:%d - y:%d", pntEnd.x, pntEnd.y); 
 
        if (iEngineStart != iEngineEnd) 
        { 
        if ((iEngineEnd != -1) && (iEngineStart != -1) && (!fRackSelect)) 
        { 
            TraceTag(tagUI, "St: %d * End: %d", iEngineStart, iEngineEnd); 
 
            AssertSz(!fRackSelect, "Trains, Planes, and Automobiles"); 
            AssertSz(iEngineEnd < SQRS_MAX,"what could be a train"); 
            AssertSz(iEngineEnd > 0,"what could be trains"); 
            AssertSz(iEngineStart < SQRS_MAX,"two what could be a train"); 
            AssertSz(iEngineStart > 0,"two what could be trains"); 
 
                b[iEngineEnd] = b[iEngineStart]; 
 
            // If high-order bit is set, the control key is down, and I should 
            // do a copy (not remove the source piece). 
 
            if (!(GetKeyState(VK_CONTROL) & 0x8000)) 
                b[iEngineStart] = EMPTY; 
 
             
        } 
            else 
            { 
            AssertSz(iEngineEnd < SQRS_MAX,"while (TRUE) fork();"); 
            AssertSz((iEngineEnd > 0) || (-1 == iEngineEnd),"while (TRUE) spoon();"); 
             
                if ((iEngineEnd != -1) && (fRackSelect)) 
                { 
                    switch(iSY) 
                    { 
                        case 2: 
                            b[iEngineEnd] = RED; 
                            break; 
                        case 3: 
                            b[iEngineEnd] = BLACK; 
                            break; 
                        case 4: 
                            b[iEngineEnd] = RED | KING; 
                            break; 
                        case 5: 
                            b[iEngineEnd] = BLACK | KING; 
                            break; 
                        case 6: 
                            b[iEngineEnd] = EMPTY; 
                            break; 
                        default: 
                            AssertSz(FALSE, "Phenylketonurics: Contains Phenylalanine"); 
                            break; 
                    } 
                } 
        } 
    } 
    else 
    { 
        // This is what happens if we picked up a piece and dropped it on 
        // the same square. Right now this is a no-op. 
    } 
                                                           
    DrawBoard(b); 
    } 
 
    rGameState.fLMouseButtonDown = FALSE; 
    TraceTag(tagMouse, "rGameState.fLMouseButtonDown: %d", rGameState.fLMouseButtonDown); 
     
} 
 
void inline SetupRButtonUp(HWND hWnd, WPARAM uParam, int x, int y) 
{ 
 
    TraceTag(tagMouse, "in SetupRButtonUp()"); 
 
    if (rGameState.fMoveInProgress) 
    { 
    int     iEngineStart=0; 
    int     iEngineEnd=0;   
    int     iSX = 0; 
    int     iSY = 0; 
    int     iEX = 0; 
    int     iEY = 0; 
    BOOL    fRackSelect = FALSE; 
        POINT   pntEnd; 
 
    SetCursor(curNormal); 
 
    rGameState.fRMouseButtonDown = FALSE; 
    pntEnd.x = x; 
    pntEnd.y = y; 
     
    vTranslateCurPosToSquare(pntStart, &iSX, &iSY); 
        if ((iSX == 10) && (iSY >=2) && (iSY <= 6)) 
            fRackSelect = TRUE; 
        else 
        { 
            fRackSelect = FALSE; 
            vTranslateUIToEngineSquare(&iEngineStart, iSX, iSY); 
        } 
 
    vTranslateCurPosToSquare(pntEnd, &iEX, &iEY); 
    vTranslateUIToEngineSquare(&iEngineEnd, iEX, iEY); 
 
    TraceTag(tagUI, "Button up at x:%d - y:%d", pntEnd.x, pntEnd.y); 
 
        if (iEngineStart == iEngineEnd) 
        { 
        if ((iEngineStart != -1) && (!fRackSelect)) 
        { 
            TraceTag(tagUI, "St: %d * End: %d", iEngineStart, iEngineEnd); 
 
            AssertSz(!fRackSelect, "Trains, Planes, and Automobiles"); 
            AssertSz(iEngineEnd < SQRS_MAX,"what could be a train"); 
            AssertSz(iEngineEnd > 0,"what could be trains"); 
            AssertSz(iEngineStart < SQRS_MAX,"two what could be a train"); 
            AssertSz(iEngineStart > 0,"two what could be trains"); 
 
                b[iEngineStart] = EMPTY; 
        } 
    } 
    else 
    { 
        // This is what happens if we clicked on a piece but moved off of the 
        // original square.. Right now this is a no-op. 
    } 
                                                           
    DrawBoard(b); 
    } 
     
    rGameState.fRMouseButtonDown = FALSE; 
    TraceTag(tagMouse, "rGameState.fRMouseButtonDown: %d", rGameState.fRMouseButtonDown); 
 
} 
 
 
void inline NormalLButtonUp(HWND hWnd, WPARAM uParam, int x, int y) 
{ 
    int iGameState = GAME_PLAYABLE; 
 
    TraceTag(tagMouse, "in NormalLButtonUp()"); 
 
    if (rGameState.fMoveInProgress) 
    { 
    int     iEngineStart;                       
    int     iEngineEnd; 
    int     iValid = 0;    // Valid move 
    int     iSX = 0; 
    int     iSY = 0; 
    int     iEX = 0; 
    int     iEY = 0; 
    POINT   pntEnd; 
 
    SetCursor(curNormal); 
 
    rGameState.fLMouseButtonDown = FALSE; 
    pntEnd.x = x; 
    pntEnd.y = y; 
     
    vTranslateCurPosToSquare(pntStart, &iSX, &iSY); 
    vTranslateUIToEngineSquare(&iEngineStart, iSX, iSY); 
    vTranslateCurPosToSquare(pntEnd, &iEX, &iEY); 
    vTranslateUIToEngineSquare(&iEngineEnd, iEX, iEY); 
 
    TraceTag(tagUI, "Button up at x:%d - y:%d", pntEnd.x, pntEnd.y); 
 
    if ((iEngineEnd != -1) && (iEngineStart != -1)) 
    { 
        TraceTag(tagUI, "St: %d * End: %d", iEngineStart, iEngineEnd); 
 
        AssertSz(((BLACK == rGameState.iPlayerTurn) ||  
              (RED == rGameState.iPlayerTurn)), "Fronk, lots!"); 
               
        if (HUMAN_PLAYER == rConfig.rPlayer[rGameState.iPlayerTurn].iPlayerType) 
        { 
        AssertSz(iEngineEnd < SQRS_MAX,"ugly players what could be a train"); 
        AssertSz(iEngineEnd >= 0,"ugly players what could be trains"); 
        AssertSz(iEngineStart < SQRS_MAX,"ugly players two what could be a train"); 
        AssertSz(iEngineStart >= 0,"ugly players two what could be trains"); 
        iValid = MoveValid(b, iEngineStart, iEngineEnd, rGameState.iPlayerTurn); 
        } 
    } 
    else 
        iValid = -1; 
 
    DrawBoard(b); 
 
        if (iValid == 1) 
        { 
        if (rGameState.iPlayerTurn == BLACK) 
            rGameState.iPlayerTurn = RED; 
        else 
            rGameState.iPlayerTurn = BLACK; 
     
        CMoves.NewMove(b, rGameState.iPlayerTurn); 
            iGameState = GameOver(b, rGameState.iPlayerTurn); 
            switch (iGameState) 
            { 
            case GAME_WON: 
                    rGameState.fGameOver = TRUE; 
                    DisplayGameOver(hMainWnd, rGameState.iPlayerTurn); 
                break; 
                case GAME_DRAWN: 
                rGameState.fGameOver = TRUE; 
                DisplayGameDrawn(hMainWnd, rGameState.iPlayerTurn); 
                    break;             
                case GAME_PLAYABLE: 
            OnGamePlayableAfterMove(); 
                    break;          
                default: 
                    AssertSz(FALSE, "very dead");                       
            } 
        } 
    } 
    rGameState.fMoveInProgress = FALSE; 
    rGameState.fLMouseButtonDown = FALSE; 
    TraceTag(tagMouse, "rGameState.fLMouseButtonDown: %d", rGameState.fLMouseButtonDown); 
     
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
BOOL CenterWindow (HWND hwndChild, HWND hwndParent) 
{ 
    RECT    rChild, rParent; 
    int     wChild, hChild, wParent, hParent; 
    int     wScreen, hScreen, xNew, yNew; 
    HDC     hdc; 
 
    // Get the Height and Width of the child window 
    GetWindowRect (hwndChild, &rChild); 
    wChild = rChild.right - rChild.left; 
    hChild = rChild.bottom - rChild.top; 
 
    // Get the Height and Width of the parent window 
    GetWindowRect (hwndParent, &rParent); 
    wParent = rParent.right - rParent.left; 
    hParent = rParent.bottom - rParent.top; 
 
    // Get the display limits 
    hdc = GetDC (hwndChild); 
    wScreen = GetDeviceCaps (hdc, HORZRES); 
    hScreen = GetDeviceCaps (hdc, VERTRES); 
    ReleaseDC (hwndChild, hdc); 
 
    // Calculate new X position, then adjust for screen 
    xNew = rParent.left + ((wParent - wChild) /2); 
    if (xNew < 0) { 
        xNew = 0; 
    } else if ((xNew+wChild) > wScreen) { 
        xNew = wScreen - wChild; 
    } 
 
    // Calculate new Y position, then adjust for screen 
    yNew = rParent.top  + ((hParent - hChild) /2); 
    if (yNew < 0) { 
        yNew = 0; 
    } else if ((yNew+hChild) > hScreen) { 
        yNew = hScreen - hChild; 
    } 
 
    // Set it, and return 
    return SetWindowPos (hwndChild, NULL, 
        xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER); 
} 
 
/**************************************************************************** 
***************************************************************************** 
***************************************************************************** 
****************************************************************************/ 
 
BOOL FAR PASCAL ComputerSettingsDialogProc (HWND hDlg, 
                    WORD wMsgID, 
                    WPARAM wParam, 
                    LPARAM lParam) 
{ 
    static struct rPlayerRec *prPlayer; 
 
    switch (wMsgID) 
 
    { 
    case WM_INITDIALOG: 
        prPlayer = (struct rPlayerRec FAR *) lParam; 
        Assert(prPlayer); 
        SendDlgItemMessage(hDlg, IDCB_USE_OPENING_BOOK, BM_SETCHECK, prPlayer->iUseOpeningBook, 0L); 
        SendDlgItemMessage(hDlg, IDCB_USE_MEM_POSITIONS, BM_SETCHECK, prPlayer->iMemPositions, 0L); 
        SendDlgItemMessage(hDlg, IDCB_USE_MOVE_THEORY, BM_SETCHECK, prPlayer->iUseMoveTheory, 0L); 
        SendDlgItemMessage(hDlg, IDCB_MOVE_SHUFFLING, BM_SETCHECK, prPlayer->iUseMoveShuffling, 0L); 
        SendDlgItemMessage(hDlg, IDCB_GOOD_MOVE_SKIPPING, BM_SETCHECK, (int)prPlayer->lUseGoodMoveSkipping, 0L); 
        SendDlgItemMessage(hDlg, IDCB_EQUAL_MOVE_SKIPPING, BM_SETCHECK, prPlayer->iUseEqualMoveSkipping, 0L); 
        SendDlgItemMessage(hDlg, IDCB_ALPHA_BETA_PRUNING, BM_SETCHECK, prPlayer->iUseAlphaBetaPruning, 0L); 
 
        // Set other edit controls 
 
        SetDlgItemLong(hDlg, IDEC_RECURSION_DEPTH, prPlayer->iMaxRecursionDepth); 
            SetDlgItemLong(hDlg, IDEC_PRUNING_DEPTH, prPlayer->lAlphaPruningOriginalDepth); 
            SetDlgItemLong(hDlg, IDEC_PRUNING_WIDTH, prPlayer->lAlphaPruningWidth); 
        return TRUE; 
 
    case WM_COMMAND: 
        switch (wParam) 
        { 
        case IDOK: 
            EndDialog (hDlg, TRUE); 
             
            // Get Checkbox values 
            prPlayer->iUseOpeningBook = (int)SendDlgItemMessage(hDlg, IDCB_USE_OPENING_BOOK, BM_GETCHECK, 0, 0L); 
            prPlayer->iMemPositions = (int)SendDlgItemMessage(hDlg, IDCB_USE_MEM_POSITIONS, BM_GETCHECK, 0, 0L); 
            prPlayer->iUseMoveTheory = (int)SendDlgItemMessage(hDlg, IDCB_USE_MOVE_THEORY, BM_GETCHECK, 0, 0L); 
            prPlayer->iUseMoveShuffling = (int)SendDlgItemMessage(hDlg, IDCB_MOVE_SHUFFLING, BM_GETCHECK, 0, 0L); 
prPlayer->lUseGoodMoveSkipping = (int)SendDlgItemMessage(hDlg, IDCB_GOOD_MOVE_SKIPPING, BM_GETCHECK, 0, 0L); 
            prPlayer->iUseEqualMoveSkipping = (int)SendDlgItemMessage(hDlg, IDCB_EQUAL_MOVE_SKIPPING, BM_GETCHECK, 0, 0L); 
            prPlayer->iUseAlphaBetaPruning = (int)SendDlgItemMessage(hDlg, IDCB_ALPHA_BETA_PRUNING, BM_GETCHECK, 0, 0L); 
            prPlayer->iMemPositions = (int)SendDlgItemMessage(hDlg, IDCB_USE_MEM_POSITIONS, BM_GETCHECK, 0, 0L); 
 
            // Get Edit Control Values 
 
            prPlayer->iMaxRecursionDepth         = GetDlgItemInt(hDlg, IDEC_RECURSION_DEPTH); 
                    prPlayer->lAlphaPruningOriginalDepth = GetDlgItemLong(hDlg, IDEC_PRUNING_DEPTH); 
                    prPlayer->lAlphaPruningWidth         = GetDlgItemLong(hDlg, IDEC_PRUNING_WIDTH); 
 
            return TRUE; 
 
        case IDCANCEL: 
            EndDialog (hDlg, TRUE); 
            return TRUE; 
        }                                    // end switch 
        break; 
    } 
    return FALSE; 
} 
 
BOOL FAR PASCAL PlayersDialogProc (HWND hDlg, 
                    WORD wMsgID, 
                    WPARAM wParam, 
                    LPARAM lParam) 
{ 
    switch (wMsgID) 
 
    { 
    case WM_INITDIALOG: 
        switch(rConfig.rPlayer[PLAYER1].iPlayerType) 
        { 
        case HUMAN_PLAYER: 
            SendDlgItemMessage(hDlg, IDRB_P1_HUMAN, BM_SETCHECK, 1, 0L); 
            break; 
        case COMPUTER_PLAYER: 
            SendDlgItemMessage(hDlg, IDRB_P1_COMPUTER, BM_SETCHECK, 1, 0L); 
            break; 
        case NETWORK_PLAYER: 
            SendDlgItemMessage(hDlg, IDRB_P1_NETWORK, BM_SETCHECK, 1, 0L); 
            break; 
        default: 
            AssertSz(FALSE, "rConfig.rPlayer[PLAYER1].iPlayerType not valid"); 
            break; 
        } 
         
        switch(rConfig.rPlayer[PLAYER2].iPlayerType) 
        { 
        case HUMAN_PLAYER: 
            SendDlgItemMessage(hDlg, IDRB_P2_HUMAN, BM_SETCHECK, 1, 0L); 
            break; 
        case COMPUTER_PLAYER: 
            SendDlgItemMessage(hDlg, IDRB_P2_COMPUTER, BM_SETCHECK, 1, 0L); 
            break; 
        case NETWORK_PLAYER: 
            SendDlgItemMessage(hDlg, IDRB_P2_NETWORK, BM_SETCHECK, 1, 0L); 
            break; 
        default: 
            AssertSz(FALSE, "rConfig.rPlayer[PLAYER2].iPlayerType not valid"); 
            break; 
        } 
 
        return TRUE; 
 
    case WM_COMMAND: 
        switch (wParam) 
        { 
        case IDB_P1_COMPUTER_SETUP: 
 
            DialogBoxParam (hInst,       // current instance 
                    "COMPUTER_SETUP_MASTER", // dlg resource to use 
                    hDlg,        // parent handle 
                    (DLGPROC)ComputerSettingsDialogProc, // Config() instance address 
                    (LPARAM) (struct rPlayerRec *) (&(rConfig.rPlayer[PLAYER1]))); 
            break; 
 
        case IDB_P2_COMPUTER_SETUP: 
 
            DialogBoxParam (hInst,       // current instance 
                    "COMPUTER_SETUP_MASTER", // dlg resource to use 
                    hDlg,        // parent handle 
                    (DLGPROC)ComputerSettingsDialogProc, // Config() instance address 
                    (LPARAM) (struct rPlayerRec *) (&(rConfig.rPlayer[PLAYER2]))); 
            break; 
         
        case IDB_P1_NETWORK_SETUP: 
        case IDB_P2_NETWORK_SETUP: 
            NYI("Player Network Setup"); 
            break; 
 
        case IDOK: 
            EndDialog (hDlg, TRUE); 
            if (SendDlgItemMessage(hDlg, IDRB_P1_HUMAN, BM_GETCHECK, 0, 0L)) 
            rConfig.rPlayer[PLAYER1].iPlayerType = HUMAN_PLAYER; 
            if (SendDlgItemMessage(hDlg, IDRB_P1_COMPUTER, BM_GETCHECK, 0, 0L)) 
            rConfig.rPlayer[PLAYER1].iPlayerType = COMPUTER_PLAYER; 
            if (SendDlgItemMessage(hDlg, IDRB_P1_NETWORK, BM_GETCHECK, 0, 0L)) 
            rConfig.rPlayer[PLAYER1].iPlayerType = NETWORK_PLAYER; 
            if (SendDlgItemMessage(hDlg, IDRB_P2_HUMAN, BM_GETCHECK, 0, 0L)) 
            rConfig.rPlayer[PLAYER2].iPlayerType = HUMAN_PLAYER; 
            if (SendDlgItemMessage(hDlg, IDRB_P2_COMPUTER, BM_GETCHECK, 0, 0L)) 
            rConfig.rPlayer[PLAYER2].iPlayerType = COMPUTER_PLAYER; 
            if (SendDlgItemMessage(hDlg, IDRB_P2_NETWORK, BM_GETCHECK, 0, 0L)) 
            rConfig.rPlayer[PLAYER2].iPlayerType = NETWORK_PLAYER; 
            return TRUE; 
 
        case IDCANCEL: 
            EndDialog (hDlg, TRUE); 
            return TRUE; 
        }                                    // end switch 
        break; 
    } 
    return FALSE; 
} 
 
 
BOOL FAR PASCAL DebugConfigDialogProc (HWND hDlg, 
                    WORD wMsgID, 
                    WPARAM wParam, 
                    LPARAM lParam) 
{ 
    switch (wMsgID) 
 
    { 
    case WM_INITDIALOG: 
        SetDlgItemInt(hDlg, IDEC_MAX_MOVES, rConfig.iMaxMoves); 
        return TRUE; 
 
    case WM_COMMAND: 
        switch (wParam) 
        { 
        case IDOK: 
            EndDialog (hDlg, TRUE); 
            rConfig.iMaxMoves = GetDlgItemInt(hDlg, IDEC_MAX_MOVES); 
            return TRUE; 
 
        case IDCANCEL: 
            EndDialog (hDlg, TRUE); 
            return TRUE; 
        }                                    // end switch 
        break; 
    } 
    return FALSE; 
} 
 
 
int FAR PASCAL PlayerTurnDialogProc (HWND hDlg, 
                    WORD wMsgID, 
                    WPARAM wParam, 
                    LPARAM lParam) 
{ 
    switch (wMsgID) 
 
    { 
    case WM_INITDIALOG: 
            CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER)); 
        return TRUE; 
 
    case WM_COMMAND: 
        switch (wParam) 
        { 
        case IDB_PLAYER1: 
            EndDialog (hDlg, PLAYER1); 
            return TRUE; 
 
        case IDB_PLAYER2: 
            EndDialog (hDlg, PLAYER2); 
            return TRUE; 
         
        case IDCANCEL: 
            // Return -1 so the calling function will know that the result 
            // from the dialog call isn't equal to a player ID. 
             
            EndDialog (hDlg, -1); 
            break;     
        }                                    // end switch 
        break; 
    } 
    return FALSE; 
} 
 
 
LRESULT CALLBACK About( 
        HWND hDlg,           // window handle of the dialog box 
        UINT message,        // type of message 
        WPARAM wParam,       // message-specific information 
        LPARAM lParam) 
{ 
    static  HFONT hfontDlg; 
    static  LPSTR   lpVersion; 
    static  DWORD   dwVerInfoSize; 
    static  DWORD   dwVerHnd; 
    static  UINT    uVersionLen; 
    static  WORD    wRootLen; 
    BOOL    bRetCode; 
    static  int     i; 
    static  char    szFullPath[256]; 
    static  char    szResult[256]; 
    static  char    szGetName[256]; 
 
    switch (message) { 
        case WM_INITDIALOG:  // message: initialize dialog box 
            // Create a font to use 
            hfontDlg = CreateFont(14, 0, 0, 0, 0, 0, 0, 0, 
                0, 0, 0, 0, 
                VARIABLE_PITCH | FF_SWISS, ""); 
 
            // Center the dialog over the application window 
            CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER)); 
 
            // Get version information from the application 
            GetModuleFileName (hInst, szFullPath, sizeof(szFullPath)); 
            dwVerInfoSize = GetFileVersionInfoSize(szFullPath, &dwVerHnd); 
            if (dwVerInfoSize) { 
                // If we were able to get the information, process it: 
                LPSTR   lpstrVffInfo; 
                HANDLE  hMem; 
                hMem = GlobalAlloc(GMEM_MOVEABLE, dwVerInfoSize); 
                lpstrVffInfo  = (char *)GlobalLock(hMem); 
                GetFileVersionInfo(szFullPath, dwVerHnd, dwVerInfoSize, lpstrVffInfo); 
                lstrcpy(szGetName, "\\StringFileInfo\\040904E4\\"); 
                wRootLen = lstrlen(szGetName); 
 
                // Walk through the dialog items that we want to replace: 
                for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) { 
                    GetDlgItemText(hDlg, i, szResult, sizeof(szResult)); 
                    szGetName[wRootLen] = (char)0; 
                    lstrcat (szGetName, szResult); 
                    uVersionLen   = 0; 
                    lpVersion     = NULL; 
                      bRetCode      =  VerQueryValue((LPVOID)lpstrVffInfo, 
                          (LPSTR)szGetName, 
                          (LPVOID *)&lpVersion, 
#if defined (_WIN32) 
                          (PUINT)&uVersionLen); // For MIPS strictness 
#else 
                          (UINT FAR *)&uVersionLen); 
#endif 
 
                      if ( bRetCode && uVersionLen && lpVersion) { 
                          // Replace dialog item text with version info 
                          lstrcpy(szResult, lpVersion); 
                          SetDlgItemText(hDlg, i, szResult); 
                          SendMessage (GetDlgItem (hDlg, i), WM_SETFONT, (UINT)hfontDlg, TRUE); 
                      } 
                } // for (i = DLG_VERFIRST; i <= DLG_VERLAST; i++) 
 
                GlobalUnlock(hMem); 
                GlobalFree(hMem); 
            } // if (dwVerInfoSize) 
 
            return (TRUE); 
 
        case WM_COMMAND:                      // message: received a command 
            if (LOWORD(wParam) == IDOK        // "OK" box selected? 
            || LOWORD(wParam) == IDCANCEL) {  // System menu close command? 
                EndDialog(hDlg, TRUE);        // Exit the dialog 
                DeleteObject (hfontDlg); 
                return (TRUE); 
            } 
            break; 
    } 
    return (FALSE); // Didn't process the message 
 
    lParam; // This will prevent 'unused formal parameter' warnings 
}