CHESS-DEKONING(6) Games Manual CHESS-DEKONING(6)

NAME

De Koning — A bridge between Chessmaster's engine and external DLLs, enabling automated play and bot development via the XBoard protocol.

METADATA

Platform: Chessmaster
Release: 2002-08-07
Status: Source available

SYNOPSIS

DeKoning.exe

DESCRIPTION

De Koning was a specialized utility designed to act as a bridge between the Chessmaster game engine (specifically The King.exe) and external modules implemented as Windows DLLs.

The primary goal of the project was to facilitate the creation of chess bots or automated players. By technical terms, it implemented a translation layer that converted standard function calls from a “Game DLL” into the XBoard protocol commands understood by the Chessmaster engine.

The communication was handled through anonymous pipes, with De Koning spawning The King.exe as a child process and redirecting its standard input/output. This allowed the external DLL to control the engine’s behavior, set difficulty levels, and receive move calculations in real-time.

The archive contains only the key source files.

TECHNICAL DETAILS

  • Inter-Process Communication: Uses anonymous pipes for stdin/stdout redirection to communicate with The King.exe.
  • Protocol: Implements a subset of the XBoard/WinBoard chess engine protocol.
  • Modularity: Designed to load any DLL that exports Start and MakeMove functions.
  • Engine Control: Automates the initialization sequence of the engine, including memory allocation, search depth, and personality settings.

NOTES

In the summer of 2002, after six months of hacking Ragnarok Online, I was ready for a new challenge.

I turned to chess programming. A longtime Chessmaster fan, I decided to reverse-engineer it and figure out how to control its engine externally.

The first two weeks were spent mapping the program’s structure and hunting for a way to communicate with the engine. I had no idea it used the XBoard protocol, I just assumed it was some proprietary pipe. After a month, I had everything figured out and a working prototype.

The result was De Koning, a utility that let me interface with the engine and build my own bots. Using it, I created separate bots that connected to popular chess servers and dominated games, beating humans and other bots alike.

ATTACHMENTS (Browsing /usr/games/hacks/)

Path: /usr/games/hacks/chessmaster/dekoning/ChessNet Module/export.h5970 bytes
//Functions exported by the Game DLL

#ifndef EXPORT_H
#define EXPORT_H

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include "MyWinsock.h"

#define DllExport   __declspec( dllexport )
//Prototypes
extern "C" {
	DllExport bool Start();
	DllExport void MakeMove( char* move );
} //extern "C"

//Imported functions from ArseMaster
typedef bool (WINAPI *fnNewGame)( int color );
typedef void (WINAPI *fnPlayerMove)( char * move, bool first );
typedef void (WINAPI *fnQuit)();
fnNewGame NewGame;
fnPlayerMove PlayerMove;
fnQuit Quit;

DWORD WINAPI RecvFunc(LPVOID param); //Thread to receive packets
void ParseData( char *data, int len );
void SendData( char *data, int len );

//Global variables
SOCKET s;
HMODULE hModuleDll;
HANDLE RecvThread;

char Account[30];
char Password[30];

#define WHITE 1
#define BLACK 2

bool logged;
bool ingame;
bool firstmove;

char ANewGame[] = {0x19,0x5b,0x31,0x32,0x31,0x20,0x00};
char SendMove[] = {0x25,0x73,0x91,0x01,0x92,0x0a,0x00};
char MyOwnMove[] = {0x19,0x5b,0x32,0x20,0x2a,0x0a,0x00};
char PlrMove[] = {0x19,0x28,0x32,0x34,0x20,0x00};

//---- Exported Functions ----

//This starts the game dll
//It reads the module config file for server address, account name,
//and creates socket for commmunication
DllExport bool Start()
{
	logged = false;
	ingame = false;
	WSADATA info;
	char tmp[150];

	//Retrieve account and password for login
	strcpy(Account, "SoftMind");
	strcpy(Password, "awmy");

	if (WSAStartup(0x101, &info) == SOCKET_ERROR)
	{
		MessageBox(NULL, "Could not initialize socket library.", "Startup", MB_OK);
		return 0;
	}

	//Logins
	s = ConnectServ("chess.net", 4001);
	if (s == INVALID_SOCKET)
	{
		MessageBox(NULL,"Couldn't connect to server.", "Connect", MB_OK);
		return 0;
	}

	send(s, "VERSION 5\n", 10, 0);
	sprintf(tmp, "AUTHENTICATE \"%s\" \"%s\"\n", Account, Password);
	send(s, tmp, strlen(tmp), 0);
	closesocket(s);

	s = ConnectServ("chess.net", 5000);
	if (s == INVALID_SOCKET)
	{
		MessageBox(NULL,"Couldn't connect to server.", "Connect 2", MB_OK);
		return 0;
	}

	DWORD threadID;
	RecvThread = CreateThread(NULL,NULL,RecvFunc,0,0,&threadID);

	send(s, "client=CN254NG\n", 15, 0);
	sprintf(tmp, "client=CN254NG\nlevel2settings=111000000111111000000011110011111111110010100101111111111100011111111\n%s\n%s\nset language English\n", Account, Password);
	send(s , tmp, strlen(tmp), 0);

	return 1;
}

