//REGEN_FILEHEADING
#include <commdlg.h>
#include <windowsx.h>
#include <fstream.h>
#include <math.h>
//REGEN_FILEHEADING


     /********************************************************************
      *                                                                  *
      *   Source File: Flipit.cpp                                        *
      *   Description: C++ Source file for Flipit application            *
      *   Date:        Sat Jun 25 15:12:28 1994                          *
      *                                                                  *
      ********************************************************************/

#include <owl.h>
#include <edit.h>
#include <listbox.h>
#include <combobox.h>
#include <scrollba.h>
#include <dialog.h>
#include <bwcc.h>
#include "FlipitID.h"
#include "Flipit.h"
#include "FlipiCls.h"


//REGEN_VARIABLES
int Player = 0,nPass = 0,Player1 = 1, Player2 = 2;
int NumPlayers = 2;
int BoardSize = 8,OldBoardSize = 8;
POINT LastMove;
char SaveFile[255] = "";
int nSize,nSquare,nXmargin,nYmargin;
int Difficulty=MEDIUM, OldDifficulty=MEDIUM;
int nStatusBar;
BOOL bDefault=FALSE, bReFresh=FALSE;

class TUndo
{ public:
    char UndoArr[12][12];
    int UndoPlayer,nUndoPass;
    int nUndoWhiteScore,nUndoBlackScore;
    POINT UndoLastMove;
    friend istream& operator >> ( istream&, TUndo& );
                                // reads a Board from a stream.  The
                                // format used must match the format used
                                // by operator <<.

    friend ostream& operator << ( ostream&, TUndo& );
                                // writes a Board to a stream.  The
                                // format used must match the format used
                                // by operator >>.
    friend istream& operator >> ( istream&, POINT& );
                                // reads a Point from a stream.  The
                                // format used must match the format used
                                // by operator <<.

    friend ostream& operator << ( ostream&, POINT& );
                                // writes a POINT to a stream.  The
                                // format used must match the format used
				// by operator >>.
};

struct WEIGHTTAG
{
  long double Mult;
  int x,y;
};
class TBoard
{ public:
    char BoardArr[12][12];

    WEIGHTTAG Weight[100];
    BOOL bFlip,bCheat;
    POINT Vectors[8];
    TBoard();
    int nWhiteScore,nBlackScore;

    virtual void TBoard::InitBoard(BOOL bFirst);
    virtual void DrawBoard(HDC PaintDC);
    virtual void FlipBoard(HDC PaintDC);
    virtual BOOL ValidMove(POINT p);
    virtual void MakeMove(POINT p);
    virtual void DrawSquare(HDC PaintDC, RECT r, int Shadow);
    virtual void PlacePiece(HDC PaintDC, RECT r, int nRow, int nCol);
    virtual int  ChkVector(HDC PaintDC, int Col, int Row, int Vector,
			       int Other, int Player, BOOL Flip);
    virtual void UpdScore(HDC PaintDC);
    virtual long double GoComputer(int Player, int Level);
    friend istream& operator >> ( istream&, TBoard& );
                                // reads a Board from a stream.  The
                                // format used must match the format used
                                // by operator <<.

    friend ostream& operator << ( ostream&, TBoard& );
                                // writes a Board to a stream.  The
                                // format used must match the format used
                                // by operator >>.
    friend istream& operator >> ( istream&, POINT& );
                                // reads a Point from a stream.  The
                                // format used must match the format used
                                // by operator <<.

    friend ostream& operator << ( ostream&, POINT& );
                                // writes a POINT to a stream.  The
                                // format used must match the format used
				// by operator >>.
};


TBoard Board;
TUndo Undo;

void Undo2Board( TUndo& , TBoard& );
void Board2Undo( TBoard& , TUndo& );

HINSTANCE ghInstance;
HWND ghWindow;
//REGEN_VARIABLES

// Define application class derived from  TApplication
class TFlipit : public TApplication
{
public:
  TFlipit(LPSTR AName, HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine, int nCmdShow)
    : TApplication(AName, hInstance, hPrevInstance, lpCmdLine, nCmdShow) {};
    virtual void InitMainWindow();
    virtual void InitInstance();
    //REGEN_APPCLASS
    //REGEN_APPCLASS
};

void TFlipit::InitInstance()
{
   TApplication::InitInstance();
   HAccTable = LoadAccelerators(hInstance, "Flipit");

}
// Declare TMainWindow, a TWindow descendant
class TMainWindow : public TWindow
{
public:
   TMainWindow(PTWindowsObject AParent, LPSTR ATitle);
   ~TMainWindow();
   virtual void NEW(RTMessage Msg) = [CM_FIRST + IDM_NEW];
   virtual void OPEN(RTMessage Msg) = [CM_FIRST + IDM_OPEN];
   virtual void SAVEAS(RTMessage Msg) = [CM_FIRST + IDM_SAVEAS];
   virtual void EXIT(RTMessage Msg) = [CM_FIRST + IDM_EXIT];
   virtual void UNDO(RTMessage Msg) = [CM_FIRST + IDM_UNDO];
   virtual void HINT(RTMessage Msg) = [CM_FIRST + IDM_HINT];
   virtual void CHEAT(RTMessage Msg) = [CM_FIRST + IDM_CHEAT];
   virtual void PREFERENCES(RTMessage Msg) = [CM_FIRST + IDM_PREFERENCES];
   virtual void CONTENTS(RTMessage Msg) = [CM_FIRST + IDM_CONTENTS];
   virtual void ABOUT(RTMessage Msg) = [CM_FIRST + IDM_ABOUT];
   //REGEN_MAINCLASS
   virtual void Paint(HDC PaintDC, PAINTSTRUCT &PaintInfo);
   virtual void WMLButtonDown(RTMessage Msg) = [WM_FIRST+WM_LBUTTONDOWN];
   virtual void WMSize(RTMessage Msg) = [WM_FIRST + WM_SIZE];
   virtual void WMMouseMove(RTMessage Msg) = [WM_FIRST + WM_MOUSEMOVE];
   virtual void WMTimer(RTMessage Msg) = [WM_FIRST + WM_TIMER];
   virtual int WMSetFocus(RTMessage Msg) = [WM_FIRST + WM_SETFOCUS];
   virtual int WMKillFocus(RTMessage Msg) = [WM_FIRST + WM_KILLFOCUS];
   virtual void WMInitMenu(RTMessage Msg) = [WM_FIRST + WM_INITMENU];
   virtual int WMMenuSelect(RTMessage Msg) = [WM_FIRST + WM_MENUSELECT];
   virtual void SetupWindow();
   virtual void OpenFiFile( LPSTR lpstrFile );
   virtual void SaveFiFile( LPSTR lpstrFile );
private:
   HBRUSH hMainBrush;
   //REGEN_MAINCLASS

protected:
   virtual void GetWindowClass(WNDCLASS _FAR & AWndClass);
   virtual LPSTR GetClassName();
};


