You've already forked lazarus-ccr
fpchess: Advances the FICS code
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@1904 8e941d3f-bd1b-0410-a28a-d453659cc2b4
This commit is contained in:
@ -116,8 +116,10 @@ type
|
|||||||
procedure StartNewGame(APlayAsWhite: Boolean; AUseTimer: Boolean; APlayerTime: Integer); overload;
|
procedure StartNewGame(APlayAsWhite: Boolean; AUseTimer: Boolean; APlayerTime: Integer); overload;
|
||||||
procedure StartNewGame(APlayAsWhite: Integer; AUseTimer: Boolean; APlayerTime: Integer); overload;
|
procedure StartNewGame(APlayAsWhite: Integer; AUseTimer: Boolean; APlayerTime: Integer); overload;
|
||||||
function ClientToBoardCoords(AClientCoords: TPoint): TPoint;
|
function ClientToBoardCoords(AClientCoords: TPoint): TPoint;
|
||||||
function BoardPosToChessCoords(APos: TPoint): string;
|
class function BoardPosToChessCoords(APos: TPoint): string;
|
||||||
function ColumnNumToLetter(ACol: Integer): string;
|
class function ChessCoordsToBoardPos(AStr: string): TPoint;
|
||||||
|
class procedure ChessMoveCoordsToBoardPos(AMoveStr: string; AFrom, ATo: TPoint);
|
||||||
|
class function ColumnNumToLetter(ACol: Integer): string;
|
||||||
function CheckStartMove(AFrom: TPoint): Boolean;
|
function CheckStartMove(AFrom: TPoint): Boolean;
|
||||||
function CheckEndMove(ATo: TPoint): Boolean;
|
function CheckEndMove(ATo: TPoint): Boolean;
|
||||||
function FindKing(): TPoint;
|
function FindKing(): TPoint;
|
||||||
@ -735,7 +737,7 @@ begin
|
|||||||
Result.Y := 1 + (INT_CHESSBOARD_SIZE - AClientCoords.Y) div INT_CHESSTILE_SIZE;
|
Result.Y := 1 + (INT_CHESSBOARD_SIZE - AClientCoords.Y) div INT_CHESSTILE_SIZE;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TChessGame.BoardPosToChessCoords(APos: TPoint): string;
|
class function TChessGame.BoardPosToChessCoords(APos: TPoint): string;
|
||||||
var
|
var
|
||||||
lStr: string;
|
lStr: string;
|
||||||
begin
|
begin
|
||||||
@ -743,7 +745,30 @@ begin
|
|||||||
Result := Format('%s%d', [lStr, APos.Y]);
|
Result := Format('%s%d', [lStr, APos.Y]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TChessGame.ColumnNumToLetter(ACol: Integer): string;
|
class function TChessGame.ChessCoordsToBoardPos(AStr: string): TPoint;
|
||||||
|
var
|
||||||
|
lStr: string;
|
||||||
|
begin
|
||||||
|
if Length(AStr) < 2 then raise Exception.Create('[TChessGame.ChessCoordsToBoardPos] Length(AStr) < 2');
|
||||||
|
lStr := Copy(AStr, 1, 1);
|
||||||
|
lStr := LowerCase(lStr);
|
||||||
|
Result.X := Byte(lStr[1]) - 96;
|
||||||
|
lStr := Copy(AStr, 2, 1);
|
||||||
|
Result.Y := StrToInt(lStr);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class procedure TChessGame.ChessMoveCoordsToBoardPos(AMoveStr: string; AFrom,
|
||||||
|
ATo: TPoint);
|
||||||
|
var
|
||||||
|
lStr: String;
|
||||||
|
begin
|
||||||
|
lStr := Copy(AMoveStr, 1, 2);
|
||||||
|
AFrom := TChessGame.ChessCoordsToBoardPos(lStr);
|
||||||
|
lStr := Copy(AMoveStr, 3, 2);
|
||||||
|
ATo := TChessGame.ChessCoordsToBoardPos(lStr);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TChessGame.ColumnNumToLetter(ACol: Integer): string;
|
||||||
begin
|
begin
|
||||||
Result := Char(ACol + 96);
|
Result := Char(ACol + 96);
|
||||||
end;
|
end;
|
||||||
|
@ -27,6 +27,7 @@ type
|
|||||||
function IsMovingAllowedNow(): Boolean; virtual;
|
function IsMovingAllowedNow(): Boolean; virtual;
|
||||||
function GetSecondPlayerName(): string; virtual; abstract;
|
function GetSecondPlayerName(): string; virtual; abstract;
|
||||||
procedure HandleOnMove(AFrom, ATo: TPoint); virtual; abstract;
|
procedure HandleOnMove(AFrom, ATo: TPoint); virtual; abstract;
|
||||||
|
procedure HandleOnTimer(); virtual;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
var
|
||||||
@ -36,6 +37,7 @@ var
|
|||||||
procedure RegisterChessModule(AModule: TChessModule);
|
procedure RegisterChessModule(AModule: TChessModule);
|
||||||
procedure PopulateChessModulesList(AList: TStrings);
|
procedure PopulateChessModulesList(AList: TStrings);
|
||||||
function GetChessModule(AIndex: Integer): TChessModule;
|
function GetChessModule(AIndex: Integer): TChessModule;
|
||||||
|
function GetCurrentChessModule: TChessModule;
|
||||||
function GetChessModuleCount(): Integer;
|
function GetChessModuleCount(): Integer;
|
||||||
procedure ChessModuleDebugLn(AStr: string);
|
procedure ChessModuleDebugLn(AStr: string);
|
||||||
procedure ChessModuleDebugOut(AStr: string);
|
procedure ChessModuleDebugOut(AStr: string);
|
||||||
@ -83,6 +85,11 @@ begin
|
|||||||
Result := TChessModule(gChessModules.Items[AIndex]);
|
Result := TChessModule(gChessModules.Items[AIndex]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function GetCurrentChessModule: TChessModule;
|
||||||
|
begin
|
||||||
|
Result := GetChessModule(gSelectedModuleIndex);
|
||||||
|
end;
|
||||||
|
|
||||||
function GetChessModuleCount: Integer;
|
function GetChessModuleCount: Integer;
|
||||||
begin
|
begin
|
||||||
Result := gChessModules.Count;
|
Result := gChessModules.Count;
|
||||||
@ -112,6 +119,11 @@ begin
|
|||||||
Result := not (vChessGame.IsWhitePlayerTurn xor vChessGame.FirstPlayerIsWhite);
|
Result := not (vChessGame.IsWhitePlayerTurn xor vChessGame.FirstPlayerIsWhite);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TChessModule.HandleOnTimer;
|
||||||
|
begin
|
||||||
|
|
||||||
|
end;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
gChessModules := TList.Create;
|
gChessModules := TList.Create;
|
||||||
vChessGame.OnAfterMove := @HandleOnMove;
|
vChessGame.OnAfterMove := @HandleOnMove;
|
||||||
|
@ -20,6 +20,7 @@ type
|
|||||||
FOnDebugOut: TLTelnetDebugOutProc;
|
FOnDebugOut: TLTelnetDebugOutProc;
|
||||||
RegexObj: TRegExpr;
|
RegexObj: TRegExpr;
|
||||||
public
|
public
|
||||||
|
LastMsg: string;
|
||||||
constructor Create(aOwner: TComponent); override;
|
constructor Create(aOwner: TComponent); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
function WaitFor(FirstMatch, SecondMatch: string; ATimeOut: Cardinal): Integer;
|
function WaitFor(FirstMatch, SecondMatch: string; ATimeOut: Cardinal): Integer;
|
||||||
@ -49,14 +50,17 @@ function TLTelnetClientEx.WaitFor(FirstMatch, SecondMatch: string;
|
|||||||
ATimeOut: Cardinal): Integer;
|
ATimeOut: Cardinal): Integer;
|
||||||
var
|
var
|
||||||
lMsg: string;
|
lMsg: string;
|
||||||
|
lRemaining: Integer;
|
||||||
begin
|
begin
|
||||||
|
lRemaining := ATimeOut;
|
||||||
Result := -1;
|
Result := -1;
|
||||||
if (FirstMatch = '') and (SecondMatch = '') then Exit;
|
if (FirstMatch = '') and (SecondMatch = '') then Exit;
|
||||||
|
|
||||||
while True do
|
repeat
|
||||||
begin
|
|
||||||
if GetMessage(lMsg) > 0 then if Assigned(OnDebugOut) then OnDebugOut(lMsg);
|
if GetMessage(lMsg) > 0 then if Assigned(OnDebugOut) then OnDebugOut(lMsg);
|
||||||
|
|
||||||
|
LastMsg := lMsg;
|
||||||
|
|
||||||
if FirstMatch <> '' then
|
if FirstMatch <> '' then
|
||||||
begin
|
begin
|
||||||
RegexObj.Expression := FirstMatch;
|
RegexObj.Expression := FirstMatch;
|
||||||
@ -70,9 +74,13 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
CallAction; // don't forget to make the clock tick :)
|
CallAction; // don't forget to make the clock tick :)
|
||||||
|
if lRemaining > 0 then // Don't sleep if the routine was called with ATimeOut=0
|
||||||
|
begin
|
||||||
Application.ProcessMessages;
|
Application.ProcessMessages;
|
||||||
Sleep(100);
|
Sleep(100);
|
||||||
end;
|
end;
|
||||||
|
Dec(lRemaining, 100);
|
||||||
|
until lRemaining <= 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -99,6 +99,7 @@ begin
|
|||||||
vChessGame.UpdateTimes();
|
vChessGame.UpdateTimes();
|
||||||
UpdateCaptions();
|
UpdateCaptions();
|
||||||
vChessDrawer.HandleOnTimer(Sender);
|
vChessDrawer.HandleOnTimer(Sender);
|
||||||
|
GetCurrentChessModule().HandleOnTimer();
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TformChess.FormatTime(ATimeInMiliseconds: Integer): string;
|
function TformChess.FormatTime(ATimeInMiliseconds: Integer): string;
|
||||||
|
@ -15,16 +15,22 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils,
|
Classes, SysUtils,
|
||||||
StdCtrls, Forms, Controls,
|
StdCtrls, Forms, Controls, ExtCtrls, maskedit,
|
||||||
lTelnetex, lnet,
|
lTelnetex, lnet,
|
||||||
sorokinregexpr, // Rename to regexpr when FPC 2.8 comes with this
|
sorokinregexpr, // Rename to regexpr when FPC 2.8 comes with this
|
||||||
chessmodules, chessgame;
|
chessmodules, chessgame, chessdrawer;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
{ TFICSChessModule }
|
{ TFICSChessModule }
|
||||||
|
|
||||||
TFICSChessModule = class(TChessModule)
|
TFICSChessModule = class(TChessModule)
|
||||||
|
private
|
||||||
|
radioConnectionType: TRadioGroup;
|
||||||
|
textPassword: TStaticText;
|
||||||
|
editPassword: TMaskEdit;
|
||||||
|
textSecondPlayerName: TStaticText;
|
||||||
|
editSecondPlayerName: TEdit;
|
||||||
public
|
public
|
||||||
SecondPlayerName: string;
|
SecondPlayerName: string;
|
||||||
TelnetComm: TLTelnetClientEx;
|
TelnetComm: TLTelnetClientEx;
|
||||||
@ -32,8 +38,6 @@ type
|
|||||||
FICS_PORT: Integer;
|
FICS_PORT: Integer;
|
||||||
FICS_USER: string;
|
FICS_USER: string;
|
||||||
FICS_PASSWORD: string;
|
FICS_PASSWORD: string;
|
||||||
// Frequency to issue commands to avoid disconnection, in miliseconds
|
|
||||||
PROTECT_LOGOUT_FREQ: Integer;
|
|
||||||
constructor Create(); override;
|
constructor Create(); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
procedure CreateUserInterface(); override;
|
procedure CreateUserInterface(); override;
|
||||||
@ -43,12 +47,14 @@ type
|
|||||||
procedure PrepareForGame(); override;
|
procedure PrepareForGame(); override;
|
||||||
function GetSecondPlayerName(): string; override;
|
function GetSecondPlayerName(): string; override;
|
||||||
procedure HandleOnMove(AFrom, ATo: TPoint); override;
|
procedure HandleOnMove(AFrom, ATo: TPoint); override;
|
||||||
|
procedure HandleOnTimer(); override;
|
||||||
procedure HandleOnDebugOut(AStr: string);
|
procedure HandleOnDebugOut(AStr: string);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
const
|
const
|
||||||
FICS_LineEnding = #10;
|
FICS_LineEnding = #10;
|
||||||
OPEN_TIMEOUT = 10000;
|
OPEN_TIMEOUT = 1000000;
|
||||||
|
PROTECT_LOGOUT_FREQ = 45 * 60 * 1000; // Frequency to issue commands to avoid disconnection, in miliseconds
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
@ -75,7 +81,6 @@ begin
|
|||||||
FICS_PORT := 5000;
|
FICS_PORT := 5000;
|
||||||
FICS_USER := 'FPChess';
|
FICS_USER := 'FPChess';
|
||||||
FICS_PASSWORD := '';
|
FICS_PASSWORD := '';
|
||||||
PROTECT_LOGOUT_FREQ := 45 * 60 * 1000;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TFICSChessModule.Destroy;
|
destructor TFICSChessModule.Destroy;
|
||||||
@ -87,31 +92,54 @@ end;
|
|||||||
|
|
||||||
procedure TFICSChessModule.CreateUserInterface;
|
procedure TFICSChessModule.CreateUserInterface;
|
||||||
begin
|
begin
|
||||||
{ textSecondPlayerName := TStaticText.Create(nil);
|
radioConnectionType := TRadioGroup.Create(nil);
|
||||||
textSecondPlayerName.SetBounds(20, 20, 180, 50);
|
radioConnectionType.SetBounds(10, 10, 300, 100);
|
||||||
textSecondPlayerName.Caption := 'Name of the second player';
|
radioConnectionType.Caption := 'FICS Connection Type';
|
||||||
|
radioConnectionType.Items.Add('Wait for a friend to connect to me');
|
||||||
|
radioConnectionType.Items.Add('Connect to a friend (he needs to be waiting for the connection)');
|
||||||
|
|
||||||
|
textPassword := TStaticText.Create(nil);
|
||||||
|
textPassword.SetBounds(10, 110, 180, 20);
|
||||||
|
textPassword.Caption := 'Your FICS Password';
|
||||||
|
|
||||||
|
editPassword := TMaskEdit.Create(nil);
|
||||||
|
editPassword.SetBounds(200, 110, 150, 20);
|
||||||
|
editPassword.Text := '';
|
||||||
|
|
||||||
|
textSecondPlayerName := TStaticText.Create(nil);
|
||||||
|
textSecondPlayerName.SetBounds(10, 130, 180, 40);
|
||||||
|
textSecondPlayerName.Caption := 'FICS Login of the other player';
|
||||||
|
|
||||||
editSecondPlayerName := TEdit.Create(nil);
|
editSecondPlayerName := TEdit.Create(nil);
|
||||||
editSecondPlayerName.SetBounds(200, 20, 150, 50);
|
editSecondPlayerName.SetBounds(200, 130, 150, 40);
|
||||||
editSecondPlayerName.Text := 'Second player';}
|
editSecondPlayerName.Text := '';
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TFICSChessModule.ShowUserInterface(AParent: TWinControl);
|
procedure TFICSChessModule.ShowUserInterface(AParent: TWinControl);
|
||||||
begin
|
begin
|
||||||
{ textSecondPlayerName.Parent := AParent;
|
radioConnectionType.Parent := AParent;
|
||||||
editSecondPlayerName.Parent := AParent;}
|
textPassword.Parent := AParent;
|
||||||
|
editPassword.Parent := AParent;
|
||||||
|
textSecondPlayerName.Parent := AParent;
|
||||||
|
editSecondPlayerName.Parent := AParent;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TFICSChessModule.HideUserInterface();
|
procedure TFICSChessModule.HideUserInterface();
|
||||||
begin
|
begin
|
||||||
{ textSecondPlayerName.Parent := nil;
|
radioConnectionType.Parent := nil;
|
||||||
editSecondPlayerName.Parent := nil;}
|
textPassword.Parent := nil;
|
||||||
|
editPassword.Parent := nil;
|
||||||
|
textSecondPlayerName.Parent := nil;
|
||||||
|
editSecondPlayerName.Parent := nil;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TFICSChessModule.FreeUserInterface;
|
procedure TFICSChessModule.FreeUserInterface;
|
||||||
begin
|
begin
|
||||||
{ textSecondPlayerName.Free;
|
radioConnectionType.Free;
|
||||||
editSecondPlayerName.Free;}
|
textPassword.Free;
|
||||||
|
editPassword.Free;
|
||||||
|
textSecondPlayerName.Free;
|
||||||
|
editSecondPlayerName.Free;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TFICSChessModule.PrepareForGame;
|
procedure TFICSChessModule.PrepareForGame;
|
||||||
@ -119,6 +147,9 @@ var
|
|||||||
lResult, WaitTerminated: Boolean;
|
lResult, WaitTerminated: Boolean;
|
||||||
lMsg: string;
|
lMsg: string;
|
||||||
begin
|
begin
|
||||||
|
FICS_USER := 'FPChess';
|
||||||
|
FICS_PASSWORD := editPassword.Text;
|
||||||
|
|
||||||
// SecondPlayerName := editSecondPlayerName.Text;
|
// SecondPlayerName := editSecondPlayerName.Text;
|
||||||
ChessModuleDebugLn('[TFICSChessModule.PrepareForGame]');
|
ChessModuleDebugLn('[TFICSChessModule.PrepareForGame]');
|
||||||
|
|
||||||
@ -207,6 +238,53 @@ begin
|
|||||||
'',
|
'',
|
||||||
OPEN_TIMEOUT);
|
OPEN_TIMEOUT);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Remove those annoying message of people seeking chess adversaries
|
||||||
|
TelnetComm.WaitFor(
|
||||||
|
'.*fics%.*',
|
||||||
|
'',
|
||||||
|
OPEN_TIMEOUT);
|
||||||
|
TelnetComm.SendMessage('set seek 0' + FICS_LineEnding);
|
||||||
|
|
||||||
|
// Set the style
|
||||||
|
TelnetComm.WaitFor(
|
||||||
|
'.*fics%.*',
|
||||||
|
'',
|
||||||
|
OPEN_TIMEOUT);
|
||||||
|
TelnetComm.SendMessage('set style 11' + FICS_LineEnding);
|
||||||
|
|
||||||
|
// Wait for a match
|
||||||
|
if radioConnectionType.ItemIndex = 0 then
|
||||||
|
begin
|
||||||
|
// Challenge: GuestZMYL (----) fpchess (----) unrated blitz 2 12.
|
||||||
|
// You can "accept" or "decline", or propose different parameters.
|
||||||
|
TelnetComm.WaitFor(
|
||||||
|
'.*You can "accept" or "decline", or propose different parameters*',
|
||||||
|
'',
|
||||||
|
OPEN_TIMEOUT);
|
||||||
|
TelnetComm.SendMessage('accept ' + FICS_LineEnding);
|
||||||
|
|
||||||
|
// You accept the match offer from GuestZMYL.
|
||||||
|
TelnetComm.WaitFor(
|
||||||
|
'.*You accept the match offer from*',
|
||||||
|
'',
|
||||||
|
OPEN_TIMEOUT);
|
||||||
|
end
|
||||||
|
// Challenge a partner
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
TelnetComm.WaitFor(
|
||||||
|
'.*fics%.*',
|
||||||
|
'',
|
||||||
|
OPEN_TIMEOUT);
|
||||||
|
TelnetComm.SendMessage('match ' + editSecondPlayerName.Text + FICS_LineEnding);
|
||||||
|
|
||||||
|
// fpchess accepts the match offer.
|
||||||
|
TelnetComm.WaitFor(
|
||||||
|
'.*accepts the match offer*',
|
||||||
|
'',
|
||||||
|
OPEN_TIMEOUT);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TFICSChessModule.GetSecondPlayerName: string;
|
function TFICSChessModule.GetSecondPlayerName: string;
|
||||||
@ -215,10 +293,43 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
// If a move came, it is because the local player did a move
|
// If a move came, it is because the local player did a move
|
||||||
// so send this move and start listening for a move
|
// so send this move
|
||||||
procedure TFICSChessModule.HandleOnMove(AFrom, ATo: TPoint);
|
procedure TFICSChessModule.HandleOnMove(AFrom, ATo: TPoint);
|
||||||
|
var
|
||||||
|
lMsg: String;
|
||||||
begin
|
begin
|
||||||
|
lMsg := Format('%s-%s', [TChessGame.BoardPosToChessCoords(AFrom), TChessGame.BoardPosToChessCoords(ATo)]);
|
||||||
|
TelnetComm.WaitFor(
|
||||||
|
'.*fics%.*',
|
||||||
|
'',
|
||||||
|
OPEN_TIMEOUT);
|
||||||
|
TelnetComm.SendMessage(lMsg + FICS_LineEnding);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// listen for moves
|
||||||
|
procedure TFICSChessModule.HandleOnTimer;
|
||||||
|
var
|
||||||
|
lIndex: Integer;
|
||||||
|
lAnimation: TChessMoveAnimation;
|
||||||
|
lFrom, lTo: TPoint;
|
||||||
|
lMoveStr: String;
|
||||||
|
begin
|
||||||
|
// Example output in style 11
|
||||||
|
// #@#086GuestZMYL :fpchess *RNBQKBNR PPP PP P PP p p ppp ppprnbqkbnr003B3939-1628-0163P/a2-a4(1:03)@#@
|
||||||
|
|
||||||
|
lIndex := TelnetComm.WaitFor(
|
||||||
|
'.*[PRNBQK]/[abcdefgh][0123456789]-[abcdefgh][0123456789].*',
|
||||||
|
'',
|
||||||
|
0);
|
||||||
|
if lIndex = 0 then
|
||||||
|
begin
|
||||||
|
lMoveStr := Copy(TelnetComm.LastMsg, Pos('/', TelnetComm.LastMsg)+1, 5);
|
||||||
|
TChessGame.ChessMoveCoordsToBoardPos(lMoveStr, lFrom, lTo);
|
||||||
|
lAnimation := TChessMoveAnimation.Create;
|
||||||
|
lAnimation.AFrom := lFrom;
|
||||||
|
lAnimation.ATo := lTo;
|
||||||
|
vChessDrawer.AddAnimation(lAnimation);
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TFICSChessModule.HandleOnDebugOut(AStr: string);
|
procedure TFICSChessModule.HandleOnDebugOut(AStr: string);
|
||||||
|
Reference in New Issue
Block a user