//Sends the appropriate packet for the move we play
DllExport void MakeMove( char* move )
{
	char tmp[15], temp[10];
	char *change = "%s=%c"; //Format to change piece on ChessNet
	char piece;

	if ( strlen( move ) == 5 ) //Changing piece
	{
		piece = move[4]; //Save the piece to change
		piece = toupper( piece ); //Uppercase
		move[4] = '\0'; //Truncate
		sprintf(temp, change, move, piece);
		sprintf(tmp, SendMove, temp);
	}

	else
		sprintf(tmp, SendMove, move); //Normal move

	SendData(tmp, strlen(tmp));	
}

//---- Intern Functions ----

//Thread that receives the datas from server
DWORD WINAPI RecvFunc(LPVOID param)
{
	char buf[1000];
	int bytes;

	while ( bytes = recv(s, buf, sizeof(buf), 0) )
	{
		if ( bytes == SOCKET_ERROR )
			break;

		ParseData(buf, bytes);
		memset(buf, 0, sizeof(buf)); //Zero memory to not conflict for next parsing
	}

	MessageBox(NULL,"Connection closed", "Closed", MB_OK);

	return 0;
}

//Analyzes received datas
void ParseData( char *data, int len )
{
	char buf[1000];
	memcpy(buf, data, len );
	char *start, *str;
	char tmp[40];
	int x, count;
	
	if ( !logged ) {
		if ( strstr(buf, "Password Accepted") ) {
			sprintf(buf, "=gold %s\n=admin %s\n", Account, Account);
			SendData(buf, strlen(buf));
			logged = true;
		}
	}

	else {
		//New game created by someone
		if ( start = strstr(buf, ANewGame) )
		{
			//Check for Blitz or Lightning games
			if ( strstr(start, "{lightning") || strstr(start, "{blitz") )
			{
				for (x = 0, count = 0, str = start; count < 9; x++) {
					if ( str[x] == ' ' )
						count++;
				}

				str += x;

				if ( str[0] == '1' ) //If Rated game
				{	//Decode the game number and accept
					str = strstr(start, "(-6 ");
					str += 4;
					for (x = 0; str[x] != ' '; x++);
					strncpy(tmp, str, x);
					tmp[x] = '\0';
					sprintf(buf, "accept %s\n\0", tmp);
					SendData(buf, strlen(buf));
				}
			}
		}

		//Joined a game
		if ( start = strstr(buf, "You accept ") )
		{
			str = strstr(start, ") vs. ");
			str += 6;
			if ( strncmp(str, Account, strlen(Account)) )
				NewGame( WHITE );
			else
				NewGame( BLACK );

			ingame = true;
			firstmove = false;
		}

		if ( ingame )
		{
			//Game is over
			if ( strstr(buf, "You are now visiting Room ") )
			{
				Quit();
				ingame = false;
			}

			//Other player made a move
			if ( strstr(buf, MyOwnMove) == NULL && (start = strstr(buf, PlrMove)) )
			{
				//third space further
				for (x = 0, count = 0, str = start; count < 3; x++)
					if ( str[x] == ' ' )
						count++;

				str += x;
				for (x = 0; str[x] != ' '; x++); //Count lenght of move

				strncpy(tmp, str, x);
				tmp[x] = '\0';

				if ( x == 5 )
				{
					if ( tmp[4] == 'Q' || tmp[4] == 'R' || tmp[4] == 'N' || tmp[4] == 'B' )
						tmp[4] = tolower( tmp[4] ); //Change into lowercase - ex: a7a8Q into a7a8q

					else
						tmp[4] = '\0'; //It's not a change piece move, but a ate piece one or castling
									   //So let's tell ArseMaster it's just a normal move
				}

				PlayerMove(tmp, !firstmove);		
				
				firstmove = true;
			}

		} //End of if ( ingame )

	}
			

	memset(buf, 0, sizeof(buf)); //Zero memory to not conflict for next parsing

}

//Send packets to connected socket
void SendData( char *data, int len )
{
	char buf[1000];
	memcpy(buf, data, len);

	send(s, buf, len, 0);
}

#endif
Path: /usr/games/hacks/chessmaster/dekoning/ChessNet Module/MyWinsock.h969 bytes
#ifndef MY_WINSOCK_FCTS
#define MY_WINSOCK_FCTS

#include <windows.h>
#include <winsock.h>