/****************************************************
 * TMainWindow implementations: 
 ****************************************************/

// Define TMainWindow, a TWindow constructor
TMainWindow::TMainWindow(PTWindowsObject AParent, LPSTR ATitle)
                         : TWindow(AParent, ATitle)
{
   AssignMenu("Flipit");
   //REGEN_MAINCONSTRUCT
   Attr.W = 400;
   Attr.H = 400;
   NumPlayers = Player = 1;
   OldBoardSize = BoardSize = GetPrivateProfileInt("Preferences", "BoardSize", 8, "FLIPIT.INI");
   nSize = nSquare = nXmargin = nYmargin = 0;
   Board2Undo(Board,Undo);
   //REGEN_MAINCONSTRUCT

}

// Define TMainWindow destructor
TMainWindow::~TMainWindow()
{
   //REGEN_MAINDESTRUCT
   KillTimer(HWindow,10);
   DeleteBrush(hMainBrush);
   //REGEN_MAINDESTRUCT

}

LPSTR TMainWindow::GetClassName()
{
   return "MainWindow";
}

void TMainWindow::GetWindowClass(WNDCLASS _FAR & AWndClass)
{
   TWindow::GetWindowClass(AWndClass);
   AWndClass.hIcon = LoadIcon(AWndClass.hInstance, "FLIPIT");
   //REGEN_CLASSINFO
   hMainBrush = CreateSolidBrush(RGB(0,128,0));
   AWndClass.hbrBackground = hMainBrush;
   AWndClass.hCursor = NULL;
   //REGEN_CLASSINFO

}

void TMainWindow::NEW(RTMessage)
{
   //REGEN_NEW_EXEC
   Board.InitBoard(FALSE);
   InvalidateRect(HWindow,NULL,TRUE);
   //REGEN_NEW_EXEC
}

void TMainWindow::OPEN(RTMessage Msg)
{
   //REGEN_OPEN_EXEC
   OPENFILENAME Ofn;
   HFILE hFile;
   OFSTRUCT FAR* lpOpenBuff;
   int j;

   
   char Filters[] = "Flip It Files (*.fi)\0*.fi\0"
		    "All Files (*.*)\0*.*\0";

   WMInitMenu(Msg);

   memset(&Ofn, 0, sizeof(OPENFILENAME));
   Ofn.lpstrTitle = "Choose a File";
   Ofn.hwndOwner  = HWindow;
   Ofn.lpstrFilter = (LPSTR) Filters;
   Ofn.nFilterIndex = 1;
   Ofn.lpstrFile = (LPSTR)SaveFile;
   Ofn.nMaxFile = sizeof(SaveFile);
   Ofn.Flags = OFN_FILEMUSTEXIST | \
			OFN_HIDEREADONLY | \
			OFN_PATHMUSTEXIST;
   Ofn.lpstrDefExt = "fi";
   Ofn.lStructSize = sizeof(OPENFILENAME);

   GetOpenFileName(&Ofn);
   OpenFiFile( Ofn.lpstrFile );

   WMSize(Msg);
   WMSetFocus(Msg);
   InvalidateRect(HWindow,NULL,FALSE);

   //REGEN_OPEN_EXEC
}

void TMainWindow::SAVEAS(RTMessage Msg)
{
   //REGEN_SAVEAS_EXEC
   OPENFILENAME Ofn;
   HFILE hFile;
   OFSTRUCT FAR* lpOpenBuff;
   int j;

   char Filters[] = "Flip It Files (*.fi)\0*.fi\0"
		    "All Files (*.*)\0*.*\0";
   WMInitMenu(Msg);
   memset(&Ofn, 0, sizeof(OPENFILENAME));
   Ofn.lpstrTitle = "Save File As";
   Ofn.hwndOwner  = HWindow;
   Ofn.lpstrFilter = (LPSTR) Filters;
   Ofn.nFilterIndex = 1;
   Ofn.lpstrFile = (LPSTR)SaveFile;
   Ofn.nMaxFile = sizeof(SaveFile);
   Ofn.Flags = OFN_FILEMUSTEXIST | \
			OFN_HIDEREADONLY | \
			OFN_PATHMUSTEXIST;
   Ofn.lpstrDefExt = "fi";
   Ofn.lStructSize = sizeof(OPENFILENAME);

   GetSaveFileName(&Ofn);
   SaveFiFile( Ofn.lpstrFile );
   WMSetFocus(Msg);
   //REGEN_SAVEAS_EXEC

}

void TMainWindow::EXIT(RTMessage)
{
   //REGEN_EXIT_EXEC
   CloseWindow();
   //REGEN_EXIT_EXEC
}

void TMainWindow::UNDO(RTMessage)
{
   //REGEN_UNDO_EXEC
   Undo2Board(Undo,Board);
   InvalidateRect(HWindow,NULL,FALSE);
   //REGEN_UNDO_EXEC
}

