Because of
a few limitations of Windows CE, it was not really possible to incorporate
engines in CEBoard as it is done on desktops with Winboard and UCI protocols. It order to have a decent
control of the engine, I chose to put engines in DLL and use COM like
interfaces for communications between CEBoard and the
engines : the Engine and CEBoard
will run on different threads in the same running process.
Anyway, in
order to simplify the model further, it is not a real COM relationship between CEBoard and engines :
·
CEBoard gets the interface implemented by the engine
through a function exported by the engine DLL, there is no registry stuff
involved (nor class factories),
·
CEBoard passes its own interface to the same function, the engine can then store the pointer and reuse it
late.
·
The
interfaces do not inherit from IUnknown an there is
no reference counting.
Here is the
DLL exported function:
interface IEngine;
typedef IEngine
*LPENGINE;
interface ICEBoard20;
typedef LPENGINE (*LoadEngineProc)(ICEBoard20
*pCEBoard);
#define EngineProcName L"LoadEngine"
What does that mean ?
The DLL
must export a function named “LoadEngine”, which
takes a pointer to an ICEBoard20 interface (described later) and returns a
pointer to a IEngine interface.
How and when CEBoard
loads a DLL and calls this function ?
At
initialization, CEBoard looks in its Engines folder
and enumerate all its subfolders ; in each of these subfolders, CEBoards checks if a DLL with the same name than the subfolder
exists; is so, it considers it as en engine.
When the
user starts an engine, CEBoard loads the
corresponding DLL, tries to locate the LoadEngine
function and calls it, passing it a pointer to an interface implemented by an
internal CEBoard’s object, the Engine Driver. LoadEngine creates the engine thread, stores the pointer
passed by CEBoard and returns an
pointer to its implementation of the IEngine
interface.
Note : in
almost all interface functions, the strings are passed as Unicode as it is the
standard of Windows CE.
IEngine interface
Now it is
time to have a closer look at the interface that the engine must implement as
well as the interface implemented by CEBoard.
#define ENGINE_INTERFACE_VERSION 2
#define MOVES_UCI 1
typedef struct tagEngineProperties {
DWORD dwSize; //
Structure size
DWORD dwVersion; // Version
of the interface
DWORD dwCaps;
TCHAR sEngineName[32];
} EngineProperties;
interface IEngine
{
BOOL GetProperties(EngineProperties *pProps) = 0;
virtual void StartGame(BOOL bPlayBlack, BOOL bAnalyze,
LPCTSTR sFen) = 0;
virtual void EndGame() = 0;
virtual void Release() = 0;
virtual void SendMove(LPCTSTR sMove) = 0;
virtual BOOL ShowOptions(HWND hWnd) = 0;
virtual HMENU GetMenu() = 0;
virtual BOOL HandleCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) = 0;
virtual void SetPosition(LPCTSTR sFEN) = 0;
virtual void MoveNow() = 0;
virtual BOOL IsAlive() = 0;
virtual void GetMessages(BOOL bWantMessages) = 0;
};
GetProperties
Called by CEBoard immediately after LoadEngine. CEBoard’s fills the dwSize
field of the structure, whiwh allows the engine to
know which fields to fill.
sEngineName = returns the name of the Engine.
MOVES_UCI =
put this value if the engines uses UCI’s move format
(ie coordinates, like e2e4 or e7e8Q), clear this
value if the engine uses algebraic notation (e4, e8=Q)
StartGame
Called by CEBoard when the user starts a new
game or a new position.
EndGame
Called by CEBoard when the user closes the
game window.
Release
Called by CEBoard when the user closes CEBoard.
SendMove
Called by CEBoard when the user has entered a
move. The
notation is either coordinates (UCI mode) or algebraic.
ShowOptions
Called by CEBoard to allow the user to change
options (for example level of play) of the engine.
GetMenu
Called by CEBoard to get an handle to a
popup menu to display when the user clicks on the Engine button. It allow the engine to offer its own commands.
HandleCommand
Called by CEBoard when the user has selected
an option in the engine’s provided menu. Allow the engine to handle the commands it
offers.
SetPosition
Called by CEBoard when the user has jumped to
a past position in the game.
MoveNow
Force the
engine to move : if it was the engine’s turn to play,
it must stop thinking and play immediately. If it was the player’s turn, the
engine has to switch colors and start thinking.
IsAlive
Called
periodically by CEBoard to check it the engine is
still running. Implementation: for example, simply check that the engine’s
thread is still running.
GetMessages
Called by CEBoard to indicate that the user wants to display engine’s
feedback such as the best move so far, the evaluation score,
…
ICEBoard20 interface
A pointer
to a ICEBoard20 interface is passed to the engine via
the LoadEngine function. This interface allows the
engine to interact with CEBoard. Here is the
definition of the interface :
interface ICEBoard20 {
// Méthodes de la version 1
virtual void EngineMove(LPCTSTR sMove) = 0;
virtual void FatalError(LPCTSTR sError) = 0;
virtual void GameFinished(LPCTSTR sResult, LPCTSTR sComment) = 0;
virtual void WhoPlays(LPCTSTR sPlayer) = 0;
virtual void SwitchSides() = 0;
virtual void ShowThinking(BOOL bShow) = 0;
virtual void SetTimeControl(int nSecByMove, int nMoves1, int nSec1,
int nMoves2, int nSec2, int nSec3,
int nInc) = 0;
virtual void EngineFeedback(int depth, int nodesSec, int totalNodes,
int nTime, LPCSTR sEval,
LPCSTR sVariante)
= 0;
virtual void GetTimeInfo(int *pwtime, int
*pbtime, int *pmovestogo) = 0;
};
EngineMove
Called by the engine to play a move. Move format is PGN like.
FatalError
Called by the engine when an unrecoverable error is encountered.
GameFinished
Called by
the engine when it detects that the game is finished :
·
sResult = PGN like format, i.e. “1-
·
sComment = detailed explanation, for example “Threefold
repetition”
WhoPlays
Called by the engine to indicate the name to store in PGN file. Used for example by Crafty SE to
indicate which personality has been selected.
SwitchSides
Called by the engine to tell CEBoard that the
engine now plays the other color.
ShowThinking
Called by the engine to tell CEBoard to
display the EngineFeedback, or to hide it.
SetTimeControl
Called by the engine to tell CEBoard the
user’s selected time control.
EngineFeedback
Called by the engine to indicate the current state of its thinking. Beware :
the strings here are in ANSI instead than Unicode !
GetTimeInfo
Called by
the engine to get the time available until next time control (to be used when
not in fixed time mode)