//Connect to a server, return socket
SOCKET ConnectServ(const char *servername, unsigned short portnum)
{
	struct sockaddr_in sa;
	struct hostent *hp;
	unsigned long addr;
	SOCKET s;
	
	if ( (addr = inet_addr(servername)) == INADDR_NONE )
		hp = gethostbyname(servername); //a DNS like battle.net
		
	else
		hp = gethostbyaddr((char*)&addr, sizeof(addr), AF_INET); //An IP address	
	
	if ( hp == NULL )
		return INVALID_SOCKET;
		
	memset(&sa, 0, sizeof(sa) );
	memcpy((char*)&sa.sin_addr, hp->h_addr, hp->h_length);
	sa.sin_family = hp->h_addrtype;
	sa.sin_port = htons((u_short)portnum);
	
	s = socket(hp->h_addrtype, SOCK_STREAM, 0);	
	if ( s == INVALID_SOCKET )
		return INVALID_SOCKET;
		
	if ( connect(s, (struct sockaddr *)&sa, sizeof(sa)) == SOCKET_ERROR )
	{
		closesocket(s);
		return INVALID_SOCKET;
	}
	
	return s;
}

#endif
Path: /usr/games/hacks/chessmaster/dekoning/interface.h4437 bytes
// The following functions are exported from Arse Master.exe
// to be used by the Game DLL.

#ifndef ARSEMASTER_INTERFACE
#define ARSEMASTER_INTERFACE

#include "Arse Master.h"
#include "global.h"
#include "intern.h"

#define DllExport   __declspec( dllexport )
//Prototypes
extern "C" {
	DllExport void NewGame( int color );
	DllExport void PlayerMove( char *move, bool first );
	DllExport void Quit();	
} //extern "C"

/** Functions **/

//Use this function to create a new chess game.
//It runs The King.exe and initializes the pipes communication
DllExport void NewGame( int color )
{
	HANDLE hThread = 0;

	GameColor = color; //Put in global variable

	CArseMasterDlg am;
	HANDLE hReadPipe1, hReadPipe2, hWritePipe1, hWritePipe2;
	SECURITY_ATTRIBUTES SecAttr = {0};

	HANDLE hProcess = GetCurrentProcess();

	SecAttr.nLength = sizeof( SECURITY_ATTRIBUTES );
	SecAttr.bInheritHandle = TRUE;
	SecAttr.lpSecurityDescriptor = NULL;

	if ( !CreatePipe(&hReadPipe1,&hWritePipe1,&SecAttr,0) )
	{
		MessageBox(hWnd, "Error for pipes 1", Error, MB_OK | MB_ICONERROR);
		am.OnTerminate();
		return ;
	}

	if ( !DuplicateHandle(hProcess,hReadPipe1,hProcess,&MainRead,0,0,2) )
	{
		MessageBox(hWnd, "Error when duplicating first handle", Error, MB_OK | MB_ICONERROR);
		am.OnTerminate();
		return ;
	}

	CloseHandle(hReadPipe1);

	SecAttr.nLength = sizeof( SECURITY_ATTRIBUTES );
	SecAttr.bInheritHandle = TRUE;
	SecAttr.lpSecurityDescriptor = NULL;

	if ( !CreatePipe(&hReadPipe2,&hWritePipe2,&SecAttr,0) )
	{
		MessageBox(hWnd, "Error for pipes 2", Error, MB_OK | MB_ICONERROR);
		am.OnTerminate();
		return ;
	}

	if ( !DuplicateHandle(hProcess,hWritePipe2,hProcess,&MainWrite,0,0,2) )
	{
		MessageBox(hWnd, "Error when duplicating second handle", Error, MB_OK | MB_ICONERROR);
		am.OnTerminate();
		return ;
	}

	CloseHandle(hWritePipe2);

	PROCESS_INFORMATION pi = {0};
	STARTUPINFO si = {0};
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_USESTDHANDLES;
	si.hStdInput = hReadPipe2;
	si.hStdOutput = hWritePipe1;
	si.hStdError = hWritePipe1;

	if ( !CreateProcess(TheKingPath, "-l- -a- -m-", 0, 0, TRUE, DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, 0, 0, &si, &pi) )
	{
		MessageBox(hWnd, "Couldn't find The King.exe", Error, MB_OK | MB_ICONERROR);
		am.OnTerminate();
		return ;
	}

	CloseHandle(hWritePipe1);
	CloseHandle(hReadPipe2);

	/** Initiate communication **/
	#define LINES 13
	DWORD bytes;
	char init[13][60] = { {"xboard\n"},
			       {"post\n"},
			       {"new\n"},
			       {"level 0 1 0\n"},
			       {"cm_parm default\n"},
			       {"cm_parm opp=100 opn=100 opb=100 opr=100 opq=100\n"},
			       {"cm_parm myp=100 myn=100 myb=100 myr=100 myq=100\n"},
			       {"cm_parm cc=100 mob=100 ks=100 pp=100 pw=100\n"},
			       {"cm_parm cfd=0 sop=100 avd=0 rnd=0 sel=6 md=99\n"},
			       {"cm_parm tts=1048576 ttu=2\n"},
			       {"hard\n"},
			       {"cm_parm myk=877654 opk=298584\n"},
			       {"force\n"} };
			       
	for (int x = 0; x < LINES; x++)
		WriteFile(MainWrite,init[x], strlen(init[x]),&bytes,0);

	if ( color == WHITE ) {
		WriteFile(MainWrite,"force\n",6,&bytes,0);
		RandomFirstMove();
	}

	DWORD threadID;
	hThread = CreateThread(NULL,NULL,ReadPipeFunc,0,0,&threadID);
}