void TMainWindow::HINT(RTMessage)
{
   //REGEN_HINT_EXEC
    SetCursor(LoadCursor(NULL,IDC_WAIT));
    Board2Undo(Board,Undo);
    Board.GoComputer(Player,1);
    SetCursor(LoadCursor(NULL,IDC_ARROW));
   //REGEN_HINT_EXEC
}

void TMainWindow::CHEAT(RTMessage)
{
   //REGEN_CHEAT_EXEC
   Board.bCheat = TRUE;
   //REGEN_CHEAT_EXEC
}

void TMainWindow::PREFERENCES(RTMessage Msg)
{
   // Execute modal dialog
   if (GetModule()->ExecDialog(
               new TPREFERDlg(this, "PREFER")) == IDOK )
   {
   //REGEN_PREFERENCES_EXEC
     if (OldBoardSize != BoardSize || OldDifficulty != Difficulty)
     {
       NEW(Msg);
       WMSize(Msg);
       OldBoardSize = BoardSize;
     }
     if (bDefault)
     {
       if (Player1 == 1)
         WritePrivateProfileString("Preferences", "Player1", "1", "FLIPIT.INI");
       else
         WritePrivateProfileString("Preferences", "Player1", "2", "FLIPIT.INI");
       if (Player2 == 1)
         WritePrivateProfileString("Preferences", "Player2", "1", "FLIPIT.INI");
       else
	 WritePrivateProfileString("Preferences", "Player2", "2", "FLIPIT.INI");

       switch ( BoardSize )
       {
       case 6:
	 WritePrivateProfileString("Preferences", "BoardSize", "6", "FLIPIT.INI");
         break;
       case 8:
	 WritePrivateProfileString("Preferences", "BoardSize", "8", "FLIPIT.INI");
         break;
       case 10:
	 WritePrivateProfileString("Preferences", "BoardSize", "10", "FLIPIT.INI");
         break;
       }

       switch ( Difficulty )
       {
       case 1:
	 WritePrivateProfileString("Preferences", "Difficulty", "1", "FLIPIT.INI");
         break;
       case 2:
	 WritePrivateProfileString("Preferences", "Difficulty", "2", "FLIPIT.INI");
         break;
       case 3:
	 WritePrivateProfileString("Preferences", "Difficulty", "3", "FLIPIT.INI");
         break;
       }
     }

     InvalidateRect(HWindow,NULL,TRUE);
   //REGEN_PREFERENCES_EXEC

   }
}

void TMainWindow::CONTENTS(RTMessage)
{
   //REGEN_CONTENTS_EXEC
   WinHelp( ghWindow, "FLIPIT.HLP", HELP_CONTENTS, 0L );
   //REGEN_CONTENTS_EXEC
}

void TMainWindow::ABOUT(RTMessage)
{
   // Execute modal dialog
   if (GetModule()->ExecDialog(
               new TDIALOG_1Dlg(this, "DIALOG_1")) == IDOK )
   {
   //REGEN_ABOUT_EXEC
   //REGEN_ABOUT_EXEC

   }
}


/***************************************************
 * TFlipitApp method implementations:
 ***************************************************/

// Construct the TFlipit's MainWindow of type TMainWindow
void TFlipit::InitMainWindow()
{
   MainWindow = new TMainWindow(NULL, Name);
   //REGEN_MAINCREATE
   ghInstance = hInstance;
   //REGEN_MAINCREATE
}

// Main program
int PASCAL WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
   HINSTANCE hBorLibrary;

   hBorLibrary = LoadLibrary("bwcc.dll");

   if((UINT)hBorLibrary <= 32)
      MessageBox(NULL, "Unable to load Borland Controls", "System Error", MB_OK | MB_ICONHAND);

   //REGEN_INIT
   //REGEN_INIT

   TFlipit Flipit ("Flip it", hInstance, hPrevInstance,
      lpCmdLine, nCmdShow);

   Flipit.Run();

   if((UINT)hBorLibrary > 32)
      FreeLibrary(hBorLibrary);

   //REGEN_CLEANUP
   //REGEN_CLEANUP

   return Flipit.Status;
}

//REGEN_CUSTOMCODE
void TMainWindow::SetupWindow()
{
   TWindow::SetupWindow();
   SetTimer(HWindow,10,1500,NULL);
}
#pragma argsused
void TMainWindow::Paint(HDC PaintDC, PAINTSTRUCT &PaintInfo)
{

  ghWindow = HWindow;

  if (bReFresh)
    Board.UpdScore(PaintDC);

  Board.DrawBoard(PaintDC);
}

void TMainWindow::WMLButtonDown(RTMessage Msg)
{
  POINT p;
  HCURSOR hCursor;
  p.x = Msg.LP.Lo; p.y = Msg.LP.Hi;
  if ( Board.ValidMove(p) )
  {
    Board2Undo(Board,Undo);
    Board.MakeMove(p);
    InvalidateRect(HWindow,NULL,FALSE);
  }
}

void TMainWindow::WMSize(RTMessage)
{
  RECT r;

  bReFresh = TRUE;

  GetClientRect(HWindow,&r);
  nStatusBar = GetSystemMetrics(SM_CYMENU);  // Height of Horz Scroll Bar
  if ( r.right < r.bottom )
    nSize = r.right;
  else
    nSize = r.bottom;

  nSquare = nSize/(BoardSize+2);

  nXmargin = (r.right-nSquare*BoardSize)/2;
  nYmargin = (r.bottom-nSquare*BoardSize-nStatusBar)/2;

}
void TMainWindow::WMMouseMove(RTMessage Msg)
{
  // determine board coordinates
  POINT p;
  HCURSOR hCursor;
  p.x = Msg.LP.Lo; p.y = Msg.LP.Hi;
  if ( Board.ValidMove(p) )
    SetCursor(LoadCursor(NULL,IDC_CROSS));
  else
    SetCursor(LoadCursor(NULL,IDC_ARROW));
  
}
TBoard::TBoard()
{
  InitBoard(TRUE);
}

