/* --------------------------------------------------------------------------

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();

    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;    // 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
}

