Inform MSCP 1.2
---------------

This is a relatively simple conversion of Marcel van Kervinck's MSCP chess
playing engine to Inform.  For details on the original MSCP, please visit
http://combinational.com/mscp/.  For your convenience (and mine), the
original MSCP distribution tar file is included here, as mscp-1.2.tgz, but
you don't need it to run Inform MSCP.

MSCP is distributed under the GNU General Public License, and so is Inform
MSCP.   There's a COPYING file that describes licensing terms.  If you want
to include Inform MSCP in an IF game, this means that you may need to make
your game source available under the GPL too; check the licensing carefully.

Because Inform MSCP relies on 32-bit arithmetic which would be too hairy to
write for the Z-machine, it requires Glulx Inform.  All of the code for the
Inform MSCP engine is in the file mscp.inf.  Mscpe.inf is a small example
runnable main program to exercise the engine.


Quick start
-----------

To run a simple MSCP program, compile mscpe.inf.  This includes the main
engine from mscp.inf, and creates a single file mscpe.ulx.  MSCP requires
more static storage that the Glulx Inform compiler 6.21 provides by default.

On Linux, compilation would be something like:

	inform '$$MAX_STATIC_DATA=200000' mscpe.inf

There's a Makefile to do this for you.

Then, run mscpe.ulx with your standard Glulx interpreter.


Interfaces
----------

There are two main interfaces to Inform MSCP: a programming interface, and
an "xboard" interface (named this way because it generates output that will
work with the xboard, and winboard, programs -- see later).  Both interfaces
have a single callable function, and you should be able to do everything you
need to through these.


Xboard interface
----------------

This is a simple line-based interface.  You make an initial call to Inform
MSCP to start the Xboard interface, then pass in strings (in the Inform
sense, where element zero indicates the string length), and Inform MSCP
prints out the response.  The simplest Inform chess-playing code is a loop
like this:

        mscp_xboard ();
        do {
                line = readline (window);
                retcode = mscp_xboard (line);
        } until (retcode == -1);

Here, readline() is a function that returns a string (in Glulx, typically a
call to request line input, then calls to select until a line event).

The line is passed to mscp_xboard(), which interprets it and prints a
response on the current output stream.  The function returns a status value,
true or false, indicating if the call was recognized and handleable, or -1
when asked to quit from the game (that is, line is "quit").

When called with a zero (or missing) argument, or with a zero-length string,
mscp_xboard() initializes MSCP, printing introductory messages and an
initial input prompt.

The mscp_xboard() function can also accept two additional, and optional,
arguments; a busy callback function, and an opaque argument for the callback.
For example:

	retcode = mscp_xboard (line, tick_callback, my_value);

See the section below on the MSCP programming interface for details about
how busy callback functions operate.

The source file mscpe.inf is an example program that creates a simple chess
playing program.  The resulting mscpe.ulx can be played on its own, or,
under Xboard on Linux, with the help of a small shell script (Windows and
Mac users will probably need some equivalent wrapper program):

	#!/bin/bash
	trap "" INT
	cheapglulxe mscpe.ulx

This script is mscpe.sh.  Assuming cheapglulxe is on the $PATH, the command

	xboard -fcp mscpe.sh

should run Xboard using the Inform MSCP engine.  In practice, apart from
testing, there's no huge benefit in this, since it is the same as the
original 'C' MSCP, just slower.


Programming interface
---------------------

The programming interface is aimed much more at a program or game that wants
to integrate Inform MSCP into itself more closely than the xboard interface
allows.  In particular, xboard prints responses to the current output
stream, and this might not be useful.

The full function call for the programming interface is

	mscp_execute (command_code, arg1, arg2, arg3, arg4, arg5, arg6)

but of this, only the command_code is required.

In general, if a command takes a numeric or string argument, it's placed in
arg1; otherwise, arg1 is omitted.  The remaining arguments are, in order

	o The address to buffer output data into.
	o The size of the output buffer, in characters.

	o A buffer callback function, called at the end of each line of
	  buffered output.

	o A busy callback function, called periodically during protracted
	  calculations.
	o An opaque argument, passed on calls to the busy callback function.

This is a fairly unlovely interface, but is in practice less complex than it
appears because some arguments can almost always be omitted.

For example, if the output of a command is not of interest, and it doesn't
take a long time to execute, all except command_code can be left out of the
call.  Or where the output from a command is short, and it takes only a
little time to execute, only command_code and the buffer address and size
need to be passed in the call.

The mscp_execute() function returns true if it executed the command it was
given successfully, false otherwise.

The following command codes are legal values for command_code:


MSCP_EXEC_INITIALIZE:

This initializes Inform MSCP.  If you don't call it explicitly, it's called
automatically on the first real MSCP function, so it's not something you
have to call.  Example usage:

	mscp_execute (MSCP_EXEC_INITIALIZE);


MSCP_EXEC_STATUS:

This is a one-stop shop to get details about the internal state of the chess
engine.  Ordinarily, you can ignore output from all of the other MSCP
functions, but it doesn't make much sense to ignore this one.  Typical usage
is:

	Array	buffer	-> 256;
	...
	mscp_execute (MSCP_EXEC_STATUS, buffer, 256, buf_callback);

This allocates 256 bytes for the status output, and requests that MSCP call
buf_callback(), the buffer callback, for each line of output.

MSCP passes the buffer callback two arguments -- the buffer address, and the
length of data available in it.  Typically, buf_callback() would print out
the data from the buffer.

The buffer callback can indicate it has handled the line by returning true,
in which case MSCP will delete the buffered data and reuse the buffer.  If
it returns false, MSCP will continue adding to the buffer until it's full,
at which point MSCP stops buffering data.

A suitable buffer callback function might look like this:

	[ buf_callback buf length
		i;
		for (i = 0: i < length: i++) {
			if (buf->i == '^')
				print "^";
			else
				print (char) buf->i;
		}
	];

Because the last character in the buffer is always a '^' newline, this can
be shortened to

	[ buf_callback buf length
		i;
		for (i = 0: i < length - 1: i++)
			print (char) buf->i;
		print "^";
	];

For these definitions of buf_callback(), a buffer of 256 bytes is enough.
Because each line is flushed as MSCP produces it, the actual buffer need be
only as long as the longest line of output from the command.

As an alternative, buf_callback() might just add up the count of characters
that MSCP has buffered, and the rest of the program can print them all out
when the call completes.  For example:

	Array	buffer	-> 1024;
	Global	total	= 0;
	...
	[ buf_callback buf length;
		total = total + length;
		return false;
	];
	...
	total = 0;
	mscp_execute (MSCP_EXEC_STATUS, buffer, 1024, buf_callback);
	...
	for (i = 0: i < total: i++) {
		if (buf->i == '^')
			print "^";
		else
			print (char) buf->i;
	}

For some MSCP commands, a buffer of 256 characters may not be enough to hold
all of the output of the command, so this example uses a larger buffer.

Inform MSCP status return strings look like this:

	3 2 w -b 30 e2e4 e4 c7c5 c5
	rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2

There are exactly two lines.  The first line has nine fields, separated by
spaces.  Some are variable length, so the status string requires a little
simple parsing.  The fields are:

	o The current maximum search depth, 0-8 (see later).
	o The current move number, 1 or more.
	o The current colour to play, 'w' or 'b'.
	o Whether MSCP is playing white, black, neither, or both, shown as
	  "-w", "-b", "--", or "wb".
	o The number of moves available to the colour to play next.  A value
	  "0" indicates the game is over.
	o The number of halfmoves since last pawn or capture move.  A value
	  "50" or higher indicates the game should end in a draw.
	o The full algebraic code of the move last played by white, or '-'
	  if none recorded (start of game).
	o The standard algebraic notation for the same move, or '-'.
	o The full algebraic code of the move last played by black, or '-'
	  if none recorded (start of game, or no black move yet).
	o The standard algebraic notation for the same move, or '-'.

The second line is the Forsythe Edwards notation (FEN) for the current board
position.  This has six space separated fields, and is a standard chess
notation.  Fields are:

	o The current position, reading sequentially from board top left to
	  right, and top to bottom.  Black pieces are represented by the
	  letters kqrbnp, white by KQRBNP, consecutive empty squares by a
	  number 1-8, and the end of a board rank by /.
	o The current colour to play, 'w' or 'b'.
	o Castling availability, or '-' if none remaining.
	o En passant target square, or '-' if none.
	o Count of halfmoves since last pawn or capture move.
	o The current move number, 1 or more.

A couple of these repeat data from the first line, but it's handy to have
them all together to form a valid complete FEN string.

Using this call, it should be pretty much possible to ignore buffer output
from all other MSCP commands.


MSCP_EXEC_DISPLAY_BOARD:

This returns an xboard-like representation of the board.  Because the board
state is available from MSCP_EXEC_STATUS, this call isn't likely to be
useful.  But the following should work:

	Array	buffer	-> 256;
	...
	mscp_execute (MSCP_EXEC_DISPLAY_BOARD, buffer, 256, buf_callback);


MSCP_EXEC_LIST_MOVES:

This returns a list of valid moves for the current colour, something like:

	moves are:
	e3
	e4
	...
	22 moves

A suitable call might be:

	Array	buffer	-> 256;
	...
	mscp_execute (MSCP_EXEC_LIST_MOVES, buffer, 256, buf_callback);