#pragma argsused
void TBoard::InitBoard(BOOL bFirst)
{
   int nRow,nCol,nLevel,i,j,Max;
   WEIGHTTAG Temp;

   if (bFirst)
   {
     BoardSize = GetPrivateProfileInt("Preferences", "BoardSize", 8, "FLIPIT.INI");
     Difficulty = GetPrivateProfileInt("Preferences", "Difficulty", 2, "FLIPIT.INI");
     Player1 = GetPrivateProfileInt("Preferences", "Player1", 1, "FLIPIT.INI");
     Player2 = GetPrivateProfileInt("Preferences", "Player2", 2, "FLIPIT.INI");
   }

   Player = 1;
   bCheat = FALSE;
   memset(&BoardArr, 0, sizeof(BoardArr));

   BoardArr[BoardSize/2][BoardSize/2] = \
   BoardArr[BoardSize/2+1][BoardSize/2+1] = 1;
   BoardArr[BoardSize/2+1][BoardSize/2] = \
   BoardArr[BoardSize/2][BoardSize/2+1] = 2;
   LastMove.x = BoardSize/2+1; LastMove.y = BoardSize/2;
   nWhiteScore = nBlackScore = 2;

   Vectors[0].x =  0; Vectors[0].y = -1;  //North
   Vectors[1].x =  1; Vectors[1].y = -1;  //Northeast
   Vectors[2].x =  1; Vectors[2].y =  0;  //East
   Vectors[3].x =  1; Vectors[3].y =  1;  //Southeast
   Vectors[4].x =  0; Vectors[4].y =  1;  //South
   Vectors[5].x = -1; Vectors[5].y =  1;  //Southwest
   Vectors[6].x = -1; Vectors[6].y =  0;  //West
   Vectors[7].x = -1; Vectors[7].y = -1;  //Northwest

   bFlip = FALSE;

   for (i=0;i<100;i++)
   {
     Weight[i].Mult= Weight[i].x = Weight[i].y = 0;
   }

   if (Difficulty == 0)
     Difficulty = MEDIUM;

   for (nCol=1;nCol<=BoardSize;nCol++)
     for (nRow=1;nRow<=BoardSize;nRow++)
     {
       i=(nRow-1)*BoardSize+nCol-1;
       Weight[i].x = nCol;
       Weight[i].y = nRow;
       if ((nCol == 1 || nCol == BoardSize) \
          && (nRow == 1 || nRow == BoardSize))
         Weight[i].Mult = 5000;
       else if (nCol == 1 || nCol == BoardSize \
               || nRow == 1 || nRow == BoardSize)
         Weight[i].Mult = 30;
       else if ((nCol == 3 || nCol == BoardSize-2) \
               && (nRow == 3 || nRow == BoardSize-2))
         Weight[i].Mult = 20;
       else if (((nCol == 3 || nCol == BoardSize-2) \
               && nRow > 3 && nRow < BoardSize-2) \
               || ((nRow == 10 || nRow == BoardSize-2)\
	       && nCol > 3 && nCol < BoardSize-2))
	 Weight[i].Mult = 5;
       else if ((nCol == 2 || nCol == BoardSize-1) \
	       && (nRow == 2 || nRow == BoardSize-1))
	 Weight[i].Mult = 1;
       else
	 Weight[i].Mult = 3;
     }
   for (i=0;i<BoardSize*BoardSize-1;i++)
   {
     Max=i;
     for (j=i+1;j<BoardSize*BoardSize;j++)
       if (Weight[j].Mult > Weight[Max].Mult)
	 Max=j;
       else if (Weight[j].Mult == Weight[Max].Mult)
	 if (Weight[j].x > Weight[Max].x)
	   Max=j;
	 else if (Weight[j].x == Weight[Max].x && Weight[j].y > Weight[Max].y)
	   Max=j;

     if (Max != i)
     {
       Temp = Weight[i];
       Weight[i] = Weight[Max];
       Weight[Max] = Temp;
     }
   }

   OldDifficulty = Difficulty;
}
void TBoard::DrawBoard(HDC PaintDC)
{
  RECT r,rSquare,rBoard;
  int nRow,nCol,nOther,nVect;
  BOOL bGameOver;

  rBoard.left = nXmargin-2;
  rBoard.top = nYmargin-2;
  rBoard.right = nXmargin+nSquare*BoardSize+2;
  rBoard.bottom = nYmargin+nSquare*BoardSize+2;

  if (!bReFresh && bFlip)
  {
    rSquare.left   = nXmargin+(LastMove.x-1)*nSquare+1;
    rSquare.top    = nYmargin+(LastMove.y-1)*nSquare+1;
    rSquare.right  = rSquare.left+nSquare-2;
    rSquare.bottom = rSquare.top+nSquare-2;
    PlacePiece(PaintDC,rSquare,LastMove.x,LastMove.y);
    FlipBoard(PaintDC);
    UpdScore(PaintDC);

    // See if we have any valid moves left.
    nOther = (Player == 1) ? 2 : 1;
    bGameOver = TRUE;
    nPass=0;

    for (nCol=1;nCol<=BoardSize;nCol++)
      for (nRow=1;nRow<=BoardSize;nRow++)
      {
	if (BoardArr[nCol][nRow] == 0)
	{
	  for ( nVect=0;nVect<8;nVect++ )
	    if (ChkVector(NULL,nCol,nRow,nVect,nOther,Player,FALSE) > 0)
            {
	      bGameOver = FALSE;
	    }
        }
      }

    if (bGameOver == TRUE)
    {
      for (nCol=1;nCol<=BoardSize;nCol++)
        for (nRow=1;nRow<=BoardSize;nRow++)
        {
 	  if (BoardArr[nCol][nRow] == 0)
	  {
	    for ( nVect=0;nVect<8;nVect++ )
	      if (ChkVector(NULL,nCol,nRow,nVect,Player,nOther,FALSE) > 0)
              {
	        bGameOver = FALSE;
	      }
          }
        }

      if (bGameOver == TRUE)
        BWCCMessageBox(NULL,"Game Over.","Flip It Message",MB_OK | MB_ICONEXCLAMATION );
      else if (Player == 1)
	  BWCCMessageBox(NULL,"White must pass, Black's turn.","Flip It Message",MB_OK | MB_ICONEXCLAMATION );
      else
	BWCCMessageBox(NULL,"Black must pass, White's turn.","Flip It Message",MB_OK | MB_ICONEXCLAMATION );

      Player = nOther;
    }
  }
  else
  {
    bReFresh = FALSE;
    Rectangle(PaintDC,rBoard.left-1,rBoard.top-1,
              rBoard.right+1,rBoard.bottom+1);
    DrawSquare(PaintDC,rBoard,1);

    MoveTo(PaintDC,nXmargin-1,nYmargin+nSquare*BoardSize+1);


    for (nCol=1;nCol<=BoardSize;nCol++)
      for (nRow=1;nRow<=BoardSize;nRow++)
      {
	rSquare.left   = nXmargin+(nCol-1)*nSquare+1;
	rSquare.top    = nYmargin+(nRow-1)*nSquare+1;
        rSquare.right  = rSquare.left+nSquare-2;
        rSquare.bottom = rSquare.top+nSquare-2;
        DrawSquare(PaintDC,rSquare,-1);
	PlacePiece(PaintDC,rSquare,nCol,nRow);
      }
    if (bFlip==TRUE)
      InvalidateRect(ghWindow,NULL,FALSE);

  }
}

