2011-08-27 20:53:13 +00:00
{
For playing through the internet via FICS - Free Internet Chess Server
Based on this article:
http: //blog.mekk.waw.pl/archives/7-How-to-write-a-FICS-bot-part-I.html
FICS website:
http: //www.freechess.org/
}
unit mod_fics;
{$mode objfpc} {$H+}
interface
uses
Classes, SysUtils,
StdCtrls, Forms, Controls,
2011-09-05 16:41:42 +00:00
lTelnet, lnet,
sorokinregexpr, // Remove when FPC 2.8 comes with this
2011-08-27 20:53:13 +00:00
chessmodules, chessgame;
type
2011-08-27 21:16:40 +00:00
{ TFICSChessModule }
2011-08-27 20:53:13 +00:00
2011-08-27 21:16:40 +00:00
TFICSChessModule = class( TChessModule)
2011-08-27 20:53:13 +00:00
public
SecondPlayerName: string ;
2011-08-27 21:49:44 +00:00
TelnetComm: TLTelnetClient;
FICS_HOST: string ;
FICS_PORT: Integer ;
FICS_USER: string ;
FICS_PASSWORD: string ;
// Frequency to issue commands to avoid disconnection, in miliseconds
PROTECT_LOGOUT_FREQ: Integer ;
2011-09-05 16:41:42 +00:00
RegexObj: TRegExpr;
2011-08-30 16:33:47 +00:00
constructor Create( ) ; override ;
2011-08-27 21:49:44 +00:00
destructor Destroy; override ;
2011-08-27 20:53:13 +00:00
procedure CreateUserInterface( ) ; override ;
procedure ShowUserInterface( AParent: TWinControl) ; override ;
procedure HideUserInterface( ) ; override ;
procedure FreeUserInterface( ) ; override ;
procedure PrepareForGame( ) ; override ;
function GetSecondPlayerName( ) : string ; override ;
procedure HandleOnMove( AFrom, ATo: TPoint) ; override ;
end ;
implementation
2011-08-27 21:16:40 +00:00
{ TFICSChessModule }
2011-08-27 20:53:13 +00:00
2011-08-27 21:16:40 +00:00
constructor TFICSChessModule. Create;
2011-08-27 20:53:13 +00:00
begin
inherited Create;
2011-09-05 16:41:42 +00:00
RegexObj : = TRegExpr. Create;
2011-08-27 21:49:44 +00:00
TelnetComm : = TLTelnetClient. Create( nil ) ;
( * $ telnet = new Net: : Telnet(
Timeout = > $ OPEN_TIMEOUT,
Binmode = > 1 ,
Errmode = > 'die' ,
) ; * )
2011-08-30 16:33:47 +00:00
Name : = 'mod_fics.pas' ;
SelectionDescription : = 'Play online - Free Internet Chess Server' ;
PlayingDescription : = 'Playing online - Free Internet Chess Server' ;
2011-08-30 13:46:20 +00:00
Kind : = cmkInternet;
2011-08-27 21:49:44 +00:00
FICS_HOST : = 'freechess.org' ;
FICS_PORT : = 5 0 0 0 ;
FICS_USER : = 'BotTutorial' ;
FICS_PASSWORD : = '' ;
PROTECT_LOGOUT_FREQ : = 4 5 * 6 0 * 1 0 0 0 ;
end ;
destructor TFICSChessModule. Destroy;
begin
TelnetComm. Free;
2011-09-05 16:41:42 +00:00
RegexObj. Free;
2011-08-27 21:49:44 +00:00
inherited Destroy;
2011-08-27 20:53:13 +00:00
end ;
2011-08-27 21:16:40 +00:00
procedure TFICSChessModule. CreateUserInterface;
2011-08-27 20:53:13 +00:00
begin
{ textSecondPlayerName : = TStaticText. Create( nil ) ;
textSecondPlayerName. SetBounds( 2 0 , 2 0 , 1 8 0 , 5 0 ) ;
textSecondPlayerName. Caption : = 'Name of the second player' ;
editSecondPlayerName : = TEdit. Create( nil ) ;
editSecondPlayerName. SetBounds( 2 0 0 , 2 0 , 1 5 0 , 5 0 ) ;
editSecondPlayerName. Text : = 'Second player' ; }
end ;
2011-08-27 21:16:40 +00:00
procedure TFICSChessModule. ShowUserInterface( AParent: TWinControl) ;
2011-08-27 20:53:13 +00:00
begin
{ textSecondPlayerName. Parent : = AParent;
editSecondPlayerName. Parent : = AParent; }
end ;
2011-08-27 21:16:40 +00:00
procedure TFICSChessModule. HideUserInterface( ) ;
2011-08-27 20:53:13 +00:00
begin
{ textSecondPlayerName. Parent : = nil ;
editSecondPlayerName. Parent : = nil ; }
end ;
2011-08-27 21:16:40 +00:00
procedure TFICSChessModule. FreeUserInterface;
2011-08-27 20:53:13 +00:00
begin
{ textSecondPlayerName. Free;
editSecondPlayerName. Free; }
end ;
2011-08-27 21:16:40 +00:00
procedure TFICSChessModule. PrepareForGame;
2011-08-27 21:49:44 +00:00
var
2011-09-05 16:41:42 +00:00
lResult, WaitTerminated: Boolean ;
lMsg: string ;
2011-08-27 20:53:13 +00:00
begin
// SecondPlayerName := editSecondPlayerName.Text;
2011-08-27 21:16:40 +00:00
ChessModuleDebugLn( '[TFICSChessModule.PrepareForGame]' ) ;
2011-08-27 21:49:44 +00:00
// Opening telnet connection. This is what happens when you issue telnet freechess.org 5000.
lResult : = TelnetComm. Connect( FICS_HOST, FICS_PORT) ;
if not lResult then
begin
ChessModuleDebugLn( 'Failed to connect to FICS' ) ;
Exit;
end ;
2011-09-05 16:41:42 +00:00
repeat
TelnetComm. CallAction; // repeat this to get info
// if KeyPressed then
// Halt;
until TelnetComm. Connected; // wait until timeout or we actualy connected
2011-08-27 21:49:44 +00:00
ChessModuleDebugLn( 'Connected to FICS' ) ;
// If $FICS_PASSWORD is given, we peform normal full login (give username and password). FICS is standard enough to have Net::Telnet::login routine perform this process properly.
if FICS_PASSWORD < > '' then
begin
//$telnet->login(Name => $FICS_USER, Password => $FICS_PASSWORD);
// $username = $FICS_USER;
// print STDERR "Successfully logged as user $FICS_USER\n" if $VERBOSE;
end
2011-09-05 16:41:42 +00:00
// Now let's go to the guest login. Again, try logging to FICS via telnet as guest to understand what we are testing for here.
2011-08-27 21:49:44 +00:00
else
begin
2011-09-05 16:41:42 +00:00
{
$ telnet- > waitfor(
Match = > '/login[: ]*$/i' ,
Match = > '/username[: ]*$/i' ,
Timeout = > $ OPEN_TIMEOUT) ;
$ telnet- > print( $F ICS_USER) ;
}
WaitTerminated : = False ;
while not WaitTerminated do
begin
if TelnetComm. GetMessage( lMsg) > 0 then
ChessModuleDebugOut( lMsg) ;
RegexObj. Expression : = '/login[: ]*$/i' ;
if RegexObj. Exec( lMsg) then WaitTerminated : = True ;
TelnetComm. CallAction; // don't forget to make the clock tick :)
Application. ProcessMessages;
Sleep( 1 0 0 ) ;
end ;
ChessModuleDebugLn( 'Found the login!!!' ) ;
2011-08-27 21:49:44 +00:00
end ;
( *
.. . and we send our username once prompted. Now we read obtained lines scanning for some patterns.
while ( 1 ) {
my $ line = $ telnet- > getline( Timeout = > $ LINE_WAIT_TIMEOUT) ;
next if $ line = ~ / ^ [ \ s\ r\ n] * $ / ;
if ( $ line = ~ / Press return to enter/ ) {
$ telnet- > print( ) ;
last;
}
Normal guest login here. We get Press return to enter suggestion and we do exactly that ( we send empty line) .
if ( $ line = ~ / ( "[^" ] * " is a registered name | \ S+ is already logged in ) / ) {
die "Can not login as $FICS_USER: $1\n" ;
}
Bad luck, we picked the name used by somebody, it is not possible to login as guest with this nick.
print STDERR "Ignored line: $line\n" if $ VERBOSE;
}
Developing- helper note and the end of loop. We get further after last breaks the loop above.
my( $ pre, $ match) = $ telnet- > waitfor(
Match = > "/Starting FICS session as ([a-zA-Z0-9]+)/" ,
Match = > "/\\S+ is already logged in/" ,
Timeout = > $ OPEN_TIMEOUT) ;
if ( $ match = ~ / Starting FICS session as ( [ a- zA- Z0- 9 ] + ) / ) {
$ username = $1 ;
}
else {
die "Can not login as $FICS_USER: $match\n" ;
}
After accepting guest login we may face two things. First, FICS may accept our login and send us a message like Starting FICS session as BotTutorial. This means everything is OK and we can go on. Alternatively, FICS may notice another guest using the same name , in such case it will tell us something like BotTutorial is already logged in and will disconnect.
print STDERR "Successfully logged as guest $username\n" if $ VERBOSE;
}
* )
2011-08-27 20:53:13 +00:00
end ;
2011-08-27 21:16:40 +00:00
function TFICSChessModule. GetSecondPlayerName: string ;
2011-08-27 20:53:13 +00:00
begin
// Result := SecondPlayerName;
end ;
// If a move came, it is because the local player did a move
// so send this move and start listening for a move
2011-08-27 21:16:40 +00:00
procedure TFICSChessModule. HandleOnMove( AFrom, ATo: TPoint) ;
2011-08-27 20:53:13 +00:00
begin
end ;
initialization
2011-08-27 21:16:40 +00:00
RegisterChessModule( TFICSChessModule. Create) ;
2011-08-27 20:53:13 +00:00
end .