{ 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 Classes, SysUtils, Forms, Process, StdCtrls, SorokinRegExpr, chessgame; type moveInCoord = array[1..2] of TPoint; TWinboardConn = class public procedure startEngine(path : String); procedure stopEngine(freeProcess : boolean); destructor Destroy; override; 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; function letterToNumber(num:String) : Integer; function numberToLetter(n : integer) : String; //procedure detectEngine(path : String); end; const nilCoord : moveInCoord = ((X:-1; Y:-1), (X:-1; Y:-1)); var engineProcess : TProcess; outputText : String; vwinboardConn : TWinboardConn; algebraicInput: boolean; engineRegExpression: TRegExpr; operator=(A, B: moveInCoord): Boolean; implementation operator=(A, B: moveInCoord): Boolean; begin Result := (A[1].X = B[1].X) and (A[1].Y = B[1].Y) and (A[2].X = B[2].X) and (A[2].Y = B[2].Y); end; destructor TWinboardConn.Destroy; begin stopEngine(True); 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 engineProcess:= TProcess.Create(nil); engineRegExpression:= TRegExpr.Create; engineProcess.CommandLine := path; engineProcess.Options := engineProcess.Options + [poUsePipes]; engineProcess.Execute; 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)); extraCommands:='easy' + #13 + #10; EngineProcess.Input.Write(extraCommands[1], length(extraCommands)); outputText:=''; end; procedure TWinboardConn.stopEngine(freeProcess : boolean); begin if engineProcess<>nil then if engineProcess.Running then begin engineProcess.Input.WriteAnsiString('quit'+#13+#10); engineProcess.Terminate(0); end; if freeProcess then begin engineProcess.free; end; end; procedure TWinboardConn.tellMove(move : String); begin move := move+#13+#10; EngineProcess.Input.Write(move[1], length(move)); end; //return the engine move. function TWinboardConn.engineMove : moveInCoord; var move : String; points: moveInCoord; begin points := nilCoord; engineRegExpression.Expression:='(?m)^(My move|my move|move)( is|)(: | : | )'; readFromPipe; if engineRegExpression.Exec(outputText) then begin move := extractMove; points := stringToCoord(move); end; result := points; end; function TWinboardConn.coordToString(AFrom, ATo : TPoint; pieceFrom, pieceTo : TChessTile) : String; var move : String; begin move:= numberToLetter(AFrom.X); move:= move + intToStr(AFrom.Y); move:= move + numberToLetter(ATo.X); move:= move + IntToStr(ATo.Y); result:=move; end; function TWinboardConn.numberToLetter(n : integer) : String; 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; function TWinboardConn.stringToCoord(moveStr : String) : moveInCoord; var move : moveInCoord; begin 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]); move[2].Y:=strToInt(moveStr[4]); result:=move; end; //Transform collum letter to number function TWinboardConn.letterToNumber(num:String) : Integer; begin case num[1] of '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; end; end; //read all the output text from the TProcess pipe (basically copy/pasted from the //TProcess example) procedure TWinboardConn.readFromPipe; 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; function TWinboardConn.extractMove : string; var initialPos : integer; begin //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); 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 begin initialPos := pos(engineRegExpression.Match[0],outputText); Delete(outputText,initialPos,Length(engineRegExpression.Match[0])); end; result:= outputText; outputText:=''; end; end.