void TBoard::DrawSquare(HDC PaintDC,RECT r,int Shadow)
{
  HPEN hWhitePen, hGrayPen, hOldPen;
  HBRUSH hGrayBrush,hOldBrush;

  hGrayPen = CreatePen(PS_SOLID, 1, RGB(96,96,96));
  hWhitePen = CreatePen(PS_SOLID, 1, RGB(255,255,255));
  hOldPen = SelectPen(PaintDC,hGrayPen);

  hGrayBrush = GetStockBrush(LTGRAY_BRUSH);
  hOldBrush = SelectBrush(PaintDC,hGrayBrush);

  Rectangle(PaintDC,r.left,r.top,r.right,r.bottom);
  SelectPen(PaintDC,hWhitePen);
  if ( Shadow > 0 )
  { 
    MoveTo(PaintDC,r.left,r.bottom-1);
    LineTo(PaintDC,r.left,r.top);
    LineTo(PaintDC,r.right-1,r.top);
  }
  else
  {
    MoveTo(PaintDC,r.left,r.bottom-1);
    LineTo(PaintDC,r.right-1,r.bottom-1);
    LineTo(PaintDC,r.right-1,r.top);
  }

  SelectPen(PaintDC,hOldPen);
  SelectBrush(PaintDC,hOldBrush);

  DeletePen(hWhitePen);
  DeletePen(hGrayPen);
}

void TBoard::PlacePiece(HDC PaintDC, RECT r, int nCol, int nRow)
{
  HDC hCompatDC;
  HBITMAP      hBitmap, hOldBitmap;
  BITMAP       BM;
  int i;

  // Get handle to the bitmap
  switch (BoardArr[nCol][nRow])
  {
    case 1:
    case 2:
      HBRUSH hBrush,hOldBrush;

      if (BoardArr[nCol][nRow] == 1)
	hBrush = GetStockBrush(WHITE_BRUSH);
      else
	hBrush = GetStockBrush(BLACK_BRUSH);

      hOldBrush = SelectBrush(PaintDC,hBrush);

      Ellipse(PaintDC,r.left+1,r.top+1,r.right-1,r.bottom-1);

      SelectBrush(PaintDC,hOldBrush);

      return;

    case 3: hBitmap = LoadBitmap(ghInstance, "BFLIP"); break;
    case 4: hBitmap = LoadBitmap(ghInstance, "VERT" ); break;
    case 5: hBitmap = LoadBitmap(ghInstance, "WFLIP"); break;
    default: return;
  }
  // Get dimensions of bitmap
  GetObject(hBitmap, sizeof(BM), &BM);

  // Create compatible display context
  hCompatDC = CreateCompatibleDC(PaintDC);

  // Select bitmap into the compataible DC
  hOldBitmap = SelectBitmap(hCompatDC, hBitmap);

  // Display bitmap in client area
  StretchBlt(PaintDC, r.left+1, r.top+1, r.right-r.left-2, r.bottom-r.top-2,
           hCompatDC, 0, 0, BM.bmWidth, BM.bmHeight,
           SRCCOPY);

  // De-select the bitmap
  SelectBitmap(hCompatDC, hOldBitmap);

  // Clean up after we are done
  DeleteDC(hCompatDC);
  DeleteBitmap(hBitmap);
	 
}

BOOL TBoard::ValidMove(POINT p)
{
  int x,y,nRow,nCol,nVect, nOther;

  if ( p.x <= nXmargin | p.y <= nYmargin ) return FALSE;
  if ( p.x >= nXmargin + nSquare*BoardSize |
       p.y >= nYmargin + nSquare*BoardSize ) return FALSE;

  // see if current square is occupied
  x=p.x-nXmargin;
  y=p.y-nYmargin;
  nCol = x/nSquare+1;
  nRow = y/nSquare+1;
  if ( bCheat )
  {
    if (BoardArr[nCol][nRow] == Player ) return FALSE;
  }
  else
    if ( BoardArr[nCol][nRow] != 0 ) return FALSE;

  // check board for adjacent pieces of the opposite color
  // if opposite color found, see if the current color is
  // on the other side
  nOther = (Player==1) ? 2 : 1;
  for ( nVect=0;nVect<8;nVect++ )
  {
    if (ChkVector(NULL,nCol,nRow,nVect,nOther,Player,FALSE) > 0)
      return TRUE;
  }
  
  // if no opposite colors are found, then return false
  return FALSE;
}

