home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / l / l040 / 10.ddi / CHESS.ZIP / OWLCHESS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-10-27  |  32.6 KB  |  1,069 lines

  1. {************************************************}
  2. {                                                }
  3. {   ObjectWindows Chess Demo                     }
  4. {   Copyright (c) 1992 by Borland International  }
  5. {                                                }
  6. {************************************************}
  7.  
  8. { The chess programs consist of 3 binary files:
  9.  
  10.     OWLCHESS.EXE  - Windows chess program (uses ObjectWindows)
  11.     TVCHESS.EXE   - DOS text mode chess program (uses Turbo Vision)
  12.     CHESS.DLL     - Chess analysis engine dynamic link library (DLL)
  13.                     that is shared by both TVCHESS.EXE and OWLCHESS.EXE
  14.  
  15.   TVCHESS is a DOS protected mode application (DPMI). To build
  16.   it, set Compile|Target to Protected from inside the IDE or type the
  17.   following command-line at a DOS prompt:
  18.  
  19.     bpc /m /cp tvchess
  20.  
  21.   (Note you can also produce a real mode version of TVCHESS which
  22.   will statically link in the CHESS DLL units.)
  23.  
  24.   OWLCHESS is a Windows application. To build it, set Compile|Target to
  25.   Windows from inside the IDE or type the following command-line at a
  26.   DOS prompt:
  27.  
  28.     bpc /m /cw owlchess
  29.  
  30.   CHESS.DLL is a Windows format DLL and comes already built. To rebuild
  31.   it, set Compile|Target to Windows from inside the IDE or type the
  32.   following command-line at a DOS prompt:
  33.  
  34.     bpc /m /cw chess
  35.  
  36. }
  37.  
  38. program OWLChess;
  39.  
  40. {$C Moveable, Preload, Permanent }
  41.  
  42. uses WinProcs, WinTypes, Objects, OWindows, ODialogs, BWCC, OWUtils,
  43.      Chessdll, OWBoard, MoveList, OWChDlgs, OWConst, OWLCmDlg, Strings,
  44.      CommDlg, WinDos, CTimers, OWAbout;
  45.  
  46. {$R OWLChess}
  47.  
  48. const
  49.   ChessSignature : array [0..33] of Char = 'Borland Pascal Chess saved game'#26#0;
  50.   ChessFileFilter = 'Chess games'#0'*.chs'#0#0;
  51.   csEndGame = [csCheckMate, csStaleMate, csResigns,
  52.                csFiftyMoveRule, csRepetitionRule];
  53.  
  54. type
  55.  
  56.   TGameMode = (mSinglePlayer, mTwoPlayer, mAutoPlay, mStartup);
  57.  
  58.   PChessApp = ^TChessApp;
  59.   TChessApp = object(TXtendedApp)
  60.     procedure InitInstance; virtual;
  61.     procedure InitMainWindow; virtual;
  62.     function  IdleAction: Boolean; virtual;
  63.   end;
  64.  
  65.   PChessWindow = ^TChessWindow;
  66.   TChessWindow = object(TWindow)
  67.     Game: HChess;
  68.     Board: TChessBoard;
  69.     BoardFrame: PBoardFrame;
  70.     ThinkState: TSearchStatus;
  71.     Mode : TGameMode;
  72.     Player: TColor;
  73.     MoveHistory: PMoveList;
  74.     InfoPane: PChessInfoWindow;
  75.     ThinkMenu: HMenu;
  76.     GameFileName: array [0..fsPathName] of Char;
  77.     Timer: array [cWhite..cBlack] of PChessTimer;
  78.     ActiveTimer: PChessTimer;   { = nil means game is suspended }
  79.     GameOver: Boolean;
  80.     Paused: Boolean;
  81.     constructor Init(AParent: PWindowsObject; ATitle: PChar);
  82.     destructor  Done; virtual;
  83.     procedure SetupWindow; virtual;         { first place HWindow is valid }
  84.     procedure WMDestroy(var Msg: TMessage); { last place HWindow is valid }
  85.       virtual wm_First + wm_Destroy;
  86.     function  GetClassName: PChar; virtual;
  87.     procedure GetWindowClass(var WC: TWndClass); virtual;
  88.     procedure Sleep;
  89.     procedure Revive;
  90.     procedure RestartGame;
  91.     function  CanClose: Boolean; virtual;
  92.     function  IdleAction: Boolean;
  93.     function  ShowMsg(const Ctx, MsgCode: Integer; Override: PChar): Integer;
  94.     function  GameTimeExpired: Boolean;
  95.     procedure LockDownGameOver;
  96.     procedure UnlockGameOver;
  97.     procedure ReportGameState;
  98.     function  SaveGame(FileName: PChar): Boolean;
  99.     function  LoadGame(FileName: PChar): Boolean;
  100.     procedure RecordMove(const Move: TMove);
  101.     procedure StartComputerMove;
  102.     procedure AcceptComputerMove;
  103.     procedure AMSubmitMove(var Msg: TMessage);
  104.       virtual am_SubmitMove;
  105.     procedure AMChoosePawnPromote(var Msg: TMessage);
  106.       virtual am_ChoosePawnPromote;
  107.     procedure CMNewGame(var Msg: TMessage);
  108.       virtual cm_First + cm_NewGame;
  109.     procedure CMLoadGame(var Msg: TMessage);
  110.       virtual cm_First + cm_LoadGame;
  111.     procedure CMSaveGame(var Msg: TMessage);
  112.       virtual cm_First + cm_SaveGame;
  113.     procedure CMSaveAs(var Msg: TMessage);
  114.       virtual cm_First + cm_SaveAs;
  115.     procedure CMAutoPlay(var Msg: TMessage);
  116.       virtual cm_First + cm_AutoPlay;
  117.     procedure CMPauseGame(var Msg: TMessage);
  118.       virtual cm_First + cm_PauseGame;
  119.     procedure CMUndoMove(var Msg: TMessage);
  120.       virtual cm_First + cm_UndoMove;
  121.     procedure CMRedoMove(var Msg: TMessage);
  122.       virtual cm_First + cm_RedoMove;
  123.     procedure CMComputerMove(var Msg: TMessage);
  124.       virtual cm_First + cm_ComputerMove;
  125.     procedure CMEnterMove(var Msg: TMessage);
  126.       virtual cm_First + cm_EnterMove;
  127.     procedure CMSettings(var Msg: TMessage);
  128.       virtual cm_First + cm_Settings;
  129.     procedure CMAbout(var Msg: TMessage);
  130.       virtual cm_First + cm_About;
  131.     procedure CMStopThinking(var Msg: TMessage);
  132.       virtual cm_First + cm_StopThinking;
  133.     procedure WMTimer(var Msg: TMessage);
  134.       virtual wm_First + wm_Timer;
  135.     procedure WMSetCursor(var Msg: TMessage);
  136.       virtual wm_First + wm_SetCursor;
  137.     procedure WMActivate(var Msg: TMessage);
  138.       virtual wm_First + wm_Activate;
  139.   end;
  140.  
  141. constructor TChessWindow.Init(AParent: PWindowsObject; ATitle: PChar);
  142. begin
  143.   inherited Init(AParent, ATitle);
  144.   Attr.X := 0;
  145.   Attr.Y := 50;
  146.   Attr.W := 350;
  147.   Attr.H := 350;
  148.   Attr.Style := ws_Overlapped or ws_Caption or ws_Border or
  149.                 ws_SysMenu or ws_MinimizeBox;
  150.   Attr.Menu := LoadMenu(HInstance, PChar(idMainMenu));
  151.   ThinkMenu := LoadMenu(HInstance, PChar(idThinkMenu));
  152.   LoadINISettings;
  153.   GameFileName[0] := #0;
  154.   Mode := mStartup;
  155.   Player := cWhite;
  156.   Status := Context(cxChessError, Ord(NewGame(Game)));
  157.   if Status <> 0 then Exit;
  158.   ThinkState := GetSearchStatus(Game);
  159.   Timer[cWhite] := New(PChessTimer, Init);
  160.   Timer[cBlack] := New(PChessTimer, Init);
  161.   ActiveTimer := nil;
  162.   GameOver := False;
  163.   Paused := False;
  164.   MoveHistory := New(PMoveList, Init(20, 10));
  165.   Board.Init(@Self, Game);
  166.   InfoPane := New(PChessInfoWindow, Init(@Self, PChar(dlgInfoPane)));
  167.   BoardFrame := New(PBoardFrame, Init(@Self, @Board));
  168. end;
  169.  
  170. destructor  TChessWindow.Done;
  171. begin
  172.   Dispose(MoveHistory, Done);
  173.   Dispose(Timer[cWhite], Done);
  174.   Dispose(Timer[cBlack], Done);
  175.   SaveINISettings;
  176.   DisposeGame(Game);
  177.   DestroyMenu(ThinkMenu);
  178.   RemoveChild(@Board);
  179.   Board.Done;
  180.   inherited Done;
  181. end;
  182.  
  183. procedure TChessWindow.SetupWindow;
  184. var
  185.   W, WX, WY, H: Word;
  186.   WR, CR, IR: TRect;
  187.   XSpacer, YSpacer: Integer;
  188.   DC: HDC;
  189. begin
  190.   inherited SetupWindow;
  191.   W := BoardFrame^.IdealWidth;
  192.   H := W;
  193.   GetWindowRect(HWindow, WR);
  194.   GetClientRect(HWindow, CR);
  195.   GetClientRect(InfoPane^.HWindow, IR);
  196.   WX := (WR.Right - WR.Left) - CR.Right;
  197.   WY := (WR.Bottom - WR.Top) - CR.Bottom;
  198.   if H < IR.Bottom then
  199.     H := IR.Bottom;
  200.   DC := GetDC(HWindow);
  201.   XSpacer := GetDeviceCaps(DC, LogPixelsX) div 8;
  202.   YSpacer := GetDeviceCaps(DC, LogPixelsY) div 8;
  203.   ReleaseDC(HWindow, DC);
  204.   SetWindowPos(HWindow, 0, 0, 0, W + 3*XSpacer + IR.Right + WX,
  205.                     H + 2*YSpacer + WY, swp_NoZOrder or swp_NoMove);
  206.   SetWindowPos(BoardFrame^.HWindow, 0, XSpacer, YSpacer, W, W,swp_NoZOrder);
  207.   SetWindowPos(InfoPane^.HWindow, 0, W + 2*XSpacer, YSpacer, 0, 0,
  208.                                swp_NoZOrder or swp_NoSize);
  209.   ShowWindow(BoardFrame^.HWindow, sw_ShowNormal);
  210.   ShowWindow(Board.HWindow, sw_ShowNormal);
  211.   SetTimer(HWindow, 1, ChessSettings.RefreshRate, nil);
  212. end;
  213.  
  214. procedure TChessWindow.WMDestroy(var Msg: TMessage);
  215. begin
  216.   KillTimer(HWindow, 1);
  217.   inherited WMDestroy(Msg);
  218. end;
  219.  
  220. function  TChessWindow.GetClassName: PChar;
  221. begin
  222.   GetClassName := 'TPWOWLChess';
  223. end;
  224.  
  225. procedure TChessWindow.GetWindowClass(var WC: TWndClass);
  226. var
  227.   LB: TLogBrush;
  228. begin
  229.   inherited GetWindowClass(WC);
  230.   WC.Style := cs_ByteAlignWindow;
  231.   WC.hCursor := 0;
  232.   { Duplicate the BWCCPattern brush.  hbrBackground brush will be destroyed
  233.     when our window is closed.  If we didn't duplicate this brush, but just
  234.     used BWCCGetPattern's result directly, BWCC could be left without
  235.     a valid background brush when our window closes.  }
  236.   GetObject(BWCCGetPattern, SizeOf(LB), @LB);
  237.   WC.hbrBackground := CreateBrushIndirect(LB);
  238.   WC.hIcon := LoadIcon(HInstance, PChar(100));
  239. end;
  240.  
  241. procedure TChessWindow.Sleep;
  242. begin
  243.   if not (GameOver or Paused) then
  244.   begin
  245.     if ActiveTimer <> nil then
  246.       ActiveTimer^.Stop;
  247.     ActiveTimer := nil;
  248.     KillTimer(HWindow, 1);
  249.     if ChessSettings.CoverBoard then
  250.       Board.Cover(True);
  251.     Board.Disable;
  252.   end;
  253. end;
  254.  
  255. procedure TChessWindow.Revive;
  256. begin
  257.   if not (GameOver or Paused) then
  258.   begin
  259.     ActiveTimer := Timer[GetPlayer(Game)];
  260.     ActiveTimer^.Start;
  261.     SetTimer(HWindow, 1, ChessSettings.RefreshRate, nil);
  262.     if IsWindowVisible(Board.CoverDlg^.HWindow) then
  263.       Board.Cover(False);
  264.     Board.Enable;
  265.   end;
  266. end;
  267.  
  268. procedure TChessWindow.RestartGame;
  269. var
  270.   Cursor : HCursor;
  271. begin
  272.   UpdateWindow(HWindow);  { Clean up after the dialog that just closed }
  273.   Cursor := SetCursor(LoadCursor(0, idc_Wait));
  274.   if GameOver then
  275.     UnlockGameOver;
  276.   if ActiveTimer <> nil then
  277.     ActiveTimer^.Stop;
  278.   Timer[cWhite]^.Clear;
  279.   Timer[cBlack]^.Clear;
  280.   MoveHistory^.Purge;
  281.   InfoPane^.MoveListBox^.DeleteRest(0);
  282.   EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or
  283.                                          mf_Disabled or mf_Grayed);
  284.   EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or
  285.                                          mf_Disabled or mf_Grayed);
  286.   DisposeGame(Game);
  287.   if ChessSettings.OnePlayer then
  288.     Mode := mSinglePlayer
  289.   else
  290.     Mode := mTwoPlayer;
  291.   ShowMsg(cxChessError, Ord(NewGame(Game)), nil);
  292.   Player := cWhite;
  293.   InfoPane^.Update(0, 0, 0, 0, 0, Word(Mode));
  294.   GameOver := False;
  295.   ThinkState := GetSearchStatus(Game);
  296.   Board.ResetBoard(Game);
  297.   SetCursor(Cursor);
  298. end;
  299.  
  300. function TChessWindow.CanClose: Boolean;
  301. begin
  302.   CanClose := inherited CanClose and
  303.               ( GameOver or
  304.                (MoveHistory^.Count = 0) or
  305.                (MessageBox(HWindow, PChar(strCancelGame),
  306.                  PChar(strLeaveGame), mb_YesNo) = id_Yes));
  307.   UpdateWindow(HWindow);    { Clean up after the message box asap }
  308. end;
  309.  
  310. function  TChessWindow.IdleAction: Boolean;
  311. var
  312.   OldState: TSearchStatus;
  313.   Value: Integer;
  314.   Line: array [0..15] of TMove;
  315. begin
  316.   if not GameOver then
  317.   begin
  318.     OldState := ThinkState;
  319.     if (OldState = ssMoveSearch) and (ActiveTimer <> nil) then
  320.       ActiveTimer^.Start;
  321.     if OldState = ssThinkAhead then
  322.       Think(Game, 1, ThinkState)
  323.     else
  324.       Think(Game, ChessSettings.ThinkTime.Position, ThinkState);
  325.     if (OldState = ssMoveSearch) and (ActiveTimer <> nil) then
  326.       ActiveTimer^.Stop;
  327.     if (ThinkState = ssComplete) and (OldState = ssMoveSearch) then
  328.       AcceptComputerMove
  329.     else
  330.     if not GameOver and (ThinkState = ssGameOver) then
  331.       ReportGameState;
  332.   end;
  333.     { Return True if we want to continue to get IdleAction calls ASAP,
  334.       Return False if we don't need more IdleAction immediately
  335.       ssThinkAhead is not included to minimize performance hits.
  336.       IdleAction will be called frequently enough for ThinkAhead,
  337.       since every wm_Timer message will set off an IdleAction call. }
  338.   IdleAction := (ThinkState = ssMoveSearch) and not GameOver;
  339. end;
  340.  
  341. function  TChessWindow.ShowMsg(const Ctx, MsgCode: Integer;
  342.                                Override: PChar): Integer;
  343. var
  344.   S: array [0..100] of Char;
  345. begin
  346.   S[0] := #0;
  347.   if Override <> nil then
  348.     InfoPane^.Msg^.SetText(Override)
  349.   else
  350.   begin
  351.     if (MsgCode <> 0) then
  352.       StrLoadRes(S, Ctx + MsgCode);
  353.     InfoPane^.Msg^.SetText(S);
  354.   end;
  355.   ShowMsg := MsgCode;
  356. end;
  357.  
  358. function TChessWindow.GameTimeExpired: Boolean;
  359. begin
  360.   GameTimeExpired := ChessSettings.LimitGame and
  361.    (Player = GetPlayer(Game)) and    { enforce game limit only on computer moves }
  362.    (Timer[cWhite]^.GetCurrentTicks +
  363.     Timer[CBlack]^.GetCurrentTicks > Trunc(ChessSettings.GameTime * 60 * 18.2065));
  364. end;
  365.  
  366. procedure TChessWindow.LockDownGameOver;
  367. var
  368.   M: TMove;
  369. begin
  370.   AbortSearch(Game);
  371.   if ThinkState = ssMoveSearch then  { Computer thought too long }
  372.   begin
  373.     Board.Enable;
  374.     SetMenu(HWindow, Attr.Menu);
  375.     Board.ResetValidMoves;
  376.   end;
  377.   if GameTimeExpired then
  378.   begin
  379.     EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or
  380.                                          mf_Disabled or mf_Grayed);
  381.     EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or
  382.                                          mf_Disabled or mf_Grayed);
  383.   end;
  384.   EnableMenuItem(Attr.Menu, cm_ComputerMove, mf_ByCommand or
  385.                                          mf_Disabled or mf_Grayed);
  386.   EnableMenuItem(Attr.Menu, cm_AutoPlay, mf_ByCommand or
  387.                                          mf_Disabled or mf_Grayed);
  388. end;
  389.  
  390. procedure TChessWindow.UnlockGameOver;
  391. begin
  392.   if GameOver then
  393.   begin
  394.     GameOver := False;
  395.     Board.SetGameOver(False);
  396.     if MoveHistory^.UndoAvail then
  397.       EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or mf_Enabled);
  398.     if MoveHistory^.RedoAvail then
  399.       EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or mf_Enabled);
  400.     EnableMenuItem(Attr.Menu, cm_ComputerMove, mf_ByCommand or mf_Enabled);
  401.     EnableMenuItem(Attr.Menu, cm_AutoPlay, mf_ByCommand or mf_Enabled);
  402.   end;
  403. end;
  404.  
  405. procedure TChessWindow.ReportGameState;
  406. var State: TChessStatus;
  407.     StrID: Word;
  408.     Count: Integer;
  409.     Color: TColor;
  410.     Data : array [0..1] of PChar;
  411.     S : array [0..100] of Char;
  412.     P : PChar;
  413.     WTime, BTime: Longint;
  414. begin
  415.   State := GetChessStatus(Game, Count);
  416.   Data[0] := nil;
  417.   Data[1] := nil;
  418.   StrID := cxChessState + ord(State);
  419.   case State of
  420.     csCheckMate:
  421.       Data[0] := StrNewRes(strWhite + ord(OtherPlayer(GetPlayer(Game))));
  422.     csResigns:
  423.       begin
  424.         Data[0] := StrNewRes(strWhite + ord(OtherPlayer(GetPlayer(Game))));
  425.         Data[1] := StrNewRes(strWhite + ord(GetPlayer(Game)));
  426.       end;
  427.     csMateFound:
  428.       Data[0] := PChar(Count);
  429.     else
  430.       if GameTimeExpired then
  431.       begin
  432.         StrID := strGameTimeExpired;
  433.         Data[0] := StrNewRes(strWhite + ord(GetPlayer(Game)));
  434.         Data[1] := StrNewRes(strWhite + ord(OtherPlayer(GetPlayer(Game))));
  435.       end;
  436.   end;
  437.   P := StrNewRes(StrID);
  438.   S[0] := #0;
  439.   if P <> nil then
  440.     WVSprintf(S, P, Data);
  441.   if Seg(Data[0]^) <> 0 then StrDispose(Data[0]);
  442.   StrDispose(Data[1]);
  443.   ShowMsg(0,0,S);
  444.   InfoPane^.Update(Game, Timer[cWhite]^.GetCurrentTicks,
  445.                          Timer[cBlack]^.GetCurrentTicks,
  446.                          Timer[GetPlayer(Game)]^.GetMarkTime,
  447.                          (MoveHistory^.UndoPos+1) div 2,
  448.                          Word(Mode));
  449.   { Check for game over conditions }
  450.   if GameTimeExpired or
  451.      not (State in [csNormal, csCheck, csMateFound]) then
  452.   begin
  453.     if ActiveTimer <> nil then
  454.       ActiveTimer^.Stop;
  455.     ActiveTimer := nil;
  456.     if Board.Dragger <> nil then
  457.       Board.CancelDrag;
  458.     Data[0] := StrNewRes(strGameOver);
  459.     GameOver := True;
  460.     Board.SetGameOver(True);
  461.     MessageBox(HWindow, S, Data[0], mb_Ok);
  462.     LockDownGameOver;
  463.   end;
  464. end;
  465.  
  466. procedure TChessWindow.RecordMove(const Move: TMove);
  467. var
  468.   S, T: array [0..7] of Char;
  469. begin
  470.   if MoveHistory^.Count = 0 then  { Enable the menu on first move }
  471.     EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or mf_Enabled);
  472.   if MoveHistory^.RedoAvail then  { not any more...}
  473.     EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or mf_Disabled
  474.                                                         or mf_Grayed);
  475.   MoveHistory^.AddMove(Move);
  476.   if Odd(MoveHistory^.UndoPos) then
  477.   begin    { Black move, need to package with the previous white move }
  478.     MoveToStr(Move, T);
  479.     MoveToStr(PMove(MoveHistory^.At(MoveHistory^.UndoPos-1))^, S);
  480.   end
  481.   else
  482.   begin    { White move, no black move to bundle }
  483.     MoveToStr(Move, S);
  484.     T[0] := ' ';
  485.     T[1] := #0;
  486.   end;
  487.   InfoPane^.MoveListBox^.AddMove(MoveHistory^.UndoPos div 2,S,T);
  488. end;
  489.  
  490. procedure TChessWindow.StartComputerMove;
  491. var
  492.   TimeLimit : Longint;
  493. begin
  494.   with ChessSettings do
  495.   begin
  496.     if NoLimit then
  497.       TimeLimit := MaxLongint
  498.     else
  499.     if LimitGame then
  500.     begin
  501.       TimeLimit := (ChessSettings.GameTime * 1092 -
  502.         Timer[OtherPlayer(GetPlayer(Game))]^.GetCurrentTicks) div 44;
  503.       if MoveHistory^.UndoPos <= 80 then        { Shoot for a 40 move game }
  504.         TimeLimit := 91 + (TimeLimit - 91) *
  505.           ((80 - MoveHistory^.UndoPos div 2) div 40);
  506.     end
  507.     else
  508.     if MatchUser then
  509.     begin
  510.       TimeLimit := Timer[OtherPlayer(GetPlayer(Game))]^.GetMarkTime;
  511.       if TimeLimit <= 1 then
  512.         TimeLimit := 5 * 18;
  513.     end
  514.     else
  515.       TimeLimit := TurnTime * 18;
  516.   end;
  517.   ComputerMove(Game, TimeLimit);
  518.   ThinkState := GetSearchStatus(Game);
  519.   SetMenu(HWindow, ThinkMenu);
  520.   InfoPane^.EnableSearchInfoUpdates(True);
  521.   Board.Disable;              { Prevent mouse dragging of pieces }
  522.   EnableMenuItem(Attr.Menu, cm_PauseGame, mf_ByCommand or
  523.                                           mf_Disabled or mf_Grayed);
  524.   ActiveTimer := Timer[GetPlayer(Game)];
  525.   ActiveTimer^.Mark;
  526. end;
  527.  
  528.  
  529. procedure TChessWindow.AcceptComputerMove;
  530. var
  531.   Move: TMove;
  532. begin
  533.   if (Mode = mStartup) then
  534.   begin
  535.     if ChessSettings.OnePlayer then
  536.       Mode := mSinglePlayer
  537.     else
  538.       Mode := mTwoPlayer;
  539.     EnableMenuItem(Attr.Menu, cm_PauseGame, mf_ByCommand or mf_Enabled);
  540.   end;
  541.   InfoPane^.EnableSearchInfoUpdates(False);
  542.   GetLastMove(Game, Move);
  543.   RecordMove(Move);
  544.   Board.ExecuteMove(Move);
  545.   ReportGameState;
  546.   if (not GameOver) and
  547.     ((Mode = mAutoPlay) or
  548.      (Mode = mSinglePlayer) and (Player <> GetPlayer(Game)))  then
  549.     StartComputerMove
  550.   else
  551.   begin
  552.     Board.Enable;
  553.     SetMenu(HWindow, Attr.Menu);
  554.     if not GameOver then
  555.     begin
  556.       Board.ResetValidMoves;
  557.       ActiveTimer := Timer[GetPlayer(Game)];
  558.       ActiveTimer^.Mark;
  559.       ActiveTimer^.Start;
  560.       EnableMenuItem(Attr.Menu, cm_PauseGame, mf_ByCommand or mf_Enabled);
  561.       if (Mode = mSinglePlayer) and ChessSettings.AllowThinkAhead then
  562.       begin
  563.         Player := GetPlayer(Game);
  564.         ThinkAhead(Game);
  565.         ThinkState := GetSearchStatus(Game);
  566.         InfoPane^.ZeroNodes;
  567.         InfoPane^.EnableSearchInfoUpdates(True);
  568.       end;
  569.     end;
  570.   end;
  571. end;
  572.  
  573. function TChessWindow.LoadGame(FileName: PChar): Boolean;
  574. var
  575.   S: PBufStream;
  576.   Test: array [0..SizeOf(ChessSignature)] of Char;
  577.   Blk: array [0..7] of Char;
  578.   NewMoveList : PMoveList;
  579.   Y, X: Integer;
  580.  
  581.   function ReplayMoves(P: PMove): Boolean; far;
  582.   begin
  583.     SubmitMove(Game, P^.Change);
  584.     Board.ExecuteMove(P^);
  585.     if Odd(X) then
  586.     begin
  587.       MoveToStr(P^, Blk);
  588.       InfoPane^.MoveListBox^.AddMove(X div 2, Test, Blk);
  589.     end
  590.     else
  591.       MoveToStr(P^, Test);
  592.     ReplayMoves := (X >= MoveHistory^.UndoPos);
  593.     Inc(X);
  594.   end;
  595.  
  596. begin
  597.   LoadGame := False;
  598.   S := New(PBufStream, Init(FileName, stOpenRead, 1024));
  599.   S^.Read(Test, SizeOf(ChessSignature));
  600.   if S^.Status <> stOK then
  601.     MessageBox(HWindow, PChar(Context(cxStreamError, S^.Status)),
  602.                         PChar(strLoadError), mb_Ok)
  603.   else
  604.   if StrLComp(ChessSignature, Test, SizeOf(ChessSignature)) <> 0 then
  605.     MessageBox(HWindow, PChar(strNotAChessFile),
  606.                         PChar(strInvalidFile), mb_Ok)
  607.   else
  608.   begin
  609.     NewMoveList := PMoveList(S^.Get);
  610.     if S^.Status <> stOK then
  611.       MessageBox(HWindow, PChar(Context(cxStreamError, S^.Status)),
  612.                           PChar(strLoadError), mb_Ok)
  613.     else
  614.     begin
  615.       RestartGame;
  616.       Dispose(MoveHistory, Done);
  617.       MoveHistory := NewMoveList;
  618.       X := 0;
  619.       Test[0] := #0;
  620.       MoveHistory^.FirstThat(@ReplayMoves);
  621.  
  622.       { Copy undone moves to the move listbox as well }
  623.       for Y := X+1 to MoveHistory^.Count-1 do
  624.       begin
  625.         if Odd(Y) then
  626.         begin
  627.           MoveToStr(PMove(MoveHistory^.At(Y))^, Blk);
  628.           InfoPane^.MoveListBox^.AddMove(Y div 2, Test, Blk);
  629.         end
  630.         else
  631.           MoveToStr(PMove(MoveHistory^.At(Y))^, Test);
  632.       end;
  633.       if Odd(MoveHistory^.Count) then
  634.         InfoPane^.MoveListBox^.AddMove(
  635.                   (MoveHistory^.Count-1) div 2, Test, ' ');
  636.       InfoPane^.MoveListBox^.SetSelIndex(MoveHistory^.UndoPos div 2);
  637.  
  638.       if MoveHistory^.UndoAvail then
  639.         EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or mf_Enabled);
  640.       if MoveHistory^.RedoAvail then
  641.         EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or mf_Enabled);
  642.       Board.ResetValidMoves;
  643.       ReportGameState;
  644.       LoadGame := True;
  645.     end;
  646.   end;
  647.   Dispose(S, Done);
  648. end;
  649.  
  650. function TChessWindow.SaveGame(FileName: PChar): Boolean;
  651. var
  652.   S: PBufStream;
  653. begin
  654.   S := New(PBufStream, Init(FileName, stCreate, 1024));
  655.   S^.Write(ChessSignature, SizeOf(ChessSignature));
  656.   S^.Put(MoveHistory);
  657.   if S^.Status <> stOK then
  658.     MessageBox(HWindow, PChar(Context(cxStreamError, S^.Status)),
  659.                         PChar(strSaveError), mb_Ok);
  660.   SaveGame := S^.Status = stOK;
  661.   Dispose(S, Done);
  662. end;
  663.  
  664. procedure TChessWindow.AMSubmitMove(var Msg: TMessage);
  665. var
  666.   Move: TMove;
  667. begin
  668.   if (Mode = mStartup) then
  669.   begin
  670.     if ChessSettings.OnePlayer then
  671.       Mode := mSinglePlayer
  672.     else
  673.       Mode := mTwoPlayer;
  674.     EnableMenuItem(Attr.Menu, cm_PauseGame, mf_ByCommand or mf_Enabled);
  675.   end;
  676.   Msg.Result := ShowMsg(cxChessError, Ord(SubmitMove(Game, PChange(Msg.LParam)^)), nil);
  677.     { Result = True if SubmitMove returns zero, else Result = False }
  678.   LongBool(Msg.Result) := not LongBool(Msg.Result);
  679.   if LongBool(Msg.Result) then
  680.   begin
  681.     if ActiveTimer <> nil then
  682.       ActiveTimer^.Stop;
  683.     GetLastMove(Game, Move);       { Retrieve the full move from the engine }
  684.     RecordMove(Move);              { Enter in history list, enable Redo menu}
  685.     Board.ExecuteMove(Move);      { Adjust the board }
  686.     ReportGameState;
  687.     if not GameOver then
  688.       if ChessSettings.TwoPlayer then
  689.       begin
  690.         Player := GetPlayer(Game);
  691.         Board.ResetValidMoves;       { Refresh the valid move tables }
  692.         ActiveTimer := Timer[GetPlayer(Game)];
  693.         ActiveTimer^.Mark;
  694.         ActiveTimer^.Start;
  695.       end
  696.       else
  697.         StartComputerMove;
  698.   end;
  699. end;
  700.  
  701. procedure TChessWindow.AMChoosePawnPromote(var Msg: TMessage);
  702. begin
  703.   XApp^.ExecDialog(New(PChoosePawnPromoteDlg,
  704.     Init(InfoPane, PChar(dlgChoosePawnPromote), PChange(Msg.LParam))));
  705. end;
  706.  
  707. procedure TChessWindow.CMNewGame(var Msg: TMessage);
  708. begin
  709.   if  GameOver or
  710.      (MoveHistory^.Count = 0) or
  711.      (MessageBox(HWindow, PChar(strCancelGame),
  712.         PChar(strStartNewGame), mb_YesNo) = id_Yes) then
  713.   begin
  714.     RestartGame;
  715.   end;
  716. end;
  717.  
  718. procedure TChessWindow.CMLoadGame(var Msg: TMessage);
  719. var
  720.   Temp : array [0..fsPathName] of Char;
  721.   Count: Integer;
  722. begin
  723.   StrCopy(Temp, GameFileName);
  724.   if ( GameOver or
  725.      (GetChessStatus(Game, Count) in csEndGame) or
  726.      (MoveHistory^.Count = 0) or
  727.      (MessageBox(HWindow, PChar(strCancelGame),
  728.         PChar(strLoadSavedGame), mb_YesNo) = id_Yes))
  729.     and
  730.      (XApp^.ExecDialog(New(PCDFileOpen, Init(@Self,
  731.         ofn_FileMustExist, Temp, SizeOf(Temp),
  732.         ChessFileFilter))) = idOk) then
  733.     if LoadGame(Temp) then
  734.       StrCopy(GameFileName, Temp);
  735. end;
  736.  
  737. procedure TChessWindow.CMSaveGame(var Msg: TMessage);
  738. begin
  739.   if GameFileName[0] = #0 then
  740.     CMSaveAs(Msg)
  741.   else
  742.     SaveGame(GameFileName);
  743. end;
  744.  
  745. procedure TChessWindow.CMSaveAs(var Msg: TMessage);
  746. var
  747.   Temp : array [0..fsPathName] of Char;
  748. begin
  749.   StrCopy(Temp, GameFileName);
  750.   if XApp^.ExecDialog(New(PCDFileSaveAs, Init(@Self,
  751.       ofn_PathMustExist, Temp, SizeOf(Temp), ChessFileFilter))) = idOk then
  752.     if SaveGame(Temp) then
  753.       StrCopy(GameFileName, Temp);
  754. end;
  755.  
  756. procedure TChessWindow.CMAutoPlay(var Msg: TMessage);
  757. begin
  758.   if Paused then   { The game is paused }
  759.     CMPauseGame(Msg);   { Clear the paused state before entering autoplay }
  760.   Mode := mAutoPlay;
  761.   AbortSearch(Game);
  762.   StartComputerMove;
  763. end;
  764.  
  765. procedure TChessWindow.CMPauseGame(var Msg: TMessage);
  766. var
  767.   S: array [0..20] of Char;
  768. begin
  769.   if Paused then
  770.   begin
  771.     Paused := False;
  772.     Revive;
  773.     ModifyMenu(Attr.Menu, cm_PauseGame, mf_ByCommand or mf_String,
  774.                            cm_PauseGame, StrLoadRes(S, strPauseMenu));
  775.     if MoveHistory^.RedoAvail then
  776.       EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or mf_Enabled);
  777.     if MoveHistory^.UndoAvail then
  778.      EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or mf_Enabled);
  779.     EnableMenuItem(Attr.Menu, cm_ComputerMove, mf_ByCommand or mf_Enabled);
  780.     EnableMenuItem(Attr.Menu, cm_EnterMove, mf_ByCommand or mf_Enabled);
  781.   end
  782.   else
  783.   begin
  784.     Sleep;
  785.     ModifyMenu(Attr.Menu, cm_PauseGame, mf_ByCommand or mf_String,
  786.                            cm_PauseGame, StrLoadRes(S, strResumeMenu));
  787.     ShowMsg(0,strGamePaused,nil);
  788.     if MoveHistory^.RedoAvail then
  789.       EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or
  790.                                 mf_Disabled or mf_Grayed);
  791.     if MoveHistory^.UndoAvail then
  792.      EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or
  793.                                         mf_Disabled or mf_Grayed);
  794.     EnableMenuItem(Attr.Menu, cm_ComputerMove, mf_ByCommand or
  795.                               mf_Disabled or mf_Grayed);
  796.     EnableMenuItem(Attr.Menu, cm_EnterMove, mf_ByCommand or
  797.                               mf_Disabled or mf_Grayed);
  798.     Paused := True;
  799.   end;
  800. end;
  801.  
  802. procedure TChessWindow.CMUndoMove(var Msg: TMessage);
  803. var
  804.   M : TMove;
  805.   RedoBefore, UndoBefore: Boolean;
  806. begin
  807.   { No error checking is performed here - it is assumed that the
  808.     menu enable/disable code will only allow the user
  809.     to select this menu item when there is a valid undo available. }
  810.   UndoBefore := MoveHistory^.UndoAvail;
  811.   RedoBefore := MoveHistory^.RedoAvail;
  812.  
  813.   AbortSearch(Game);   { You can't submit or retract moves while thinking }
  814.  
  815.   if GameOver then
  816.     UnlockGameOver;
  817.  
  818.   if ActiveTimer <> nil then
  819.     ActiveTimer^.Stop;
  820.   Revive;
  821.   ActiveTimer^.Stop;
  822.   ActiveTimer^.Mark;
  823.  
  824.   MoveHistory^.Undo(M);
  825.   RetractMove(Game, M);
  826.   Board.RetractMove(M);
  827.  
  828.   if (Mode = mSinglePlayer) and
  829.      MoveHistory^.UndoAvail then  { Undo both player's and computer's move }
  830.   begin
  831.     MoveHistory^.Undo(M);
  832.     RetractMove(Game, M);
  833.     Board.RetractMove(M);
  834.   end;
  835.   if MoveHistory^.RedoAvail and not RedoBefore then
  836.     EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or mf_Enabled);
  837.   if (not MoveHistory^.UndoAvail) and UndoBefore then
  838.    EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or
  839.                                           mf_Disabled or mf_Grayed);
  840.   InfoPane^.MoveListBox^.SetSelIndex(MoveHistory^.UndoPos div 2);
  841.   Board.ResetValidMoves;
  842.   InfoPane^.EnableSearchInfoUpdates(False);
  843.   ReportGameState;
  844.   ActiveTimer^.Start;
  845. end;
  846.  
  847. procedure TChessWindow.CMRedoMove(var Msg: TMessage);
  848. var
  849.   M : TMove;
  850.   RedoBefore, UndoBefore: Boolean;
  851. begin
  852.   { No error checking is performed here - it is assumed that the
  853.     menu enable/disable code will only allow the user
  854.     to select this menu item when there is a valid redo available. }
  855.   UndoBefore := MoveHistory^.UndoAvail;
  856.   RedoBefore := MoveHistory^.RedoAvail;
  857.  
  858.   AbortSearch(Game);   { You can't submit or retract moves while thinking }
  859.  
  860.   if GameOver then
  861.     UnlockGameOver;
  862.  
  863.   if ActiveTimer <> nil then
  864.     ActiveTimer^.Stop;
  865.   Revive;
  866.   ActiveTimer^.Stop;
  867.   ActiveTimer^.Mark;
  868.  
  869.   MoveHistory^.Redo(M);
  870.   SubmitMove(Game, M.Change);
  871.   Board.ExecuteMove(M);
  872.  
  873.   if (Mode = mSinglePlayer) and
  874.      MoveHistory^.RedoAvail then
  875.   begin
  876.     MoveHistory^.Redo(M);  { Redo both player's and computer's moves }
  877.     SubmitMove(Game, M.Change);
  878.     Board.ExecuteMove(M);
  879.   end;
  880.  
  881.   { Update the menus, but only when the undo/redo state changes
  882.     (to avoid menubar flicker caused by unnecessary menu changes) }
  883.   if (not MoveHistory^.RedoAvail) and RedoBefore then
  884.     EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or
  885.                                            mf_Disabled or mf_Grayed);
  886.   if MoveHistory^.UndoAvail and not UndoBefore then
  887.     EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or mf_Enabled);
  888.  
  889.   InfoPane^.MoveListBox^.SetSelIndex(MoveHistory^.UndoPos div 2);
  890.   Board.ResetValidMoves;
  891.   InfoPane^.EnableSearchInfoUpdates(False);
  892.   ReportGameState;
  893.   if ActiveTimer <> nil then
  894.     ActiveTimer^.Start;
  895. end;
  896.  
  897. procedure TChessWindow.CMComputerMove(var Msg: TMessage);
  898. var
  899.   Move: TMove;
  900. begin
  901.   AbortSearch(Game);
  902.   GetHintMove(Game, Move);
  903.   if VerifyMove(Game, Move.Change) = ceOK then
  904.   begin
  905.     Msg.LParam := Longint(@Move.Change);
  906.     AMSubmitMove(Msg);
  907.   end
  908.   else
  909.   begin
  910.     Player := GetPlayer(Game);
  911.     StartComputerMove;
  912.   end;
  913. end;
  914.  
  915. procedure TChessWindow.CMEnterMove(var Msg: TMessage);
  916. var
  917.   S: array [0..10] of Char;
  918.   Chg: TChange;
  919. begin
  920.   S[0] := #0;
  921.   Board.CoverDlg^.Lock;  { Prevent EnterMoveDlg from covering board }
  922.   if XApp^.ExecDialog(New(PEnterMoveDlg,
  923.      Init(InfoPane, PChar(dlgEnterMove), S))) = IDOK then
  924.   begin
  925.     if (ShowMsg(cxChessError, Ord(ParseMove(S, Chg)),nil) = 0) then
  926.     begin
  927.       Msg.LParam := Longint(@Chg);
  928.       Msg.wParam := 0;
  929.       AMSubmitMove(Msg);
  930.     end
  931.   end;
  932.   Board.CoverDlg^.Unlock;
  933. end;
  934.  
  935. procedure TChessWindow.CMSettings(var Msg: TMessage);
  936. begin
  937.   XApp^.ExecDialog(new(PSettingsDlg, Init(@Self, PChar(dlgSettings),
  938.                                                  ChessSettings)));
  939.   if (Mode <> mStartup) then
  940.   begin
  941.     if ChessSettings.OnePlayer then
  942.       Mode := mSinglePlayer
  943.     else
  944.       Mode := mTwoPlayer;
  945.     Board.ResetValidMoves;
  946.   end;
  947.   if (ChessSettings.CoverBoard <> Board.CoverDlg^.Locked) then
  948.     if ChessSettings.CoverBoard then
  949.       Board.CoverDlg^.Unlock
  950.     else
  951.       Board.CoverDlg^.Lock;
  952. end;
  953.  
  954. procedure TChessWindow.CMAbout(var Msg: TMessage);
  955. begin
  956.   XApp^.ExecDialog(New(PAboutBox,
  957.     Init(@Self, 'About OWLChess', PChar(bmBlaise))));
  958. end;
  959.  
  960. procedure TChessWindow.CMStopThinking(var Msg: TMessage);
  961. begin
  962.   if ThinkState = ssMoveSearch then
  963.     ForceMove(Game);     { Move search will terminate at next Think call }
  964.   if Mode = mAutoPlay then
  965.   begin
  966.     Mode := mSinglePlayer;  { Stop the demo, if it is running }
  967.     Player := OtherPlayer(GetPlayer(Game));
  968.   end;
  969. end;
  970.  
  971. procedure TChessWindow.WMTimer(var Msg: TMessage);
  972. begin
  973.   if not GameOver then
  974.     ReportGameState;
  975. end;
  976.  
  977. procedure TChessWindow.WMSetCursor(var Msg: TMessage);
  978. begin
  979.   DefWndProc(Msg);
  980.   Msg.Result := 1;  { Cancel any pending WMSetCursor in children }
  981.   if Msg.LParamLo = HTClient then
  982.     if (ThinkState = ssMoveSearch)  then
  983.       SetCursor(LoadCursor(0, PChar(idc_Wait)))
  984.     else
  985.     begin
  986.       if Msg.WParam = Board.HWindow then
  987.         Msg.Result := 0   { Allow Board to use its own cursor }
  988.       else
  989.         SetCursor(LoadCursor(0, PChar(idc_Arrow)));
  990.     end;
  991. end;
  992.  
  993. procedure TChessWindow.WMActivate(var Msg: TMessage);
  994. begin
  995.   inherited WMActivate(Msg);
  996.   if Mode = mStartup then Exit;
  997.   if Msg.LParamHi = 0 then  { we're not minimized }
  998.     if Msg.WParam = 0 then   { we're going inactive }
  999.       Sleep
  1000.     else
  1001.       Revive
  1002.   else
  1003.     if ActiveTimer <> nil then  { we're being minimized }
  1004.       Sleep;
  1005. end;
  1006.  
  1007.  
  1008. procedure TChessApp.InitInstance;
  1009. begin
  1010.   inherited InitInstance;
  1011.   if Status = 0 then
  1012.   begin
  1013.     HAccTable := LoadAccelerators(HInstance, 'Accel');
  1014.     if HAccTable = 0 then
  1015.       Status := em_InvalidWindow;
  1016.   end;
  1017. end;
  1018.  
  1019. procedure TChessApp.InitMainWindow;
  1020. begin
  1021.   MainWindow := new(PChessWindow, Init(nil, 'OWL Chess'));
  1022. end;
  1023.  
  1024. function TChessApp.IdleAction: Boolean;
  1025. begin
  1026.   IdleAction := PChessWindow(MainWindow)^.IdleAction;
  1027. end;
  1028.  
  1029. procedure ShowSplashScreen(var R: TRect);
  1030. var
  1031.   BM: HBitmap;
  1032.   DC, MemDC: HDC;
  1033.   BitInfo: TBitmap;
  1034. begin
  1035.   DC := GetDC(0);
  1036.   MemDC := CreateCompatibleDC(DC);
  1037.   BM := LoadBitmap(HInstance, PChar(bmBlaise));
  1038.   GetObject(BM, SizeOf(BitInfo), @BitInfo);
  1039.   BM := SelectObject(MemDC, BM);   { swap BMs}
  1040.   R.Left := GetDeviceCaps(DC,HORZRES) div 2 - BitInfo.bmWidth div 2;
  1041.   R.Top := GetDeviceCaps(DC,VERTRES) div 2 - BitInfo.bmHeight div 2;
  1042.   BitBlt(DC, R.Left, R.Top, BitInfo.bmWidth, BitInfo.bmHeight,
  1043.          MemDC, 0, 0, SrcCopy);
  1044.   DeleteObject(SelectObject(MemDC, BM));
  1045.   DeleteDC(MemDC);
  1046.   ReleaseDC(0, DC);
  1047.   R.Right := R.Left + BitInfo.bmWidth;
  1048.   R.Bottom := R.Top + BitInfo.bmHeight;
  1049. end;
  1050.  
  1051.  
  1052. var
  1053.   App: TChessApp;
  1054.   R: TRect;
  1055. begin
  1056.   RegisterType(RMoveList);
  1057.  
  1058.   { Show Splash screen asap }
  1059.   ShowSplashScreen(R);
  1060.  
  1061.   App.Init('OWL Chess');
  1062.  
  1063.   { Erase the splash bitmap }
  1064.   InvalidateRect(0, @R, True);
  1065.  
  1066.   App.Run;
  1067.   App.Done;
  1068. end.
  1069.