2011-09-07 18:02:31 +00:00
|
|
|
{
|
|
|
|
Unit with the basics to communicate with engines that use the winboard protocol.
|
|
|
|
I used some code of the TProcess example also:
|
|
|
|
https://lazarus-ccr.svn.sourceforge.net/svnroot/lazarus-ccr/examples/process/
|
|
|
|
|
|
|
|
}
|
|
|
|
unit winboardConn;
|
|
|
|
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses
|
2013-11-20 01:28:33 +00:00
|
|
|
Classes, SysUtils, Forms, Process, StdCtrls, SorokinRegExpr, chessgame;
|
2011-09-07 18:02:31 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
moveInCoord = array[1..2] of TPoint;
|
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
TWinboardConn = class
|
2011-09-07 18:02:31 +00:00
|
|
|
public
|
|
|
|
procedure startEngine(path : String);
|
2013-11-20 01:28:33 +00:00
|
|
|
procedure stopEngine(freeProcess : boolean);
|
|
|
|
destructor Destroy; override;
|
2011-09-07 18:02:31 +00:00
|
|
|
procedure tellMove(move : String);
|
|
|
|
function engineMove() : moveInCoord;
|
|
|
|
function coordToString(AFrom, ATo : TPoint; pieceFrom, pieceTo : TChessTile) : String;
|
|
|
|
function stringToCoord(moveStr : String) : moveInCoord;
|
|
|
|
private
|
|
|
|
procedure readFromPipe;
|
|
|
|
function extractMove : string;
|
2011-11-27 16:13:27 +00:00
|
|
|
function letterToNumber(num:String) : Integer;
|
|
|
|
function numberToLetter(n : integer) : String;
|
2013-11-20 01:28:33 +00:00
|
|
|
//procedure detectEngine(path : String);
|
2011-09-07 18:02:31 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
var
|
|
|
|
engineProcess : TProcess;
|
|
|
|
outputText : String;
|
2013-11-20 01:28:33 +00:00
|
|
|
vwinboardConn : TWinboardConn;
|
2011-11-27 16:13:27 +00:00
|
|
|
algebraicInput: boolean;
|
2013-11-20 01:28:33 +00:00
|
|
|
engineRegExpression: TRegExpr;
|
2011-09-07 18:02:31 +00:00
|
|
|
|
|
|
|
implementation
|
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
destructor TWinboardConn.Destroy;
|
2011-09-07 18:02:31 +00:00
|
|
|
begin
|
2013-11-20 01:28:33 +00:00
|
|
|
stopEngine(True);
|
2011-11-27 16:13:27 +00:00
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
Inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TWinboardConn.startEngine(path : String);
|
|
|
|
var extraCommands : String;
|
|
|
|
begin
|
|
|
|
//stop engine if it is running
|
|
|
|
stopEngine(False);
|
|
|
|
|
|
|
|
//create TProcess and start the engine in xboard mode
|
2011-09-07 18:02:31 +00:00
|
|
|
engineProcess:= TProcess.Create(nil);
|
2013-11-20 01:28:33 +00:00
|
|
|
engineRegExpression:= TRegExpr.Create;
|
2011-09-07 18:02:31 +00:00
|
|
|
engineProcess.CommandLine := path;
|
|
|
|
|
|
|
|
engineProcess.Options := engineProcess.Options + [poUsePipes];
|
|
|
|
engineProcess.Execute;
|
2013-11-20 01:28:33 +00:00
|
|
|
extraCommands:='xboard'+#13+#10;
|
|
|
|
EngineProcess.Input.Write(extraCommands[1], length(extraCommands));
|
|
|
|
extraCommands:='level 60 0.5 3'+#13+#10;
|
|
|
|
EngineProcess.Input.Write(extraCommands[1], length(extraCommands));
|
2011-11-27 16:13:27 +00:00
|
|
|
outputText:='';
|
2013-11-20 01:28:33 +00:00
|
|
|
|
2011-09-07 18:02:31 +00:00
|
|
|
end;
|
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
procedure TWinboardConn.stopEngine(freeProcess : boolean);
|
2011-09-07 18:02:31 +00:00
|
|
|
begin
|
2011-11-27 16:13:27 +00:00
|
|
|
if engineProcess<>nil then
|
|
|
|
if engineProcess.Running then
|
2013-11-20 01:28:33 +00:00
|
|
|
begin
|
|
|
|
engineProcess.Input.WriteAnsiString('quit'+#13+#10);
|
2011-11-27 16:13:27 +00:00
|
|
|
engineProcess.Terminate(0);
|
2013-11-20 01:28:33 +00:00
|
|
|
end;
|
|
|
|
if freeProcess then
|
|
|
|
begin
|
|
|
|
engineProcess.free;
|
|
|
|
end;
|
2011-09-07 18:02:31 +00:00
|
|
|
end;
|
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
procedure TWinboardConn.tellMove(move : String);
|
2011-09-07 18:02:31 +00:00
|
|
|
begin
|
|
|
|
move := move+#13+#10;
|
|
|
|
EngineProcess.Input.Write(move[1], length(move));
|
|
|
|
end;
|
|
|
|
|
|
|
|
//return the engine move.
|
2013-11-20 01:28:33 +00:00
|
|
|
function TWinboardConn.engineMove : moveInCoord;
|
2011-09-07 18:02:31 +00:00
|
|
|
var move : String;
|
|
|
|
points: moveInCoord;
|
|
|
|
begin
|
2013-11-20 01:28:33 +00:00
|
|
|
engineRegExpression.Expression:='(?m)^(My move|my move|move)( is|)(: | : | )';
|
|
|
|
readFromPipe;
|
|
|
|
if engineRegExpression.Exec(outputText) then
|
|
|
|
begin
|
|
|
|
move := extractMove;
|
|
|
|
points := stringToCoord(move);
|
|
|
|
result:=points;
|
|
|
|
end;
|
2011-09-07 18:02:31 +00:00
|
|
|
end;
|
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
function TWinboardConn.coordToString(AFrom, ATo : TPoint; pieceFrom, pieceTo : TChessTile) : String;
|
2011-09-07 18:02:31 +00:00
|
|
|
var
|
|
|
|
move : String;
|
|
|
|
begin
|
|
|
|
|
|
|
|
move:= move + numberToLetter(AFrom.X);
|
|
|
|
move:= move + intToStr(AFrom.Y);
|
|
|
|
|
|
|
|
move:= move + numberToLetter(ATo.X);
|
|
|
|
move:= move + IntToStr(ATo.Y);
|
|
|
|
result:=move;
|
|
|
|
|
|
|
|
end;
|
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
function TWinboardConn.numberToLetter(n : integer) : String;
|
2011-09-07 18:02:31 +00:00
|
|
|
begin
|
|
|
|
case n of
|
|
|
|
1 : result := 'a';
|
|
|
|
2 : result := 'b';
|
|
|
|
3 : result := 'c';
|
|
|
|
4 : result := 'd';
|
|
|
|
5 : result := 'e';
|
|
|
|
6 : result := 'f';
|
|
|
|
7 : result := 'g';
|
|
|
|
8 : result := 'h';
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
function TWinboardConn.stringToCoord(moveStr : String) : moveInCoord;
|
2011-09-07 18:02:31 +00:00
|
|
|
var move : moveInCoord;
|
|
|
|
begin
|
2011-11-27 16:13:27 +00:00
|
|
|
if moveStr[1] in ['P','R','N','B','Q','K'] then
|
|
|
|
Delete(moveStr,1,1);
|
|
|
|
move[1].X:=letterToNumber(moveStr[1]);
|
|
|
|
move[1].Y:=StrToInt(moveStr[2]);
|
|
|
|
if moveStr[3] in ['x','-'] then
|
|
|
|
Delete(moveStr,3,1);
|
|
|
|
if moveStr[4] in ['P','R','N','B','Q','K'] then
|
|
|
|
Delete(moveStr,4,1);
|
|
|
|
move[2].X:=letterToNumber(moveStr[3]);
|
2011-09-07 18:02:31 +00:00
|
|
|
move[2].Y:=strToInt(moveStr[4]);
|
|
|
|
result:=move;
|
|
|
|
end;
|
2011-11-27 16:13:27 +00:00
|
|
|
//Transform collum letter to number
|
2013-11-20 01:28:33 +00:00
|
|
|
function TWinboardConn.letterToNumber(num:String) : Integer;
|
2011-09-07 18:02:31 +00:00
|
|
|
begin
|
|
|
|
case num[1] of
|
2011-11-27 16:13:27 +00:00
|
|
|
'a' : result:=1;
|
|
|
|
'b' : result:=2;
|
|
|
|
'c' : result:=3;
|
|
|
|
'd' : result:=4;
|
|
|
|
'e' : result:=5;
|
|
|
|
'f' : result:=6;
|
|
|
|
'g' : result:=7;
|
|
|
|
'h' : result:=8;
|
|
|
|
else result :=0;
|
2011-09-07 18:02:31 +00:00
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
//read all the output text from the TProcess pipe (basically copy/pasted from the
|
|
|
|
//TProcess example)
|
|
|
|
procedure TWinboardConn.readFromPipe;
|
2011-09-07 18:02:31 +00:00
|
|
|
var
|
|
|
|
NoMoreOutput: boolean;
|
|
|
|
|
|
|
|
procedure DoStuffForProcess;
|
|
|
|
var
|
|
|
|
Buffer: string;
|
|
|
|
BytesAvailable: DWord;
|
|
|
|
BytesRead:LongInt;
|
|
|
|
begin
|
|
|
|
if engineProcess.Running then
|
|
|
|
begin
|
|
|
|
BytesAvailable := engineProcess.Output.NumBytesAvailable;
|
|
|
|
BytesRead := 0;
|
|
|
|
while BytesAvailable>0 do
|
|
|
|
begin
|
|
|
|
SetLength(Buffer, BytesAvailable);
|
|
|
|
BytesRead := engineProcess.OutPut.Read(Buffer[1], BytesAvailable);
|
|
|
|
OutputText := OutputText + copy(Buffer,1, BytesRead);
|
|
|
|
BytesAvailable := engineProcess.Output.NumBytesAvailable;
|
|
|
|
NoMoreOutput := false;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
begin
|
|
|
|
if engineProcess.Running then
|
|
|
|
repeat
|
|
|
|
NoMoreOutput := true;
|
|
|
|
DoStuffForProcess;
|
|
|
|
until noMoreOutput;
|
|
|
|
end;
|
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
function TWinboardConn.extractMove : string;
|
|
|
|
var
|
|
|
|
initialPos : integer;
|
2011-09-07 18:02:31 +00:00
|
|
|
begin
|
2013-11-20 01:28:33 +00:00
|
|
|
//delete the text from the start of the engine output
|
|
|
|
engineRegExpression.Expression:='.*(My move|my move|move)( is|)(: | : | )';
|
|
|
|
engineRegExpression.Exec(outputText);
|
|
|
|
initialPos := pos(engineRegExpression.Match[0],outputText);
|
2011-11-27 16:13:27 +00:00
|
|
|
|
2013-11-20 01:28:33 +00:00
|
|
|
Delete(outputText,initialPos,Length(engineRegExpression.Match[0]));
|
|
|
|
|
|
|
|
//if there's text after the engine move, delete it too
|
|
|
|
engineRegExpression.Expression:=' .+';
|
|
|
|
if (engineRegExpression.Exec(outputText)) then
|
2011-11-27 16:13:27 +00:00
|
|
|
begin
|
2013-11-20 01:28:33 +00:00
|
|
|
initialPos := pos(engineRegExpression.Match[0],outputText);
|
|
|
|
Delete(outputText,initialPos,Length(engineRegExpression.Match[0]));
|
2011-11-27 16:13:27 +00:00
|
|
|
end;
|
2013-11-20 01:28:33 +00:00
|
|
|
|
|
|
|
result:= outputText;
|
|
|
|
|
2011-09-07 18:02:31 +00:00
|
|
|
outputText:='';
|
|
|
|
end;
|
|
|
|
|
|
|
|
end.
|