void TBoard::MakeMove(POINT p)
{
  int x,y,nCol,nRow;
  x=p.x-nXmargin;
  y=p.y-nYmargin;
  nCol = x/nSquare+1;
  nRow = y/nSquare+1;
  BoardArr[nCol][nRow] = Player;
  LastMove.x = nCol; LastMove.y = nRow;
  bFlip = TRUE;
  bCheat = FALSE;
}

void TBoard::FlipBoard(HDC PaintDC)
{
  int nOther,nVect;
  nOther = (Player==1) ? 2 : 1;
  for ( nVect=0;nVect<8;nVect++ )
    ChkVector(PaintDC,LastMove.x,LastMove.y,nVect,nOther,Player,TRUE);

  bFlip = FALSE;
  Player = nOther;
}

int TBoard::ChkVector(HDC PaintDC,int NewCol, int NewRow, int nVect,int nChkPiece,\
                      int nFlipto, BOOL Flipit)
{ // This routine uses recursion to count the number of pieces that would be
  // flipped if a piece were placed at the specified row & col.
  // To see how this works, try walking through this code with a
  // piece of paper and make a sample move.
  int nFlipCount=0;
  RECT rSquare;
  NewCol += Vectors[nVect].x; NewRow += Vectors[nVect].y;
  if (BoardArr[NewCol][NewRow] == nChkPiece)
  {
    nFlipCount++; 
    nFlipCount += ChkVector(PaintDC,NewCol,NewRow,nVect,nChkPiece,nFlipto,Flipit);
  }
  else if (BoardArr[NewCol][NewRow] == nFlipto) return 0;
  else return -10;

  if (Flipit && nFlipCount > 0)
  {
    if (PaintDC == NULL)  // Flip piece but don't display it...
      BoardArr[NewCol][NewRow] = Player;
    else
    {
      rSquare.left   = nXmargin+(NewCol-1)*nSquare+1;
      rSquare.top    = nYmargin+(NewRow-1)*nSquare+1;
      rSquare.right  = rSquare.left+nSquare-2;
      rSquare.bottom = rSquare.top+nSquare-2;
      switch (Player)
      {
	case 1: //White
      	  BoardArr[NewCol][NewRow]=3;
	  PlacePiece(PaintDC,rSquare,NewCol,NewRow);
	  BoardArr[NewCol][NewRow]=4;
	  PlacePiece(PaintDC,rSquare,NewCol,NewRow);
	  BoardArr[NewCol][NewRow]=5;
	  PlacePiece(PaintDC,rSquare,NewCol,NewRow);
	  BoardArr[NewCol][NewRow]=1;
	  PlacePiece(PaintDC,rSquare,NewCol,NewRow);
          break;
        case 2: //Black
	  BoardArr[NewCol][NewRow]=5;
	  PlacePiece(PaintDC,rSquare,NewCol,NewRow);
	  BoardArr[NewCol][NewRow]=4;
	  PlacePiece(PaintDC,rSquare,NewCol,NewRow);
	  BoardArr[NewCol][NewRow]=3;
	  PlacePiece(PaintDC,rSquare,NewCol,NewRow);
	  BoardArr[NewCol][NewRow]=2;
	  PlacePiece(PaintDC,rSquare,NewCol,NewRow);
	  break;
      }
    }
  }
  return nFlipCount;
}