MSCP_EXEC_NEW_GAME:

This resets the board to starting positions, and clears any engine buffers.
Typical call:

	mscp_execute (MSCP_NEW_GAME);


MSCP_EXEC_GO:

This asks Inform MSCP to make a move for the current colour.  Because this
may take some time, it may be necessary to give this call a callback
function that MSCP can call back to fairly frequently.  Something like:

	[ tick_callback my_value;
		glk_tick ();
		...do something to keep the display looking active...
		return true;
	];

	...
	mscp_execute (MSCP_EXEC_GO, 0, 0, 0, tick_callback, my_value);

This calls tick_callback() several times each second while Inform MSCP
calculates the best move to make.  When Inform MSCP calls tick_callback(),
it will pass the fifth call argument's value to tick_callback().  This way,
you can pass data through MSCP, and have it supplied to each call of the
tick callback.  If you don't have any need for this, use 0 and forget about
it, or just omit it from the call altogether.

A busy callback would generally return true, but can return false to request
MSCP to return from mscp_execute() as soon as possible, just making the
current best move, ending the move search, or stopping listing game history.
This can be used to to cancel a long-running search, or other lengthy MSCP
operation.

The buffer, buffer size, and buffer callback in the above example are set to
0, indicating that we don't need to know what move MSCP actually made.  We
can find this later with MSCP_EXEC_STATUS.

Finally a word about calling mscp_execute() from within a busy callback:
Don't.  Requesting MSCP_EXEC_STATUS will return intermediate or invalid
engine states since it is in mid calculation.  Calling MSCP_EXEC_GO will
certainly end in tears.  Other MSCP operations will fail in varying, and
unpleasant, ways.


MSCP_EXEC_SEARCH:

Requests a search of best moves at depths up to the maximum set, for the
current colour, without making any actual move.  Useful for getting hints at
a good move for a player.  Likely usage:

	Array	buffer	-> 80;
	...
	mscp_execute (MSCP_EXEC_SEARCH, buffer, 80, buf_callback,
						tick_callback, my_value);

This example call uses both a buffer with callback, to get moves as MSCP
finds them, and a tick callback, because it is compute-intensive.


MSCP_EXEC_SET_DEPTH:

Sets the search depth for use when looking for the best move.  Valid values
are 1 to 8, with higher levels potentially taking several minutes to make a
move.  Example usage, to set depth 4, say:

	mscp_execute (MSCP_EXEC_SET_DEPTH, 4);


MSCP_EXEC_BOTH:

Tells MSCP to play both black and white.  If used, MSCP will play the
current game to conclusion, which might take a long time, and is probably
not what you want to happen anyway, since it's not very interactive.  Use
with:

	mscp_execute (MSCP_EXEC_BOTH);


MSCP_EXEC_WHITE:
MSCP_EXEC_BLACK:

These tell MSCP which colour to play.  The definition of these functions is
somewhat confused by the requirement to match xboard expectations, and
changing them will cause strange behaviours.  Their usage is:

	mscp_execute (MSCP_EXEC_WHITE);
	...
	mscp_execute (MSCP_EXEC_BLACK);

In practice, the xboard interface uses them, but they're probably best
avoided for the programming interface; MSCP_EXEC_GO switches MSCP's play
colour to the current colour to move, so use that instead.


MSCP_EXEC_FORCE:

Tells MSCP to play neither side.  Handy, since otherwise MSCP may try to
make a reply move when handed a player move with MSCP_EXEC_MOVE (see
below).  Use this call for absolute control over MSCP.  Typical usage:

	mscp_execute (MSCP_EXEC_FORCE);


MSCP_EXEC_BOOK:

MSCP has a built in book of common game openings.  For the current position,
this will look up which moves, if any, from the book fit the situation.
It's not wildly useful except for curiousity, and for the xboard interface.
Typical usage:

	Array	buffer	-> 256;
	...
	mscp_execute (MSCP_EXEC_BOOK, buffer, 256, buf_callback);


MSCP_EXEC_UNDO:

Undo the last move.  MSCP's undo stack goes right back to the game start.
Use with:

	mscp_execute (MSCP_EXEC_UNDO);


MSCP_EXEC_RESET:

Reset MSCP's internal tables and history, and reset search depth to its
initial value.  Usage is:

	mscp_execute (MSCP_EXEC_RESET);


MSCP_EXEC_SETUP_FEN:

Set up a board position using Forsythe Edwards notation.  The argument is a
string holding the FEN data:

	Array	fen -> "4k3/4p3/8/8/8/8/4P3/4K3 w - - 0 1"
	...
	mscp_execute (MSCP_EXEC_SETUP_FEN, fen);

