home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Pascal / BPASCAL.700 / D12 / CHESSOWL.ZIP / OWLCHESS.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1992-10-01  |  18.0 KB  |  590 lines

  1. program OWLChess;
  2.  
  3. uses Winprocs, WinTypes, Objects, OWindows, ODialogs, BWCC, AppUtils,
  4.      Chessdll, Board, MoveList, ChessDlg, ChConst, FileDlgs, Strings,
  5.      CommDlg, WinDos, CTimers;
  6.  
  7. {$R OWLChess}
  8.  
  9. const
  10.  
  11.   ChessSignature : array [0..33] of Char = 'Borland Pascal Chess saved game'#26#0;
  12.   ChessFileFilter = 'Chess games'#0'*.chs'#0#0;
  13.  
  14. type
  15.  
  16.   TGameMode = (SinglePlayer, TwoPlayer, AutoPlay);
  17.  
  18.   PChessApp = ^TChessApp;
  19.   TChessApp = object(TXtendedApp)
  20.     procedure InitMainWindow; virtual;
  21.     function  IdleAction: Boolean; virtual;
  22.   end;
  23.  
  24.   PChessWindow = ^TChessWindow;
  25.   TChessWindow = object(TWindow)
  26.     Game: HChess;
  27.     Board: PChessBoard;
  28.     ThinkState: TSearchStatus;
  29.     Mode : TGameMode;
  30.     Player: TColor;
  31.     MoveHistory: PMoveList;
  32.     InfoPane: PChessInfoWindow;
  33.     ThinkMenu: HMenu;
  34.     GameFileName: array [0..fsPathName] of Char;
  35.     Timer: array [cWhite..cBlack] of PChessTimer;
  36.     ActiveTimer: PChessTimer;
  37.     constructor Init(AParent: PWindowsObject; ATitle: PChar);
  38.     destructor  Done; virtual;
  39.     procedure SetupWindow; virtual;         { first place HWindow is valid }
  40.     procedure WMDestroy(var Msg: TMessage); { last place HWindow is valid }
  41.       virtual wm_First + wm_Destroy;
  42.     function  GetClassName: PChar; virtual;
  43.     procedure GetWindowClass(var WC: TWndClass); virtual;
  44.     procedure RestartGame;
  45.     function  CanClose: Boolean; virtual;
  46.     function  IdleAction: Boolean;
  47.     function  ShowMsg(const Ctx, MsgCode: Integer): Integer;
  48.     procedure ReportGameState;
  49.     procedure GameOver;
  50.     function  SaveGame(FileName: PChar): Boolean;
  51.     function  LoadGame(FileName: PChar): Boolean;
  52.     procedure RecordMove(const Move: TMove);
  53.     procedure StartComputerMove;
  54.     procedure AcceptComputerMove;
  55.     procedure AMSubmitMove(var Msg: TMessage);
  56.       virtual am_SubmitMove;
  57.     procedure CMNewGame(var Msg: TMessage);
  58.       virtual cm_First + cm_NewGame;
  59.     procedure CMLoadGame(var Msg: TMessage);
  60.       virtual cm_First + cm_LoadGame;
  61.     procedure CMSaveGame(var Msg: TMessage);
  62.       virtual cm_First + cm_SaveGame;
  63.     procedure CMSaveAs(var Msg: TMessage);
  64.       virtual cm_First + cm_SaveAs;
  65. {    procedure CMAutoPlay(var Msg: TMessage);
  66.       virtual cm_First + cm_AutoPlay;
  67. }    procedure CMPauseGame(var Msg: TMessage);
  68.       virtual cm_First + cm_PauseGame;
  69.     procedure CMUndoMove(var Msg: TMessage);
  70.       virtual cm_First + cm_UndoMove;
  71.     procedure CMRedoMove(var Msg: TMessage);
  72.       virtual cm_First + cm_RedoMove;
  73.     procedure CMSettings(var Msg: TMessage);
  74.       virtual cm_First + cm_Settings;
  75.     procedure CMStopThinking(var Msg: TMessage);
  76.       virtual cm_First + cm_StopThinking;
  77.     procedure WMTimer(var Msg: TMessage);
  78.       virtual wm_First + wm_Timer;
  79.     procedure WMSetCursor(var Msg: TMessage);
  80.       virtual wm_First + wm_SetCursor;
  81.   end;
  82.  
  83. constructor TChessWindow.Init(AParent: PWindowsObject; ATitle: PChar);
  84. begin
  85.   inherited Init(AParent, ATitle);
  86.   Attr.X := 10;
  87.   Attr.Y := 50;
  88.   Attr.W := 350;
  89.   Attr.H := 350;
  90.   Attr.Style := ws_OverlappedWindow;
  91.   Attr.Menu := LoadMenu(HInstance, PChar(idMainMenu));
  92.   ThinkMenu := LoadMenu(HInstance, PChar(idThinkMenu));
  93.   LoadINISettings;
  94.   GameFileName[0] := #0;
  95.   if ChessSettings.OnePlayer then
  96.     Mode := SinglePlayer
  97.   else
  98.     Mode := TwoPlayer;
  99.   Player := cWhite;
  100.   Status := Context(cxChessError, Ord(NewGame(Game)));
  101.   if Status <> 0 then Exit;
  102.   ThinkState := GetSearchStatus(Game);
  103.   Timer[cWhite] := New(PChessTimer, Init);
  104.   Timer[cBlack] := New(PChessTimer, Init);
  105.   ActiveTimer := nil;
  106.   MoveHistory := New(PMoveList, Init(20, 10));
  107.   Board := New(PChessBoard, Init(@Self, Game));
  108.   InfoPane := New(PChessInfoWindow, Init(@Self, PChar(dlgInfoPane)));
  109. end;
  110.  
  111. destructor  TChessWindow.Done;
  112. begin
  113.   Dispose(MoveHistory, Done);
  114.   Dispose(Timer[cWhite], Done);
  115.   Dispose(Timer[cBlack], Done);
  116.   SaveINISettings;
  117.   DisposeGame(Game);
  118.   DestroyMenu(ThinkMenu);
  119.   inherited Done;
  120. end;
  121.  
  122. procedure TChessWindow.SetupWindow;
  123. var
  124.   W, WX, WY, H: Word;
  125.   WR, CR, IR: TRect;
  126. begin
  127.   inherited SetupWindow;
  128.   W := Board^.IdealWidth;
  129.   H := W;
  130.   GetWindowRect(HWindow, WR);
  131.   GetClientRect(HWindow, CR);
  132.   GetClientRect(InfoPane^.HWindow, IR);
  133.   WX := (WR.Right - WR.Left) - CR.Right;
  134.   WY := (WR.Bottom - WR.Top) - CR.Bottom;
  135.   if H < IR.Bottom then
  136.     H := IR.Bottom;
  137.   SetWindowPos(HWindow, 0, 0, 0, W + 75 + IR.Right + WX,
  138.                               H + 50 + WY, swp_NoZOrder or swp_NoMove);
  139.   SetWindowPos(Board^.HWindow, 0, 25, 25, W, W, swp_NoZOrder);
  140.   SetWindowPos(InfoPane^.HWindow, 0, W + 50, 25, 0, 0,
  141.                                swp_NoZOrder or swp_NoSize);
  142.   ShowWindow(Board^.HWindow, sw_ShowNormal);
  143.   SetTimer(HWindow, 1, ChessSettings.RefreshRate, nil);
  144. end;
  145.  
  146. procedure TChessWindow.WMDestroy(var Msg: TMessage);
  147. begin
  148.   KillTimer(HWindow, 1);
  149.   inherited WMDestroy(Msg);
  150. end;
  151.  
  152. function  TChessWindow.GetClassName: PChar;
  153. begin
  154.   GetClassName := 'TPWOWLChess';
  155. end;
  156.  
  157. procedure TChessWindow.GetWindowClass(var WC: TWndClass);
  158. var
  159.   LB: TLogBrush;
  160. begin
  161.   inherited GetWindowClass(WC);
  162.   WC.Style := cs_ByteAlignWindow;
  163.   WC.hCursor := 0;
  164.   { Duplicate the BWCCPattern brush.  hbrBackground brush will be destroyed
  165.     when our window is closed.  If we didn't duplicate this brush, but just
  166.     used BWCCGetPattern's result directly, BWCC could be left without
  167.     a valid background brush when our window closes.  }
  168.   GetObject(BWCCGetPattern, SizeOf(LB), @LB);
  169.   WC.hbrBackground := CreateBrushIndirect(LB);
  170.   { !! add icon here }
  171. end;
  172.  
  173. procedure TChessWindow.RestartGame;
  174. var
  175.   Cursor : HCursor;
  176. begin
  177.   UpdateWindow(HWindow);  { Clean up after the dialog that just closed }
  178.   Cursor := SetCursor(LoadCursor(0, idc_Wait));
  179.   if ActiveTimer <> nil then
  180.     ActiveTimer^.Stop;
  181.   Timer[cWhite]^.Clear;
  182.   Timer[cBlack]^.Clear;
  183.   MoveHistory^.FreeAll;
  184.   EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or
  185.                                          mf_Disabled or mf_Grayed);
  186.   EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or
  187.                                          mf_Disabled or mf_Grayed);
  188.   DisposeGame(Game);
  189.   ShowMsg(cxChessError, Ord(NewGame(Game)));
  190.   ThinkState := GetSearchStatus(Game);
  191.   Board^.ResetBoard(Game);
  192.   SetCursor(Cursor);
  193. end;
  194.  
  195. function TChessWindow.CanClose: Boolean;
  196. begin
  197.   CanClose := inherited CanClose and
  198.               ((ThinkState = ssGameOver) or
  199.                (MoveHistory^.Count = 0) or
  200.                (MessageBox(HWindow, PChar(strCancelGame),
  201.                  PChar(strLeaveGame), mb_YesNo) = id_Yes));
  202.   UpdateWindow(HWindow);    { Clean up after the message box asap }
  203. end;
  204.  
  205. function  TChessWindow.IdleAction: Boolean;
  206. var
  207.   OldState: TSearchStatus;
  208.   Value: Integer;
  209.   Line: array [0..15] of TMove;
  210. begin
  211.   OldState := ThinkState;
  212.   if (OldState = ssMoveSearch) and (ActiveTimer <> nil) then
  213.     ActiveTimer^.Start;
  214.   Think(Game, ChessSettings.ThinkTime.Position, ThinkState);
  215.     { Return True if we want to continue to get IdleAction calls ASAP,
  216.       Return False if we don't need more IdleAction immediately }
  217.   if (OldState = ssMoveSearch) and (ActiveTimer <> nil) then
  218.     ActiveTimer^.Stop;
  219.   IdleAction := False;
  220.   if (ThinkState = ssComplete) and (OldState <> ssComplete) then
  221.     AcceptComputerMove
  222.   else
  223.   if (ThinkState = ssGameOver) and (Oldstate <> ssGameOver) then
  224.     GameOver
  225.   else
  226.     IdleAction := True;  { continue calling IdleAction - still thinking }
  227. end;
  228.  
  229. function  TChessWindow.ShowMsg(const Ctx, MsgCode: Integer): Integer;
  230. var
  231.   S: array [0..100] of Char;
  232. begin
  233.   S[0] := #0;
  234.   if MsgCode <> 0 then
  235.     LoadString(HInstance, Ctx + MsgCode, S, SizeOf(S));
  236.   InfoPane^.Msg^.SetText(S);
  237.   ShowMsg := MsgCode;
  238. end;
  239.  
  240. procedure TChessWindow.ReportGameState;
  241. var State: TChessStatus;
  242.     Count: Integer;
  243. begin
  244.   State := GetChessStatus(Game, Count);
  245.   ShowMsg(cxChessState, ord(State));
  246.   InfoPane^.Update(Game, Timer[cWhite]^.GetCurrentTicks,
  247.                          Timer[cBlack]^.GetCurrentTicks);
  248. end;
  249.  
  250. procedure TChessWindow.RecordMove(const Move: TMove);
  251. begin
  252.   if MoveHistory^.Count = 0 then  { Enable the menu on first move }
  253.     EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or mf_Enabled);
  254.   if MoveHistory^.RedoAvail then  { not any more...}
  255.     EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or mf_Disabled
  256.                                                         or mf_Grayed);
  257.   MoveHistory^.AddMove(Move);
  258. end;
  259.  
  260. procedure TChessWindow.StartComputerMove;
  261. var
  262.   TimeLimit : Longint;
  263. begin
  264.   with ChessSettings do
  265.   begin
  266.     if NoLimit then
  267.       TimeLimit := MaxLongint
  268.     else {if LimitTurn then}  {!! do matchuser and limitgame}
  269.       TimeLimit := TurnTime * 18;
  270.   end;
  271.   ComputerMove(Game, TimeLimit);
  272. end;
  273.  
  274.  
  275. procedure TChessWindow.AcceptComputerMove;
  276. var
  277.   Move: TMove;
  278. begin
  279.   GetLastMove(Game, Move);
  280.   RecordMove(Move);
  281.   Board^.ExecuteMove(Move);
  282.   ReportGameState;
  283.   if Mode = AutoPlay then
  284.     StartComputerMove
  285.   else
  286.   begin
  287.     Board^.Enable;
  288.     SetMenu(HWindow, Attr.Menu);
  289.     Board^.ResetValidMoves;
  290.     ActiveTimer := Timer[GetPlayer(Game)];
  291.     ActiveTimer^.Start;
  292.     EnableMenuItem(Attr.Menu, cm_PauseGame, mf_ByCommand or mf_Enabled);
  293.   end;
  294. end;
  295.  
  296. procedure TChessWindow.GameOver;
  297. begin
  298.   if GetPlayer(Game) = cWhite then
  299.     MessageBox(HWindow, PChar(strWhiteWins), PChar(strGameOver), mb_OK)
  300.   else
  301.     MessageBox(HWindow, PChar(strBlackWins), PChar(strGameOver), mb_OK);
  302. end;
  303.  
  304. function TChessWindow.LoadGame(FileName: PChar): Boolean;
  305. var
  306.   S: PBufStream;
  307.   Test: array [0..SizeOf(ChessSignature)] of Char;
  308.   NewMoveList : PMoveList;
  309.   X: Integer;
  310.  
  311.   function ReplayMoves(P: PMove): Boolean; far;
  312.   begin
  313.     SubmitMove(Game, P^.Change);
  314.     Board^.ExecuteMove(P^);
  315.     ReplayMoves := (X >= MoveHistory^.UndoPos);
  316.     Inc(X);
  317.   end;
  318.  
  319. begin
  320.   LoadGame := False;
  321.   S := New(PBufStream, Init(FileName, stOpenRead, 1024));
  322.   S^.Read(Test, SizeOf(ChessSignature));
  323.   if S^.Status <> stOK then
  324.     MessageBox(HWindow, PChar(Context(cxStreamError, S^.Status)),
  325.                         PChar(strLoadError), mb_Ok)
  326.   else
  327.   if StrLComp(ChessSignature, Test, SizeOf(ChessSignature)) <> 0 then
  328.     MessageBox(HWindow, PChar(strNotAChessFile),
  329.                         PChar(strInvalidFile), mb_Ok)
  330.   else
  331.   begin
  332.     NewMoveList := PMoveList(S^.Get);
  333.     if S^.Status <> stOK then
  334.       MessageBox(HWindow, PChar(Context(cxStreamError, S^.Status)),
  335.                           PChar(strLoadError), mb_Ok)
  336.     else
  337.     begin
  338.       RestartGame;
  339.       Dispose(MoveHistory, Done);
  340.       MoveHistory := NewMoveList;
  341.       X := 0;
  342.       MoveHistory^.FirstThat(@ReplayMoves);
  343.       if MoveHistory^.UndoAvail then
  344.         EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or mf_Enabled);
  345.       if MoveHistory^.RedoAvail then
  346.         EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or mf_Enabled);
  347.       Board^.ResetValidMoves;
  348.       ReportGameState;
  349.       LoadGame := True;
  350.     end;
  351.   end;
  352.   Dispose(S, Done);
  353. end;
  354.  
  355. function TChessWindow.SaveGame(FileName: PChar): Boolean;
  356. var
  357.   S: PBufStream;
  358. begin
  359.   S := New(PBufStream, Init(FileName, stCreate, 1024));
  360.   S^.Write(ChessSignature, SizeOf(ChessSignature));
  361.   S^.Put(MoveHistory);
  362.   if S^.Status <> stOK then
  363.     MessageBox(HWindow, PChar(Context(cxStreamError, S^.Status)),
  364.                         PChar(strSaveError), mb_Ok);
  365.   SaveGame := S^.Status = stOK;
  366.   Dispose(S, Done);
  367. end;
  368.  
  369. procedure TChessWindow.AMSubmitMove(var Msg: TMessage);
  370. var
  371.   Move: TMove;
  372. begin
  373.   Msg.Result := ShowMsg(cxChessError, Ord(SubmitMove(Game, PChange(Msg.LParam)^)));
  374.     { Result = True if SubmitMove returns zero, else Result = False }
  375.   LongBool(Msg.Result) := not LongBool(Msg.Result);
  376.   if LongBool(Msg.Result) then
  377.   begin
  378.     if ActiveTimer <> nil then
  379.       ActiveTimer^.Stop;
  380.     ActiveTimer := Timer[GetPlayer(Game)];
  381.     GetLastMove(Game, Move);       { Retrieve the full move from the engine }
  382.     RecordMove(Move);              { Enter in history list, enable Redo menu}
  383.     Board^.ExecuteMove(Move);      { Adjust the board }
  384.     ReportGameState;
  385. (*    if ChessSettings.TwoPlayer then
  386.     begin
  387.       Player := GetPlayer(Game);
  388.       Board^.ResetValidMoves;       { Refresh the valid move tables }
  389.       ActiveTimer^.Start;
  390.     end
  391.     else
  392. *)    begin
  393.       StartComputerMove;
  394.       ThinkState := GetSearchStatus(Game);
  395.       SetMenu(HWindow, ThinkMenu);
  396.       Board^.Disable;              { Prevent mouse dragging of pieces }
  397.       EnableMenuItem(Attr.Menu, cm_PauseGame, mf_ByCommand or mf_Disabled or mf_Grayed);
  398.     end;
  399.   end;
  400. end;
  401.  
  402. procedure TChessWindow.CMNewGame(var Msg: TMessage);
  403. begin
  404.   if (ThinkState = ssGameOver) or
  405.      (MoveHistory^.Count = 0) or
  406.      (MessageBox(HWindow, PChar(strCancelGame),
  407.         PChar(strStartNewGame), mb_YesNo) = id_Yes) then
  408.   begin
  409.     RestartGame;
  410.   end;
  411. end;
  412.  
  413. procedure TChessWindow.CMLoadGame(var Msg: TMessage);
  414. var
  415.   Temp : array [0..fsPathName] of Char;
  416. begin
  417.   StrCopy(Temp, GameFileName);
  418.   if ((ThinkState = ssGameOver) or
  419.      (MoveHistory^.Count = 0) or
  420.      (MessageBox(HWindow, PChar(strCancelGame),
  421.         PChar(strLoadSavedGame), mb_YesNo) = id_Yes))
  422.     and
  423.      (XApp^.ExecDialog(New(PCDFileOpen, Init(@Self,
  424.         ofn_FileMustExist, Temp, SizeOf(Temp),
  425.         ChessFileFilter))) = idOk) then
  426.     if LoadGame(Temp) then
  427.       StrCopy(GameFileName, Temp);
  428. end;
  429.  
  430. procedure TChessWindow.CMSaveGame(var Msg: TMessage);
  431. begin
  432.   if GameFileName[0] = #0 then
  433.     CMSaveAs(Msg)
  434.   else
  435.     SaveGame(GameFileName);
  436. end;
  437.  
  438. procedure TChessWindow.CMSaveAs(var Msg: TMessage);
  439. var
  440.   Temp : array [0..fsPathName] of Char;
  441. begin
  442.   StrCopy(Temp, GameFileName);
  443.   if XApp^.ExecDialog(New(PCDFileSaveAs, Init(@Self,
  444.       ofn_PathMustExist, Temp, SizeOf(Temp), ChessFileFilter))) = idOk then
  445.     if SaveGame(Temp) then
  446.       StrCopy(GameFileName, Temp);
  447. end;
  448.  
  449. procedure TChessWindow.CMPauseGame(var Msg: TMessage);
  450. var
  451.   P: PChar;
  452. begin
  453.   if ActiveTimer = Timer[Player] then
  454.   begin
  455.     ActiveTimer^.Stop;
  456.     ActiveTimer := nil;
  457.     ModifyMenu(Attr.Menu, cm_PauseGame, mf_ByCommand or mf_String,
  458.                            cm_PauseGame, StrNewRes(P, PChar(strResumeMenu)));
  459.     StrDispose(P);
  460.     Board^.Disable;
  461.   end
  462.   else
  463.     if ActiveTimer = nil then
  464.     begin
  465.       ActiveTimer := Timer[GetPlayer(Game)];
  466.       ActiveTimer^.Start;
  467.       ModifyMenu(Attr.Menu, cm_PauseGame, mf_ByCommand or mf_String,
  468.                              cm_PauseGame, StrNewRes(P, PChar(strPauseMenu)));
  469.       StrDispose(P);
  470.       Board^.Enable;
  471.     end;
  472. end;
  473.  
  474.  
  475. procedure TChessWindow.CMUndoMove(var Msg: TMessage);
  476. var
  477.   M : TMove;
  478.   RedoBefore, UndoBefore: Boolean;
  479. begin
  480.   { No error checking is performed here - it is assumed that the
  481.     menu enable/disable code will only allow the user
  482.     to select this menu item when there is a valid undo available. }
  483.  
  484.   UndoBefore := MoveHistory^.UndoAvail;
  485.   RedoBefore := MoveHistory^.RedoAvail;
  486.  
  487.   MoveHistory^.Undo(M);
  488.   RetractMove(Game, M);
  489.   Board^.RetractMove(M);
  490.  
  491.   if Mode = SinglePlayer then  { Undo both player's and computer's move }
  492.   begin
  493.     MoveHistory^.Undo(M);
  494.     RetractMove(Game, M);
  495.     Board^.RetractMove(M);
  496.   end;
  497.   if MoveHistory^.RedoAvail and not RedoBefore then
  498.     EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or mf_Enabled);
  499.   if (not MoveHistory^.UndoAvail) and UndoBefore then
  500.    EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or
  501.                                           mf_Disabled or mf_Grayed);
  502.   Board^.ResetValidMoves;
  503. end;
  504.  
  505. procedure TChessWindow.CMRedoMove(var Msg: TMessage);
  506. var
  507.   M : TMove;
  508.   RedoBefore, UndoBefore: Boolean;
  509. begin
  510.   { No error checking is performed here - it is assumed that the
  511.     menu enable/disable code will only allow the user
  512.     to select this menu item when there is a valid redo available. }
  513.   UndoBefore := MoveHistory^.UndoAvail;
  514.   RedoBefore := MoveHistory^.RedoAvail;
  515.  
  516.   MoveHistory^.Redo(M);
  517.   SubmitMove(Game, M.Change);
  518.   Board^.ExecuteMove(M);
  519.  
  520.   if Mode = SinglePlayer then
  521.   begin
  522.     MoveHistory^.Redo(M);  { Redo both player's and computer's moves }
  523.     SubmitMove(Game, M.Change);
  524.     Board^.ExecuteMove(M);
  525.   end;
  526.  
  527.   { Update the menus, but only when the undo/redo state changes
  528.     (to avoid menubar flicker caused by unnecessary menu changes) }
  529.   if (not MoveHistory^.RedoAvail) and RedoBefore then
  530.     EnableMenuItem(Attr.Menu, cm_RedoMove, mf_ByCommand or
  531.                                            mf_Disabled or mf_Grayed);
  532.   if MoveHistory^.UndoAvail and not UndoBefore then
  533.     EnableMenuItem(Attr.Menu, cm_UndoMove, mf_ByCommand or mf_Enabled);
  534.   Board^.ResetValidMoves;
  535. end;
  536.  
  537. procedure TChessWindow.CMSettings(var Msg: TMessage);
  538. begin
  539.   XApp^.ExecDialog(new(PSettingsDlg, Init(@Self, PChar(dlgSettings),
  540.                                                  ChessSettings)));
  541. end;
  542.  
  543. procedure TChessWindow.CMStopThinking(var Msg: TMessage);
  544. begin
  545.   if ThinkState = ssMoveSearch then
  546.     ForceMove(Game);     { Move search will terminate at next Think call }
  547. end;
  548.  
  549. procedure TChessWindow.WMTimer(var Msg: TMessage);
  550. begin
  551.   InfoPane^.Update(Game, Timer[cWhite]^.GetCurrentTicks,
  552.                          Timer[cBlack]^.GetCurrentTicks);
  553. end;
  554.  
  555. procedure TChessWindow.WMSetCursor(var Msg: TMessage);
  556. begin
  557.   DefWndProc(Msg);
  558.   Msg.Result := 1;  { Cancel any pending WMSetCursor in children }
  559.   if (ThinkState = ssMoveSearch) and (Msg.LParamLo = HTClient) then
  560.     SetCursor(LoadCursor(0, PChar(idc_Wait)))
  561.   else
  562.   begin
  563.     if Msg.WParam = Board^.HWindow then
  564.       Msg.Result := 0   { Allow Board to use its own cursor }
  565.     else
  566.       SetCursor(LoadCursor(0, PChar(idc_Arrow)));
  567.   end;
  568. end;
  569.  
  570.  
  571. procedure TChessApp.InitMainWindow;
  572. begin
  573.   MainWindow := new(PChessWindow, Init(nil, 'OWL Chess'));
  574. end;
  575.  
  576. function TChessApp.IdleAction: Boolean;
  577. begin
  578.   IdleAction := PChessWindow(MainWindow)^.IdleAction;
  579. end;
  580.  
  581. var
  582.   App: TChessApp;
  583.  
  584. begin
  585.   RegisterType(RMoveList);
  586.   App.Init('OWL Chess');
  587.   App.Run;
  588.   App.Done;
  589. end.
  590.