void TBoard::UpdScore(HDC PaintDC)
{
  RECT r,rMessage,rWhiteScoreBmp,rWhiteScore,rBlackScoreBmp,rBlackScore;
  HPEN hWhitePen, hGrayPen, hOldPen;
  HDC hCompatDC;
  HBITMAP      hBitmap, hOldBitmap;
  BITMAP       BM;
  WORD wScoreWidth;
  char cszWhite[6],cszBlack[6];
  int nOrigWhite;

  nOrigWhite = nWhiteScore;

  nWhiteScore = nBlackScore = 0;
  int nCol,nRow;
  for (nCol=1;nCol<=BoardSize;nCol++)
    for (nRow=1;nRow<=BoardSize;nRow++)
      switch (BoardArr[nCol][nRow])
      {
      case 1: nWhiteScore++; break;
      case 2: nBlackScore++; break;
      }

  hGrayPen = CreatePen(PS_SOLID, 1, RGB(96,96,96));
  hWhitePen = CreatePen(PS_SOLID, 1, RGB(255,255,255));

  memset(cszWhite,0,sizeof(cszWhite));
  memset(cszBlack,0,sizeof(cszBlack));


  GetClientRect(ghWindow,&r);
  r.top    = r.bottom-nStatusBar;

  if ( bReFresh || nOrigWhite == Board.nWhiteScore )
    DrawSquare(PaintDC,r,1);

  wScoreWidth = LOWORD((GetTextExtent(PaintDC,"999",3)+nStatusBar+15)*2);

  rMessage.left   = 1;
  rMessage.top    = r.top+1;
  rMessage.right  = r.right-wScoreWidth;
  rMessage.bottom = r.bottom-1;

  rWhiteScoreBmp.left   = r.right-wScoreWidth+10;
  rWhiteScoreBmp.top    = r.top+2;
  rWhiteScoreBmp.bottom = r.bottom-2;
  rWhiteScoreBmp.right  = rWhiteScoreBmp.left+r.bottom-r.top-4;
  rWhiteScore.left = rWhiteScoreBmp.right+2;
  rWhiteScore.top  = rMessage.top;
  rWhiteScore.bottom = rMessage.bottom;
  rWhiteScore.right = rWhiteScore.left
		      + LOWORD(GetTextExtent(PaintDC,"999",3));

  rBlackScore.right = r.right-2;
  rBlackScore.left = rBlackScore.right
    		     -LOWORD(GetTextExtent(PaintDC,"999",3))-2;
  rBlackScore.top  = rMessage.top;
  rBlackScore.bottom = rMessage.bottom;
  rBlackScoreBmp.right   = rBlackScore.left-2;
  rBlackScoreBmp.top    = r.top+2;
  rBlackScoreBmp.bottom = r.bottom-2;
  rBlackScoreBmp.left  = rBlackScoreBmp.right-r.bottom+r.top+4;

  SetBkColor(PaintDC,RGB(192,192,192));
  if ( bReFresh || nOrigWhite == Board.nWhiteScore )
  {
    hOldPen = SelectPen(PaintDC,hGrayPen);

    MoveTo(PaintDC,rMessage.right,rMessage.top);
    LineTo(PaintDC,rMessage.right,rMessage.bottom+1);

    SelectPen(PaintDC,hWhitePen);

    MoveTo(PaintDC,rMessage.right+1,rMessage.top);
    LineTo(PaintDC,rMessage.right+1,rMessage.bottom+1);

    SelectPen(PaintDC,hOldPen);

    hBitmap = LoadBitmap(ghInstance, "WHITE");
    // Get dimensions of bitmap
    GetObject(hBitmap, sizeof(BM), &BM);

    // Create compatible display context
    hCompatDC = CreateCompatibleDC(PaintDC);

    // Select bitmap into the compataible DC
    hOldBitmap = SelectBitmap(hCompatDC, hBitmap);

    // Display bitmap in client area
    StretchBlt(PaintDC, rWhiteScoreBmp.left, rWhiteScoreBmp.top,
    	       rWhiteScoreBmp.right-rWhiteScoreBmp.left,
	       rWhiteScoreBmp.bottom-rWhiteScoreBmp.top,
               hCompatDC, 0, 0, BM.bmWidth, BM.bmHeight,
               SRCCOPY);
    SelectBitmap(hCompatDC, hOldBitmap);
    DeleteDC(hCompatDC);
    DeleteBitmap(hBitmap);

    hBitmap = LoadBitmap(ghInstance, "BLACK");
    GetObject(hBitmap, sizeof(BM), &BM);

    hCompatDC = CreateCompatibleDC(PaintDC);
    // Select bitmap into the compataible DC
    hOldBitmap = SelectBitmap(hCompatDC, hBitmap);

    // Display bitmap in client area
    StretchBlt(PaintDC, rBlackScoreBmp.left, rBlackScoreBmp.top,
	       rBlackScoreBmp.right-rBlackScoreBmp.left,
	       rBlackScoreBmp.bottom-rBlackScoreBmp.top,
               hCompatDC, 0, 0, BM.bmWidth, BM.bmHeight,
               SRCCOPY);
    // De-select the bitmap
    SelectBitmap(hCompatDC, hOldBitmap);

    // Clean up after we are done
    DeleteDC(hCompatDC);
    DeleteBitmap(hBitmap);
  }
  if (Player == 1)
    DrawText(PaintDC,"White's Turn  ",14,&rMessage,DT_SINGLELINE | DT_BOTTOM | DT_LEFT);
  else
    DrawText(PaintDC,"Black's Turn  ",14,&rMessage,DT_SINGLELINE | DT_BOTTOM | DT_LEFT); 

  DrawSquare(PaintDC,rWhiteScore,-1);
  DrawSquare(PaintDC,rBlackScore,-1);
  SetBkMode(PaintDC,TRANSPARENT);
  itoa(Board.nWhiteScore,cszWhite,10);
  DrawText(PaintDC,cszWhite,strlen(cszWhite),
	   &rWhiteScore,DT_SINGLELINE | DT_BOTTOM | DT_CENTER);

  itoa(Board.nBlackScore,cszBlack,10);
  DrawText(PaintDC,cszBlack,strlen(cszBlack),
	   &rBlackScore,DT_SINGLELINE | DT_BOTTOM | DT_CENTER);
  SetBkMode(PaintDC,OPAQUE);
  DeletePen(hWhitePen);
  DeletePen(hGrayPen);
}

void TMainWindow::WMTimer(RTMessage)
{
  if ( (Player == 1 && Player1 == 2) || (Player == 2 && Player2 == 2) )
  {
    SetCursor(LoadCursor(NULL,IDC_WAIT));
    Board.GoComputer(Player,1);
    SetCursor(LoadCursor(NULL,IDC_ARROW));
  }
}

#pragma argsused
long double TBoard::GoComputer(int Player, int Level)
{
  int nCol, nRow;
  int nOther,nVect,j,k,nVectCnt;
  long double MoveVal, MoveCnt, nOldMult, MaxVal=-100000000;
  BOOL ValMove=FALSE;
  TUndo SaveBoard,MoveBoard;

  POINT MaxMove; 

  Board2Undo(Board,SaveBoard);
  nOther = (Player==1) ? 2 : 1;
  nOldMult = 0;

  j=0;
  do
  {
    if ( Weight[j].Mult==0 ) { j++; continue; }

    if ( MaxVal > 10*Weight[j].Mult
	 && ValMove==TRUE && nOldMult != Weight[j].Mult )
       break;

    nOldMult = Weight[j].Mult;

    do
    {
      nCol = Weight[j].x;
      nRow = Weight[j].y;

      if ( BoardArr[nCol][nRow] == 0 )
      {
        MoveCnt = 0;
        MoveVal = 0;

        for ( nVect=0;nVect<8;nVect++ )
        {
          nVectCnt = ChkVector(NULL,nCol,nRow,nVect,nOther,Player,TRUE);
          MoveCnt += (nVectCnt > 0) ? nVectCnt : 0 ;
        }


        if ( MoveCnt > 0 )
        {
          MoveVal = MoveCnt*Weight[j].Mult;
	  //MoveVal = powl(MoveVal,Weight[j].Mult);
	  BoardArr[nCol][nRow] = Player;
	  if (Level < Difficulty*2-2 )
	    MoveVal -= GoComputer(nOther,Level+1);

	  if ( MoveVal > MaxVal || (MoveVal == MaxVal && random(10) > 4 ))
	  {
	    MaxVal = MoveVal;
	    MaxMove.x = nCol; MaxMove.y = nRow;
	    ValMove=TRUE;
	  }

	}
      }
      Undo2Board(SaveBoard,Board);
      j++;
    } while (j<BoardSize*BoardSize && nOldMult == Weight[j].Mult);
  } while (j<BoardSize*BoardSize);
  //See if the computer could move...
  if ( ValMove == FALSE )
  {
    MaxVal = 0;
    Player = nOther;
    bFlip = FALSE;
  }
  else
    if (Level == 1)
    {
      BoardArr[MaxMove.x][MaxMove.y] = Player;
      LastMove.x = MaxMove.x ; LastMove.y = MaxMove.y;
      bFlip = TRUE;
      nPass = 0;
      InvalidateRect(ghWindow,NULL,FALSE);
    }
  return MaxVal;
}