This sets up a board for a king-and-pawn exercise.  It returns true if the
FEN string is a valid one, false otherwise.


MSCP_EXEC_HISTORY:

Prints out a history of the complete set of game moves so far.  The list is
printed in PGN-like output, with several moves on each output line.  Typical
usage is:

	Array	buffer	-> 80;
	...
	mscp_execute (MSCP_EXEC_HISTORY, buffer, 80, buf_callback);


MSCP_EXEC_MOVE:

This enters a player move.  The move is a string, in either full algebraic
or standard algebraic notation, for example:

	Array	move	string	"Nc3";
	...
	if (~~mscp_execute (MSCP_EXEC_MOVE, move))
		print "Invalid move!^";
	else
		...

moves a knight to square c3.  If the move is a valid one, the call returns
true, otherwise false.


MSCP_EXEC_ABOUT:

Returns, in its buffer, credits and licensing for MSCP.  Use with:

	Array	buffer	-> 256;
	...
	mscp_execute (MSCP_EXEC_ABOUT, buffer, 256, buf_callback);


Build notes
-----------

The main mscp.inf engine doesn't need infglk.h, but mscpe.inf does.

There are no Glk function calls in mscp.inf, so it's not Glk-specific source
in that sense.  It does however rely on 32-bit word length, so it does in
fact needs Glulx, and this makes it not possible to build for the Z-machine.

There are a couple of features in Inform MSCP that are extra to the original
'C' version, and that you can turn off with selected constant definitions:

	MSCP_NO_AGGRESSIVE - In normal use, 'C' MSCP will clear its tables
	before considering a move.  However, the tables may contain a record
	of a prior search for the same position, so retaining the data makes
	searches faster, at the slightly increased risk of accidentally
	using an incorrect prior search result.  By default, Inform MSCP
	retains the data.  Define this constant to disable aggressive search
	optimization.

	MSCP_NO_TICK_CHECK - Define this constant to disable callbacks while
	calculating moves.  Because checking for callbacks, even if none was
	passed in, takes time, this will speed up searching a little if you
	know that you'll never use them.

	MSCP_NO_UNWIND - Turns off the feature that causes MSCP to return
	the current best move as soon as possible if a busy callback
	function return false.  Again, speeds up processing a little by
	removing runtime tests, if you'll never use them.


Book move tables
----------------

'C' MSCP holds its opening move book in a separate file, and loads it in on
startup.  To do the same in Inform MSCP is difficult and slow.

To save effort in building book tables from a list of moves, this patch
modifies mscp.c to print out its zobrist[] and book move tables in the form
of Inform data structures.  Then, providing that the hash function in Inform
MSCP functions identically to the one in mscp.c, the data dumped by the C
program is directly usable in Inform MSCP.

The output from running 'C' MSCP modified by this diff is embedded in the
file mscp.inf.

1968c1968
<         int i;
---
>         int i, j, k;
1979,1981c1979,1995
<         for (i=0; i<sizeof(zobrist); i++) {
<                 ( (byte*)zobrist )[i] = rnd() & 0xff;
<         }
---
> 	/* for Inform, we need signed hash/random tables */
> 	rnd (); rnd (); rnd ();
> 	for (i=0; i<12; i++) {
> 		for (j=0; j<64; j++) {
> 			zobrist[i][j] = rnd();
>        		}
>        	}
> 
> 	/* dump zobrist */
> 	printf ("Array\t\tmscp_zobrist\t-->");
> 	for (i = 0, k = 0; i < 12; i++) {
> 		for (j = 0; j < 64; j++) {
> 			printf ("%s", k++ % 7 == 0 ? "\n\t" : " ");
> 			printf ("$%08lX", zobrist[i][j]);
> 		}
> 	}
> 	printf (";\n\n");
1994a2009,2023
> 
> 	/* dump book, with count and move packed into one word */
> 	printf ("Constant\tMSCP_C_BOOKSIZE\t\t%ld;\n\n", booksize);
> 	printf ("Array\t\tmscp_book_data_hash\t-->");
> 	for (i = 0; i < booksize; i++) {
> 		printf ("%s", i % 7 == 0 ? "\n\t" : " ");
> 		printf ("$%08lX", book[i].hash);
> 	}
> 	printf (";\n\n");
> 	printf ("Array\t\tmscp_book_data_pack\t-->");
> 	for (i = 0; i < booksize; i++) {
> 		printf ("%s", i % 7 == 0 ? "\n\t" : " ");
> 		printf ("$%08lX", book[i].count << 16 | book[i].move);
> 	}
> 	printf (";\n\n");


--

Simon Baldwin <simon_baldwin@yahoo.com>
10th Sept 2003