//The Game DLL uses this function to tell ArseMaster what move the
//other player just made.
//Move format: e2e4\0
//Or : a7a8q\0 (Other side reached, change into queen)
DllExport void PlayerMove( char* move, bool first )
{
	char movetmp[10];
	DWORD bytes;
	int len;

	len = strlen( move );	
	if ( len < 4 || len > 5 )
		return; //Invalid format
		
	strcpy(movetmp, move);
	movetmp[len] = '\n';
	WriteFile(MainWrite,movetmp,len+1,&bytes,0);

	char* white = "white\n";
	char* black = "black\n";

	if ( first )
	{
		WriteFile(MainWrite,white,6,&bytes,0);

		if ( GameColor == WHITE )
			WriteFile(MainWrite,white,6,&bytes,0);

		else
			WriteFile(MainWrite,black,6,&bytes,0);

		WriteFile(MainWrite,"go\n",3,&bytes,0);
	}
}

//Use this function to quit the current game (Game with other player is over).
//This will close The King.exe and the pipes handles, until the next NewGame.
DllExport void Quit()
{
	char* quit = "quit\n";
	DWORD bytes;

	WriteFile(MainWrite,quit,strlen(quit),&bytes,0);
	CloseHandle(MainWrite);
	CloseHandle(MainRead);
}

#endif
Path: /usr/games/hacks/chessmaster/dekoning/intern.h1343 bytes
//Functions used by ArseMaster.

#ifndef INTERN_FUNCTIONS
#define INTERN_FUNCTIONS

#include <time.h>
#include <stdlib.h>
#include "global.h"

// Prototypes
DWORD WINAPI ReadPipeFunc(LPVOID param); //Thread to read datas from pipe
void RandomFirstMove(); //Make a random first move when playing white

/** Functions **/

//Thread to read datas from pipe
DWORD WINAPI ReadPipeFunc(LPVOID param)
{
	#define BUF_SIZE 10000
	char tmp[BUF_SIZE];
	DWORD bytes;
	char* result;
	char move[6];
	int len;

	while( ReadFile(MainRead,tmp,BUF_SIZE,&bytes,0) && bytes > 0 )
	{
		tmp[bytes] = '\0';
		result = strstr(tmp, "move ");
		if ( result )
		{
			result += 5;
			for (len = 0; result[len] != (char)0x0d; len++);
			strncpy(move, result, len);
			move[len] = '\0';
			MakeMove( move ); //Game DLL
		}
	}

	return 0;
}

//When creating a new game and we play white.
//Select a random move from a table and tell the Game DLL of it.
void RandomFirstMove()
{
	char moves[5][6] = { {"g1f3\n"},
			     {"c2c4\n"},
			     {"d2d4\n"},
			     {"e2e4\n"},
			     {"b1c3\n"} };
	char move[5];
	DWORD bytes;
			     
	srand( time(0) );
	int nb = rand() % 5;
	
	WriteFile(MainWrite,moves[nb],5,&bytes,0);
	strncpy(move, moves[nb], 5);
	move[4] = '\0';
	MakeMove( move ); //Tell Game Dll	
}

#endif
Path: /usr/games/hacks/chessmaster/dekoning/global.h555 bytes
#ifndef GLOBAL_H
#define GLOBAL_H

HWND hWnd; //Handle to main window

//GameDll exported functions
HINSTANCE GameDll; //For LoadLibrary
typedef bool (WINAPI *fnStart)();
typedef void (WINAPI *fnMakeMove)( char* move );
fnStart Start;			//Pointer to Start function
fnMakeMove MakeMove;		//Pointer to MakeMove function

//Handles for pipes
HANDLE MainRead;
HANDLE MainWrite;

#define WHITE 1
#define BLACK 2

char* Error = "Error";
char path[300];
char TheKingPath[300]; //The directory that The King is in!

int GameColor;

#endif

TECHNOLOGIES

  • C++
  • Inter-Process Communication
  • Bridge
  • Reverse Engineering
int03h.com circa 2002 CHESS-DEKONING(6)