void Board2Undo( TBoard& b, TUndo& u)
{
  int i,j;

  memcpy(u.UndoArr,b.BoardArr,sizeof(b.BoardArr));

  u.UndoPlayer = Player;
  u.nUndoPass = nPass;
  u.nUndoWhiteScore = b.nWhiteScore;
  u.nUndoBlackScore = b.nBlackScore;
  u.UndoLastMove.x = LastMove.x;
  u.UndoLastMove.y = LastMove.y;
}

void Undo2Board( TUndo& u, TBoard& b)
{
  int i,j;

  memcpy(b.BoardArr,u.UndoArr,sizeof(b.BoardArr));

  Player = u.UndoPlayer;
  nPass = u.nUndoPass;
  b.nWhiteScore = u.nUndoWhiteScore;
  b.nBlackScore = u.nUndoBlackScore;
  LastMove.x = u.UndoLastMove.x;
  LastMove.y = u.UndoLastMove.y;
}

istream& operator >> ( istream& is, TBoard& bd )
{
  int i,j;

  for (i=0;i<12;i++)
    for (j=0;j<12;j++)
     is >> bd.BoardArr[i][j];

  is >> bd.bFlip;

  for (i=0;i<8;i++)
    is >> bd.Vectors[i];

  for (j=0;j<100;j++)
  {
     is >> bd.Weight[j].Mult;
     is >> bd.Weight[j].x;
     is >> bd.Weight[j].y;
  }
  is >> bd.nWhiteScore;
  is >> bd.nBlackScore;
  return is;
}

ostream& operator << ( ostream& os, TBoard& bd )
{
  int i,j;
  for (i=0;i<12;i++)
    for (j=0;j<12;j++)
     os << bd.BoardArr[i][j];

  os << bd.bFlip;
  os << ' ';

  for (i=0;i<8;i++)
    os << bd.Vectors[i];

  for (j=0;j<100;j++)
  {
     os << bd.Weight[j].Mult << ' ';
     os << bd.Weight[j].x << ' ';
     os << bd.Weight[j].y << ' ';
  }
  os << bd.nWhiteScore;
  os << ' ';
  os << bd.nBlackScore;
  os << ' ';
  return os;
}

istream& operator >> ( istream& is, TUndo& u )
{
  int i,j;

  for (i=0;i<12;i++)
    for (j=0;j<12;j++)
     is >> u.UndoArr[i][j];

  is >> u.UndoPlayer;
  is >> u.nUndoPass;
  is >> u.nUndoWhiteScore;
  is >> u.nUndoBlackScore;
  is >> u.UndoLastMove;

  return is;
}

ostream& operator << ( ostream& os, TUndo& u )
{
  int i,j;
  for (i=0;i<12;i++)
    for (j=0;j<12;j++)
     os << u.UndoArr[i][j];

  os << u.UndoPlayer;
  os << ' ';
  os << u.nUndoPass;
  os << ' ';
  os << u.nUndoWhiteScore;
  os << ' ';
  os << u.nUndoBlackScore;
  os << ' ';
  os << u.UndoLastMove;

  return os;
}
istream& operator >> ( istream& is, POINT& pt )
{
  int x,y;

  is >> x;
  is >> y;

  pt.x = x;
  pt.y = y;

  return is;
}

ostream& operator << ( ostream& os, POINT& pt )
{
  int x,y;

  x = pt.x;
  y = pt.y;

  os << x;
  os << ' ';
  os << y;
  os << ' ';

  return os;
}

int TMainWindow::WMSetFocus(RTMessage)
{
  KillTimer(ghWindow,10);
  SetTimer(ghWindow,10,1500,NULL);
  bReFresh=TRUE;
  return 0;
}

int TMainWindow::WMKillFocus(RTMessage Msg)
{
  WMInitMenu(Msg);
  return 0;
}

void TMainWindow::WMInitMenu(RTMessage)
{                  
  KillTimer(ghWindow,10);
}

int TMainWindow::WMMenuSelect(RTMessage Msg)
{
  if (Msg.LP.Hi == 0)
    WMSetFocus(Msg);

  return 0;
}

void TMainWindow::OpenFiFile( LPSTR lpstrFile )
{
   ifstream in( lpstrFile );    // open the input file
   in >> Board;                  // read the Todo list
   in >> Undo;
   in >> Player;
   in >> Player1;
   in >> Player2;
   in >> nPass;
   in >> NumPlayers;
   in >> BoardSize;
   in >> LastMove;
   in >> Difficulty;
   in.close();
}

void TMainWindow::SaveFiFile( LPSTR lpstrFile )
{
   ofstream out( lpstrFile );
   out << Board;
   out << Undo;
   out << Player << ' ';
   out << Player1 << ' ';
   out << Player2 << ' ';
   out << nPass << ' ';
   out << NumPlayers << ' ';
   out << BoardSize << ' ';
   out << LastMove;
   out << Difficulty;
   out.close();
}
//REGEN_CUSTOMCODE
