Check out Janggi (Korean Chess), our featured variant for November, 2024.

GAME Code Tutorial

This is a tutorial on how to program Game Courier to enforce the rules of a game. Game Courier enforces the rules of a game with a program it constructs out of code written in the GAME Code language. The first thing you need to know is how to evaluate the legality of a move. After you understand this, you will better understand what kind of code is needed before and after a game.

Testing the Legality of Piece Moves in Chess

The best place to evaluate the legality of a move is right after it is made. You can enter code for this in the Post-Move fields. There will be one for each player, so that you can write code specific to each player. Let's start with the information you can access after a move is made. When a move is made, four preprocessor variables get set. These are origin, dest, moved, and old. When any of these appear, it gets replaced with another value:

origin
Gets replaced with the cooordinate of the space the last piece to move moved from.
dest
Gets replaced with the coordinate of the space the last piece to move has moved to.
moved
Gets replaced with the label used for the last piece to move.
old
Gets replaced with the label used for the last piece captured. If the last move was not a capture, old will be replaced by @, which is the internal label for an empty space.

In evaluating the legality of a move, you normally want to check whether the piece that moved (moved) can legally move from its origin (origin) to its destination (dest). There are built-in functions for handing certain types of moves common in Chess variants. These are the two most commonly used for Chess:

checkleap from to distance distance
Checks the legality of a leap, which is a direct, unblockable move from one space to another. In Chess, the King, Knight, and Pawn move by leaps. This function takes four arguments. The first two are the origin and destination of a move, and the last two specify a distance in ranks and files. This will check whether the move was actually the specified number of ranks and files away. For this function, ranks and files are interchangeable, and it checks symmetrically in all directions. Negative values for the last two arguments have no special meaning, because only their absolute values are used. For example, checkleap b8 c6 1 2 would check whether a move from b8 to c6 was a legitimate Knight move. This would return true for any legal Knight move.
checkride from to distance distance
Checks the legality of a riding move, which consists of one or more leaps in the same direction, passing over empty spaces at least until the destination is reached. In Chess, Bishops, Rooks, and Queens are all riding pieces. This function takes four arguments. The first two are the origin and destination of the move. The last two specify the number of ranks and files of the basic leap. This operation checks symmetrically in all directions. Negative values for the last two arguments have no special meaning, because only their absolute values are used. For example, checkride d1 g4 0 1 would check whether the move from d1 to g4 was a Rook move, and checkride d1 g4 1 1 would check whether the same move was a Bishop move

When checking the legality of a move, you also need to perform an appropriate action. Usually, you want to stop the program with an error message if the move was illegal or let it continue if it was legal. You can use the if control structure to specify an action to perform, and the action of ending the program with an error message is done with the die command. Aside from these few things, you need to know that the if control structure can use logical operators, such as and, or, and not to evalute complex expressions, and it follows the grammar of Polish notation when evaluating expressions. This means that it reads the operator first, as in "+ 5 6" instead of "5 + 6".

With this in mind, we could write some code for Chess, which looks like this:

if and == moved K not or checkleap origin dest 1 0 checkleap origin dest 1 1:
	die The King cannot move from origin to dest;
elseif and == moved B not checkride origin dest 1 1:
	die The Bishop cannot move from origin to dest;
elseif and == moved R not or checkride origin dest 1 1:
	die The Rook cannot move from origin to dest;
elseif and == moved Q not or checkride origin dest 1 0 checkride origin dest 1 1:
	die The Queen cannot move from origin to dest;
elseif and == moved N not checkleap origin dest 1 2:
	die The Knight cannot move from origin to dest;
endif;

This is a good start at enforcing the rules of Chess, but it doesn't cover Pawns. The Pawn is a divergent piece. It moves one way and captures another. Therefore, code for a Pawn move must take into account whether the move was a capture. You can do this by checking == moved @ or by using the capture operator. Also, the Pawn does not move symmetrically. It can move forward but not sideways or backward. So we cannot use checkleap for the Pawn. Instead, we need the checkaleap function, which checks the legality of a leap in a single direction.

checkaleap
Checks the legality of a leap in one specific direction. The first two arguments are for the origin and destination of the move, and the second two are for a distance away in files and ranks. The third is for files, and the fourth is for ranks. An easy way to remember this order is that the file appears first in a coordinate. For files, positive values are to the right, and negative values are to the left. For ranks, positive values are forward, and negative values are backward. No matter who is moving, this is from White's perspective. Positive values advance coordinates numerically or alphabetically, and negative values decrease coordinates numerically or alphabetically. For example, or checkaleap 2a 3c -1 2 checkaleap 2a 3c 1 2 would check whether the move from 2a to 3c was one a White Shogi Knight could make.

With this in mind, we could write code for the Pawn like this:

if == moved P:
	if and capture and not checkaleap origin dest 1 1 not checkaleap origin dest -1 1:
		die A Pawn cannot make a capturing move from origin to dest;
	elseif and not capture not checkleap origin dest 0 1:
		die A Pawn cannot make a non-capturing move from origin to dest;
	endif;
endif;

Because the Pawn moves forward, different code is needed for the Black Pawn. It might look like this:

if == moved p:
	if and capture and not checkaleap origin dest 1 -1 not checkaleap origin dest -1 -1:
		die A Pawn cannot make a capturing move from origin to dest;
	elseif and not capture not checkleap origin dest 0 -1:
		die A Pawn cannot make a non-capturing move from origin to dest;
	endif;
endif;

This code differs in two ways. I changed P to p, because uppercase labels are used for White pieces, and lowercase labels are used for Black pieces. I changed the last argument to checkaleap from 1 to -1 to account for movement in the opposite direction. But the Pawn's movement is actually more complex than this. On its first move, a Pawn gets to move two spaces straight forward, and a Pawn may capture another Pawn that does this by en passant if it lands next to it. Let's first look at how to move the Pawn two spaces. checkaleap origin dest 0 2 will not do, because this is a direct leap, and a Pawn's double move may not pass over an empty space. Just as checkleap is paired with checkaleap, checkride is paired with checkaride. This works just like checkaleap except it checks a riding move. However, checkaride origin dest 0 1 also won't work, because it doesn't check the distance of the move. What will work is using them together, as in and checkaleap origin dest 0 2 checkaride origin dest 0 1. This checks that the move is exactly two spaces forward, using checkaleap, and that it has passed over an empty space, using checkaride.

When a Pawn makes a two-space move, there is the possibility that another Pawn may capture it by en passant on the next move. So, when a Pawn makes a double move, this must be noted with a variable. A variable may be set with the set command:

set [many] variable arguments
Sets a program variable.
many
Set many variables. Each even argument is a variable name, and each following argument is its value. This shortcut for setting many variables cannot be used for anything but simple values. It will not evaluate complex expressions.
anything else
Without the many keyword, a single variable gets set to the value of the expression following it. This expression gets evaluated by a Polish notation calculator, and the result is placed in the variable. It includes math, logic, board information operations, and user defined functions.

I normally keep track of possible en passant captures with a variable called ep. When a Pawn makes a double move, I set this to the space it just moved to, and when any other move is made, I set ep to false. So that it is false at the beginning of the game, I also set it to false in the Pre-Game code. Here is how it may used in code for checking the legality of a Pawn move:

if == moved P:
	if capture:
		if and not checkaleap origin dest 1 1 not checkaleap origin dest -1 1:
			die A Pawn cannot make a capturing move from origin to dest;
		endif;
		set ep false;
	elseif checkaleap origin dest 0 1:
		set ep false;
	elseif and checkaleap origin dest 0 2 checkaride origin dest 0 1:
		set ep dest;
	else:
		die A Pawn cannot make a non-capturing move from origin to dest;
	endif;
endif;

Note that I split up some expressions into nested if-statements. This is to avoid checking for the same thing multiple times. After if capture:, each elseif-statement can assume a non-capturing move has been made. In general, each elseif-statement can assume that conditions appearing in previous if or elseif statements at the same level are false. The else-statement at the end assumes that all previous tested conditions are false. Although the code now keeps track of en passant, it still doesn't handle en passant. This adds some code to the previous example for handling en passant:

if == moved P:
	if capture:
		if and not checkaleap origin dest 1 1 not checkaleap origin dest -1 1:
			die A Pawn cannot make a capturing move from origin to dest;
		endif;
		set ep false;
	elseif checkaleap origin dest 0 1:
		set ep false;
	elseif and checkaleap origin dest 0 2 checkaride origin dest 0 1:
		set ep dest;
	elseif and checkaleap dest ep 0 1 or checkaleap origin dest 1 1 checkaleap origin dest -1 1:
		empty #ep;
		set ep false;
	else:
		die A Pawn cannot make a non-capturing move from origin to dest;
	endif;
endif;

Although en passant is a capturing move, the code for this appears outside the condition that the piece has been captured. This is because in en passant, the Pawn moves to an empty space, and the Pawn behind it gets removed from the board. Since the move is to an empty space, it is initially considered a non-capturing move. To test whether it is a legal en passant move, it uses checkaleap thrice, once to test whether the move takes it in front of the Pawn that moved by en passant, and two more times to test whether it moved one space diagonally forward.

There is still more to a Pawn's move. It may promote upon reaching the last rank. This will normally be done by changing the piece on the location the Pawn has moved to, such as P e7-e8; Q-e8. What the code for checking a Pawn move's legality must do is check whether a promotion is legal. To do this, we need to compare the identity of the piece moved with the identity of the piece on the destination square. We know that the moved variable gets replaced with the identity of the piece moved. To find the identity of the piece on a particular space, use the space operator. When followed by a coordinate, this returns what is at that coordinate on the board. We also need to compare this value with the set of pieces it is legal to promote to. This could be done with a series of == conditions, but the match operator will be more efficient. And we need to know what rank the Pawn is on, so that we know when promotion is legal. The rank operator returns the internal value used for the rank of the coordinate it is given. This will always be an integer, the first rank being 0 and the last rank in Chess being 7. An alternative is the rankname operator, which returns the rank identifier used in the coordinate name. For Chess, ranks are numbered from 1 to 8. Besides checking the legality of a promotion, it must also require promotion on the last rank. The following adds some code to the previous example to prevent illegal promotion and to force promotion on the last rank:

if == moved P:
	if capture:
		if and not checkaleap origin dest 1 1 not checkaleap origin dest -1 1:
			die A Pawn cannot make a capturing move from origin to dest;
		endif;
		set ep false;
	elseif checkaleap origin dest 0 1:
		set ep false;
	elseif and checkaleap origin dest 0 2 checkaride origin dest 0 1:
		set ep dest;
	elseif and checkaleap dest ep 0 1 or checkaleap origin dest 1 1 checkaleap origin dest -1 1:
		empty #ep;
		set ep false;
	else:
		die A Pawn cannot make a non-capturing move from origin to dest;
	endif;
	if != space dest moved:
		if or != rankname 8 not match space dest Q R B N:
			die Illegal Pawn promotion;
		endif;
	elseif == rankname 8:
		die Pawn promotion is required on the last rank;
	endif;
endif;

We can make this more user-friendly by replacing the last die command with the askpromote command. This command will ask what the player wants to promote to and amend the previous move to include the promotion.

if == moved P:
	if capture:
		if and not checkaleap origin dest 1 1 not checkaleap origin dest -1 1:
			die A Pawn cannot make a capturing move from origin to dest;
		endif;
		set ep false;
	elseif checkaleap origin dest 0 1:
		set ep false;
	elseif and checkaleap origin dest 0 2 checkaride origin dest 0 1:
		set ep dest;
	elseif and checkaleap dest ep 0 1 or checkaleap origin dest 1 1 checkaleap origin dest -1 1:
		empty #ep;
		set ep false;
	else:
		die A Pawn cannot make a non-capturing move from origin to dest;
	endif;
	if != space dest moved:
		if or != rankname 8 not match space dest Q R B N:
			die Illegal Pawn promotion;
		endif;
	elseif == rankname 8:
		askpromote Q R B N;
	endif;
endif;

One last thing to check for in Chess is whether a castling move is legal. But this involves checking whether the King is in check or is passing over a checked space. So we first need to look into how to check whether a King is in check. Before we do this, it will help to put the code used for checking the legality of piece moves into functions. This will let us create a subroutine that checks for check by calling functions for legal piece moves. (Note that function and subroutine are not synonymous in GAME Code. A function is a one-line expression with placeholders for arguments, and a subroutine is a body of code that the program can divert execution to.) We can define functions with the def command:

def name expression
The def command lets you create a one-line function that can be accessed with the fn operator of the Polish notation calculator. The first argument is the name of the function, and the remaining arguments define the function. The function's name should not match any built-in function name used by the Polish notation calculator. Otherwise, you will get unwanted results. The function should be defined as a Polish notation expression. It may also include placeholders for arguments. Each placeholder should begin with the number sign and be followed only by digits, such as #0, #2, etc. The number following the number sign indicates which argument it is a placeholder for. #0 is a placeholder for the first argument, #1 for the second argument, etc. The highest numbered placeholder should not exceed the number of different placeholders that you use. So, for example, if you use #4 as a placeholder, you should also include #3, #2, #1, and #0.

Here is how code for checking the legality of piece moves in Chess can be defined as functions:

def K or checkleap #0 #1 1 0 checkleap #0 #1 1 1;
def B checkride #0 #1 1 1;
def R checkride #0 #1 1 0;0
def Q or checkride #0 #1 1 0 checkride #0 #1 1 1;
def N checkleap #0 #1 1 2;

Note that origin and dest have been replaced with #0 and #1. These are placeholders for arguments. When you call a function, you will precede it with fn to indicate that it is a function, and you will follow it with values for its arguments. The first argument following the function name will give the value for #0, the second for #1, and so on. The placeholders in function definitions are numbered, starting with 0 and going up to 1 less than the total number of arguments. There should be no gaps. Don't use a number unless you use every non-negative integer below it.

With functions like this, this code can be used to check the legality of a move:

if fn moved origin dest:
	die A moved cannot legally move from origin to dest;
endif;

Here moved gets replaced by the piece label, which is also the name of the function for testing the legality of that piece's move. In general, you can use the values of variables or even of expressions to determine which function to call. More on this later. Note that the function name is preceded by the fn operator. This indicates that what follows it is a function. The code for the Pawn move is more complicated, and it handles en passant and promotion. So a function should not be used to check the legality of a Pawn move. Instead of using a function, we can use a subroutine, such as this:

sub P from to:
	if capture:
		if and not checkaleap #from #to 1 1 not checkaleap #from #to -1 1:
			die A Pawn cannot make a capturing move from #from to #to;
		endif;
		set ep false;
	elseif checkaleap #from #to 0 1:
		set ep false;
	elseif and checkaleap #from #to 0 2 checkaride #from #to 0 1:
		set ep #to;
	elseif and checkaleap #to ep 0 1 or checkaleap #from #to 1 1 checkaleap #from #to -1 1:
		empty #ep;
		set ep false;
	else:
		die A Pawn cannot make a non-capturing move from #from to #to;
	endif;
	if != space #to moved:
		if or != rankname 8 not match space #to Q R B N:
			die Illegal Pawn promotion;
		endif;
	elseif == rankname 8:
		askpromote Q R B N;
	endif;
	return true;
endsub;

This uses the code from before, enclosing it with sub and endsub, except that I have replaced origin and dest with the parameters from and to. The parameters are defined as part of the subroutine, and values for them get passed as arguments when the subroutine is called. It also gets rid of the initial if-statement, because this subroutine will be used only when it is already known that the piece moving is a Pawn. It returns true if the move passes all tests of legality.

Although we won't use a function for testing the legality of a Pawn move, it is useful to have a Pawn function for use with the subroutine that will tell whether the King is in check. This function can be used for the purpose of telling whether a Pawn is checking a King:

def P or checkaleap #0 #1 1 1 checkaleap #0 #1 1 -1;

One more thing we can do is optimize our functions and subroutines. The standard forms of and and or take two arguments. When both arguments are expressions, they will evaluate both expressions before returning a value. This is inefficient, because an and-statement is false if either of its arguments is false, and an or-statement is true if either of its arguments is true. To take advantage of this, these two logical operators also come in break-away forms. When and or or appears in an expression with a single argument, it will exit the expression early when that value is enough to determine the value of the whole logical expression. When and receives a single false value, it will exit any expression it is in, returning a value of false for the whole expression. For example, == 9 9 and != 9 9 would return false without evaluating == 9 9, but != 9 9 and == 9 9 would evaluate both equations and return the value of the first one. Note that Polish notation evaluates the entire expression backwards, moving from the end to the beginning. When or receives a single true value, it will exit any expression it is in, returning a value of true. For example, == 9 8 or == 8 8 will return true without evaluating == 9 8, but == 8 8 or == 9 8 will evaluate both equations, returning the value of == 8 8. With this in mind, the following functions will be changed to these:

def K checkleap #0 #1 1 0 or checkleap #0 #1 1 1;
def Q checkride #0 #1 1 0 or checkride #0 #1 1 1;
def P checkaleap #0 #1 1 1 or checkaleap #0 #1 1 -1;

Let's also optimize the P subroutine. We can reduce the use of checkaleap by immediately screening out any moves that are not forward. Here's a modified version of the P subroutine.

sub P from to:
	if <= rank #to rank #from:
		die A Pawn may only move forward. The move from #from to #to is not forward;
	elseif capture:
		if not checkleap #from #to 1 1:
			die The Pawn on #from cannot make a capturing move to #to;
		endif;
		set ep false;
	elseif checkaleap #from #to 0 1:
		set ep false;
	elseif checkaride #from #to 0 1 and checkaleap #from #to 0 2:
		set ep #to;
	elseif checkaleap #to ep 0 1 and checkleap #from #to 1 1:
		empty #ep;
		set ep false;
	else:
		die A Pawn cannot make a non-capturing move from #from to #to;
	endif;
	if != space #to moved:
		if or != rankname 8 not match space #to Q R B N:
			die Illegal Pawn promotion;
		endif;
	elseif == rankname 8:
		askpromote Q R B N;
	endif;
	return true;
endsub;

Now that we have functions for testing the legality of each piece move, we can write a subroutine that tests whether a King is in check. It may look like this:

sub checked king:
	my from piece;
	if isupper #king:
		def enemies onlylower;
	else:
		def enemies onlyupper;
	endif;
	for (from piece) fn enemies:
		if fn #piece #from #king:
			return #from;
		endif;
	next;
	return false;
endsub;

This subroutine has one parameter, the location of the king. When it is used in the subroutine, it is prepended with #, which indicates that it should be treated as a preprocessing variable. Before running a line, it will replace #king with the value of king. This subroutine uses two other variables. These are from and piece. Both are defined with my scope, which means that they are exclusive to a single instance of the checked subroutine. First, this subroutine creates a function for listing the enemy pieces. The onlylower function returns a list of Black pieces on the board, and onlyupper returns a list of White pieces on the board. Remember that piece color is represented by difference in case. It checks the color of the player by checking the case of the label at the King's position. It then goes through the enemies with a for-next loop. For each enemy piece, it checks whether it can legally move to the King's space. If it can, the King is in check, and it returns the location of the piece checking the King. This will serve as a true value even if the location itself doesn't get used. If no enemy piece can move to the King's space, it returns false.

This comes in handy not only for telling whether a castling move is legal but for telling whether any move leaves the King in check. For this to work, we need to keep track of the King's location. I normally do this with two variables called k and K. The lowercase k is for the Black King, and the uppercase K is for the White king. Note that variable names and function names can overlap, because variables and functions use separate name spaces. That's why k and K can be used both as variable names and as function names. Before the game begins, I set these with the findpiece built-in function.

findpiece pattern [first|last] C1 ... Cn
Searches for the first or last piece in the given list of coordinates that matches the given wildcard pattern. The coordinates may be given as a list or as an array. Returns coordinate of found piece or false if piece is not found. If the first or last keyword is not specified, the default behavior is to find the first piece.

This is just in case someone composes a fairy chess problem with either King in a different position. It also makes it easier to reuse the same code for different games. That code looks like this:

set k findpiece k spaces;
set K findpiece K spaces;

For these, I have told it to look for the King among a set of spaces specified by an array. This array is provided by a built-in function. The spaces function returns a numerically indexed array of every space on the board.

Besides checking whether a King is moving from, through, or into check, a castling subroutine has to check whether the King or Rook have already moved. I normally do this with flags. A flag is a global boolean variable that uses a different name space than the regular variables. Thus, it is possible for a flag and a variable to have the same name. Flags are set with the setflag command, and they are unset with the unsetflag command. Before the game begins, I set flags named after the coordinates of the spaces that Kings and Rooks begin on, like so:

setflag a1 e1 h1 a8 e8 h8;

Whenever a Rook or King moves, I unset the flag corresponding to its space. If this is a flag I didn't previously set, unsetting it changes nothing. But that doesn't matter. It is just simpler to unset the flag for any space a Rook or King moves from. Just in case a piece captures an unmoved Rook, it is also important to unset the flag for the location the piece has just moved to. It is simple enough to include this code after the legality of a move has been confirmed:

unsetflag origin dest;

Now that we have a way to check for check and to check whether the King or Rook has moved, we can write a castling subroutine. The first thing it should do is get the direction of movement. This will be used to find the piece to castle with, and it will reduce the code needed for checking the move's legality. We do it by taking the sign of the difference between the King's file and the destination file. Files are represented in the notation as letters, but they are represented internally as integers. The file function returns the internal integer value of a coordinate's file.

set xdir sign minus file dest file origin;

This just subtracts the origin file from the destination file and returns the sign of the difference. It will return -1 for movement toward the Queen side and 1 for movement toward the King side. We can now use this value in code that checks the legality of the move. We will also be checking whether the King has moved and whether the move is to an occupied space.

if not checkaleap origin dest * #xdir 2 0:
	die A King may not move from origin to dest;
elseif not flag origin:
	die A King may not castle after it has already moved.;
elseif capture:
	die A King may not castle to an occupied space.;
elseif not empty where #xdir 0:
	die A King may not pass over an occupied space when castling.;
endif;

Next, we will look for the first piece to be found in the direction of the King's movement. Since all spaces between the King and Rook must be empty, this is the only piece that the King can castle with in its direction of movement. Along the way, we will check for a flagged space and the end of the board. If we come to either of these before finding a piece, the King will not be able to castle. We are going to do this with a do-loop. Together, do and loop create an infinite loop. This can be broken out of by giving either do or loop a while or until condition, but we will be breaking out of this loop with the break command. Before the loop, we set the variable c to the space the King is moving to. Inside the loop, we set c to the next space after it in the direction of the King's movement. If it's flagged and occupied, we're done and break out of the loop. Otherwise, if it is off the board, flagged or occupied, the King can't castle, and it exits with an error message.

set c dest;
do:
	set c where #c #xdir 0;
	if not empty #c and flag #c:
		break;
	elseif flag #c:
		die The King cannot castle with a missing piece;
	elseif not onboard #c:
		die No piece was found to castle with.;
	elseif not empty #c:
		die The King cannot castle with the piece at #c;
	endif;
loop;

We will finish by storing the value of c to RPOS, which is the variable for the Rook's position.

set RPOS #c;

Now we get to the more expensive part of the castling routine, checking for checks. So that we can accurately test whether the King is moving from check, we must first undo the King move.

move dest origin;

Then we can test whether the King was in check before moving:

if sub checked origin:
	die A King may not castle out of check.;
endif;

To test whether the King is moving through Check, we must move the King one space at a time, testing each space of its move. We will do this with a for loop. Before the loop, we use store to store the current board configuration. After each move of the King, we will use restore to restore the stored board configuration. If the King is in check at any space along the way, it will exit with an error message.

store;
for c path origin dest:
	move origin #c;
	if sub checked #c:
		die A King may not castle through check.;
	endif;
	restore;
next;

When this is done, we move the pieces and unset their flags:

move origin dest;
set RDEST where origin #xdir 0;
move #RPOS #RDEST;
unsetflag origin dest #RPOS #RDEST;

Here now is the completed castle subroutine with a return value at the end, and with origin and dest replaced by the parameters from and to:

sub castle from to:
	local c RPOS RDEST xdir;
	set xdir sign minus file #to file #from;
	if not checkaleap #from #to * #xdir 2 0:
		die A King may not move from #from to #to;
	elseif not flag #from:
		die A King may not castle after it has already moved.;
	elseif capture:
		die A King may not castle to an occupied space.;
	elseif not empty where #from #xdir 0:
		die A King may not pass over an occupied space when castling.;
	endif;
	set c #to;
	do:
		set c where #c #xdir 0;
		if not empty #c and flag #c:
			break;
		elseif flag #c:
			die The King cannot castle with a missing piece;
		elseif not onboard #c:
			die No piece was found to castle with.;
		elseif not empty #c:
			die The King cannot castle with the piece at #c;
		endif;
	loop;
	set RPOS #c;
	move #to #from;
	if sub checked #from:
		die A King may not castle out of check.;
	endif;
	move #from #to;
	set RDEST where #from #xdir 0;
	move #RPOS #RDEST;
	unsetflag #from #to #RPOS #RDEST;
	return true;
endsub;

With this subroutine, castling is done as a King move. Just move the King two spaces left or right, and this subroutine will handle the rest.

Writing the Post-Move Code for Chess

What we have done so far is preparatory. It is code we will need for enforcing the rules, but it hasn't been put into use yet. Once a move is made, we want our code to evaluate the move. This will be done in the Post-Move sections. There are two Post-Move sections, one for the first player (White), and one for the second player (Black). Before we check whether a piece moved legally, there are some other legal moves we want to rule out. One is preventing a player from moving his opponent's pieces. It can be done like this:

if islower moved:
  die You may not move one of your opponent's pieces.;
endif;

Another thing to prevent is capturing a piece on the same side. The functions for evaluating piece movement do not check for this. So it has to be done separately. It can be done like this for this first player:

if isupper old:
  die You may not capture your own pieces.;
endif;

Next, we get to testing the legality of the piece's movement. First, we will set a variable called legal to false, then we'll handle evalation of piece movement with some nested conditionals:

set legal false;
if == moved P:
    set legal sub P origin dest;
else:
  set ep false;
  if != space dest moved:
    die You may not change the type of this piece.;
  elseif == moved K:
    set legal sub castle origin dest or fn K origin dest;
    set K dest;
  elseif match Q R B N:  
    set legal fn moved origin dest;
  endif;
endif;

The Pawn is treated separately, because it uses a subroutine instead of a function to evaluate its movement. Also, there are a couple more things to do for any piece that is not a Pawn. One is to set ep to false, and the other is to prevent the piece from changing to another type of piece. The last elseif uses match Q R B N just to rule out movement of pieces not used in Chess. What we have so far can be tightened up a little but by using elseif in place of the subsequent ifs. To do it like this, set legal false has been moved to the beginning.

set legal false;
if islower moved:
  die You may not move one of your opponent's pieces.;
elseif isupper old:
  die You may not capture your own pieces.;
elseif == moved P:
    set legal sub P origin dest;
else:
  set ep false;
  if != space dest moved:
    die You may not change the type of this piece.;
  elseif == moved K:
    set legal sub castle origin dest or fn K origin dest;
    set K dest;
  elseif match Q R B N:  
    set legal fn moved origin dest;
  endif;
endif;

Next, we check the value of legal, and this should be done with an if, not an elseif.

if not var legal:
  die You may not move a moved from origin to dest;
endif;

After this, we make sure that the move has not put the King in check:

if sub checked #K:
  die You may not move into check.;
endif;

Finally, we will unset the flags, if any were set, for the origin and destination of the move. This will cover any movement of a Rook or King, as well as any capture of an unmoved Rook. Since flags are not set for anything else, checking for the exact conditions when a flag needs to be unset is a waste of time. It is simple enough to just unset the flags, even if they were never set.

unsetflag origin dest;

That's everything that needs to be done in the Post-Move section for White. Let's look at it all together and tighten things up a little bit:

set legal false;
if islower moved:
  die You may not move one of your opponent's pieces.;
elseif isupper old:
  die You may not capture your own pieces.;
elseif == moved P:
    set legal sub P origin dest;
else:
  set ep false;
  if != space dest moved:
    die You may not change the type of this piece.;
  elseif == moved K:
    set legal sub castle origin dest or fn K origin dest;
    set K dest;
  elseif match moved Q R B N:  
    set legal fn moved origin dest;
  endif;
endif;
if not var legal:
  die You may not move a moved from origin to dest;
elseif sub checked #K:
  die You may not move into check.;
endif;
unsetflag origin dest;

The code for the second player's Post-Move section will follow the same logic, but it will switch cases, like so:

set legal false;
if isupper moved:
  die You may not move one of your opponent's pieces.;
elseif islower old:
  die You may not capture your own pieces.;
elseif == moved p:
    set legal sub p origin dest;
else:
  set ep false;
  if != space dest moved:
    die You may not change the type of this piece.;
  elseif == moved k:
    set legal sub castle origin dest or fn k origin dest;
    set k dest;
  elseif match moved q r b n:  
    set legal fn moved origin dest;
  endif;
endif;
if not var legal:
  die You may not move a moved from origin to dest;
elseif sub checked #k:
  die You may not move into check.;
endif;
unsetflag origin dest;

Testing the Legality of Piece Moves in Other Variants

While much of what has been described above will apply to Chess variants too, some Chess variants include more pieces than just leapers and riders. Here are built-in functions for testing the legality of other types of moves:

These take four arguments. As with checkleap, checkride and others, the first two arguments are for the origin and destination of the move, and the last two describe a leap distance in files and ranks. For the symmetrical functions, files and ranks may be entered in any order, but for asymmetrical functions, files should be entered before ranks. In the descriptions I copied from the Developer's Guide, the arguments, or operands, are called op1, op2, op3, and op4.

checkahop
Checks whether the move from op1 to op2 was a legitimate hopping move, consisting of leaps of op3 files and op4 ranks. A hopping move must hop over one screen. This operation checks only in the direction described. Negative numbers may be used for op3 and op4 to reverse directions. Positive directions go from a1 to h8 in Chess.
checkgrasshop
Checks the legality of a Grasshopper move. The Grasshopper is a fairy piece that moves and captures by jumping to the space immediately after an occupied space.
checkhop
Checks whether the move from op1 to op2, or vice versa, was a legitimate hopping move, consisting of leaps of op3 files and op4 ranks, or of op4 ranks and op3 files. A hopping move must hop over one screen. This operation checks symmetrically in all directions. Negative values for op3 and op4 have no special meaning, because only their absolute values are used. For example, checkhop a4 g4 0 1 would check whether this move was a legitimate move for a Cannon in Korean Chess. The Cannon in Chinese Chess is a divergent piece that uses this only for its capturing move.
checklongleap
Checks the legality of a Longleaper move, a piece found in Ultima that can capture multiple pieces by leaping over them.
voidleap
Checks the legality of a voidleap from op1 to op2 in a direction defined by op3 and op4. This is a one space move that may pass over void, i.e. nonspace, identified in Game Courier with a hyphen, -, for the piece value. This is for the Voidrider from Voidrider Chess.
voidride
Checks the legality of a voidride from op1 to op2 in a direction defined by op3 and op4. This is a move that may carry the piece's space across void to a void location or leap across void. This is for the Voidrider from Voidrider Chess.

These functions take three arguments. The first two are the origin and destination of the move, and the third is a number of steps:

checkmaxsteps
Checks whether a move made up of up to op3 steps from one adjacent space to another can go from op1 to op2.
checknsteps
Checks whether a move made up of exactly op3 steps from one adjacent space to another can go from op1 to op2. This function is useful for pieces in Jetan.

These take six arguments. The first two are the origin and destination of the move. The next two specify a distance in files and ranks, and so do the last two. The symmetrical version is useful for the Knight in Chinese Chess, and the asymmetrical version is useful for the Pawn in chess.

checkatwostep
Checks whether a two step move from op1 to op2 could be made with the first step of op3 files and op4 ranks, and the second step of op5 files and op6 ranks. The first step should be to an empty space. This operation checks only in the direction described. Negative numbers may be used for op3 and op4 to reverse directions. For example, "checkatwostep e2 e4 0 1 0 1" would check whether a piece stepped foward two spaces, as a Pawn can on its first move in Chess.
checktwostep
Checks whether a two step move from op1 to op2 could be made with a first step of op3 files and op4 ranks, and a second step of op5 files and op6 ranks, or if it could be made with a first step of op4 files and op3 ranks and a second of op6 files and op5 ranks. For example, "checktwostep b1 c3 0 1 1 1" checks whether the move from b1 to c3 could be made by a Knight in Chinese Chess.

These last two take an indefinately long number of arguments. The first two arguments will be the origin and destination of the move, and the remaining arguments will be pairs of files and ranks. For the asymmetrical version, files must go before ranks in each pair. These are for pieces that move along a winding path without leaping over pieces.

checkapath
Checks whether a piece may go along a specific unobstructed path from op1 to op2 according to a path given in an array as op3 or given as the remaining arguments. The path should be described as a series of paired numbers, the first number giving the number of files to move, and the second giving the number of ranks to move.
checkpath
Checks whether a piece may go along a symmetrically defined unobstructed path from op1 to op2 according to a path given in an array as op3 or given as the remaining arguments. The path should be described as a series of paired numbers, the first number giving the number of files to move, and the second giving the number of ranks to move.

Checkmate and Stalemate in Chess

Chess is won through checkmate, and it is drawn through stalemate. To fully enforce the rules of Chess, we need to detect stalemate and checkmate. Since we already have a checked subroutine, and checkmate is just check plus stalemate, all that is needed is a subroutine for detecting stalemate. The basic thing that a stalemate subroutine needs to do is go through all possible moves and check whether any can be made. If it finds even one that can be made, it will exit without checking the remaining possible moves and return false. If it goes through all possible moves without finding one that is legal, it will return true. Let's look at how a stalemated subroutine can go about doing this.

Two factors will affect whether a piece move is legal, whether the piece move is allowed by its powers of movement and whether the move leaves the King in check. Because a King move changes the position of the King, it is important to do King moves separately from other pieces. The rest of the pieces can be done with a for-loop that goes through each of a player's pieces, testing whether any of its possible moves are legal. It would be inefficient to go through every board space and test whether a piece can move there. Instead, we will use functions to create an array of the locations a piece can move to. These functions will make use of the built-in functions leaps and rides. Pawns, being aymmetrical, will use where

leaps
Given a coordinate and a distance given in x y form, it returns an array of every space that distance away from the space whose coordinate was given. For example, leaps h1 1 0 would return the array (g1 h2). The array may have as few as one space, leaps a1 1 1 or as many as eight, leaps e4 1 2. Used for generating an array of all spaces a piece may legally step one space to. Useful when generating possible moves while checking for stalemate.
rides
Given a coordinate and a distance given in x y form, it returns an array of every space a piece could reach by riding a leap of that distance from the space. This will include empty spaces and the first occupied space. Basically, it puts in an array every space the checkride function would recognize as a legal move to with the same parameters. Its main use is to generate an array of the spaces a riding piece may move to, which can be used to check the legality of possible moves in a subroutine for checkmate or stalemate.
where
Given a coordinate and two numbers, returns the coordinate of the space that is op2 files and op3 ranks away from the space whose coordinate is given in op1.

I normally use the convention of naming these functions after the piece label plus a capital L. Here are the functions for possible piece moves:

def PL array where #0 0 2 where #0 0 1 where #0 -1 1 where #0 1 1;
def pL array where #0 0 -2 where #0 0 -1 where #0 -1 -1 where #0 1 -1;

def NL leaps #0 1 2;
def BL rides #0 1 1;
def RL rides #0 1 0;
def QL merge rides #0 1 0 rides #0 1 1;
def KL merge leaps #0 1 0 leaps #0 1 1;

def nL leaps #0 1 2;
def bL rides #0 1 1;
def rL rides #0 1 0;
def qL merge rides #0 1 0 rides #0 1 1;
def kL merge leaps #0 1 0 leaps #0 1 1;

Since we're going to use functions to test the legality of possible moves, the P and p functions will need to be expanded. These were initially written just for the sake of testing whether a Pawn was checking a King. The P and p subroutines will not do, because they are for actual moves, sometimes exiting with error messages or asking the player what to promote to. Functions for testing possible Pawn moves don't have to deal with promotion, and they can silently fail when a move is illegal. Here are our new P and p functions:

def P and == var ep join filename #1 rankname #0 checkleap #0 #1 1 1 or and == rankname #0 2 checkatwostep #0 #1 0 1 0 1 or checkleap #0 #1 0 1 and empty #1 or and islower space #1 checkleap #0 #1 1 1 and > rank #1 rank #0;

def p and == var ep join filename #1 rankname #0 checkleap #0 #1 1 1 or and == rankname #0 7 checkatwostep #0 #1 0 -1 0 -1 or checkleap #0 #1 0 1 and empty #1 or and isupper space #1 checkleap #0 #1 1 1 and < rank #1 rank #0;

Since these are long, let's parse out one of them:

def P 
	and 
		== 
			var ep 
			join filename #1 rankname #0 
		checkleap #0 #1 1 1 
	or 
		and 
			== rankname #0 2 
			checkatwostep #0 #1 0 1 0 1 
	or checkleap #0 #1 0 1 
	and empty #1 
	or 
		and 
			islower space #1 
			checkleap #0 #1 1 1 
	and > rank #1 rank #0;

To follow the logic of this, there are still two things you need to remember. One is that Polish notation is interpreted backwards, starting at the end. Like Reverse Polish Notation, which has been used in calculators, Polish notation is unambiguous. It does not require parentheses to disambiguate which operands belong to which operator. But RPN would put operands before operators, and PN puts operators first. So I went with PN. The only drawback is that you have to read it backwards to fully understand its logic.

The other thing is that the logical operators and and or have the option of breaking away early when given a single operator. When and is followed by a single value that is false, it stops execution of the function and returns false. An expression like and == 9 9 == 3 4 will be false, but it will calculate both equations before determining this. Rewriting it as == 9 9 and == 3 4 allows it to stop and return false if == 3 4 is false. When or is followed by a single argument that is true, it stops execution of the function and returns true. An expression like or == 3 7 != 8 9 will be true, but it will calculate both equations before determining this. Rewriting it as == 3 7 and != 8 9 allows it to stop and return true if != 3 4 is true.

Starting from the end, and > rank #1 rank #0 is a test to make sure that the Pawn is moving forward. If it is not a forward move, it will not be legal, and nothing more needs to be calculated. Next, or and islower space #1 checkleap #0 #1 1 1 tests whether it is a normal capture of an enemy piece. If it is, it is a legal move, and the function can return true without continuing. Next, and empty #1 verifies that the move is to an empty space. Since a legal capture has already been checked for, any subsequent legal move will be to an empty space. If the space is not empty, nothing more needs to be calculated, and the function can return false. With or checkleap #0 #1 0 1 it checks for a legal one space move. Since it has already been determined that the Pawn is moving forward, it's alright to use the symmetrical checkleap instead of the aymmetrical checkaleap. If it's not a one-step forward move, the function continues with or and == rankname #0 2 checkatwostep #0 #1 0 1 0 1 to check for a legal double move. It it is a legal double move from the second rank, it stops execution and returns true. Finally, if it has not been any other kind of legal move, it checks if it is a legal en passant move with and == var ep join filename #1 rankname #0 checkleap #0 #1 1 1. The and starting this expression is not a break-away and. It takes two arguments, an equation and the checkleap function. This checks that it is a diagonal move to the space directly past the Pawn that made a double move. It does not have to remove the enemy Pawn to determine whether capture by en passant is legal, because there is no chance that capturing a Pawn by en passant will reveal a check. This is because a double Pawn move would never block a check on the enemy King nor would it ever capture an enemy piece that was already blocking a check, and en passant capture is available only on the next turn. Therefore, it isn't essential, at least for Chess, for these functions to remove a captured Pawn when checking the legality of an en passant move. It is enough to check whether the Pawn can legally make the move. With our functions in hand, we can now get to writing the stalemated subroutine.

A stalemated subroutine has to search for a piece capable of moving legally, potentially cycling through all pieces that can move and testing the legality of every possible move. The first thing it needs to do is identify the pieces belonging to the side whose turn it is to move. We will need an array of all pieces belonging to the player and a function for identifying a piece belonging to the same player. The first is for cycling through all the pieces that can move, and the second is for ruling out moves to spaces occupied by friendly pieces. To do this, it has to identify whose turn it is. It will do this by looking at the case of the King, and it will be given the King's position as a parameter. Initially, the subroutine looks like this:

sub stalemated kingpos:
	if isupper #kingpos:
		def friends onlyupper;
		def friend isupper #0;
	else:
		def friends onlylower;
		def friend islower #0;
	endif;
endsub;

The onlyupper function returns an array of all uppercase pieces on the board, and the onlylower function returns an array of all lowercase pieces on the board. We define the function friends to match the function returning an array of all pieces on the same side as the King. Likewise, isupper and islower identify a piece belonging to one side or another. We can now create a for-next loop that cycles through the pieces:

for (from piece) fn friends:
next;

When given two variable names in parentheses, for, which works the same as foreach, places the key of the array element in the first and the value of the array element in the second. Since the friends function will be returning a portion of the $space array, which uses coordinates as keys and piece labels as values, that's what to expect from this. The coordinate of the space will be placed in from, and the piece label will be placed in piece. Now, we need to add the content of the for-next loop. Let's use a switch-statement.

store;
for (from piece) fn friends:
	switch #piece:
		case K k:
			for to fn join #piece L #kingpos:
				if not fn friend space #to:
					move #kingpos #to;
					set incheck sub checked #to;
					restore;
					verify #incheck;
				endif;
			next;
			break;
		case P p:
			for to fn join #piece L #from:
				if fn #piece #from #to and not fn friend space #to:
					move #from #to;
					set incheck sub checked #kingpos;
					restore;
					verify #incheck;
				endif;
			next;
			break;
		default:
			for to fn join #piece L #from:
				if not fn friend space #to:
					move #from #to;
					set incheck sub checked #kingpos;
					restore;
					verify #incheck;
				endif;
			next;
			break;
	endswitch;
next;

The switch statement works as a computed goto. It includes various case labels, which must all be scalar values, not expressions. It goes directly to the case label matching the expression following switch. When there isn't one, it goes to the default label. Each body of code following a label should end with break, which will go down to the end of the switch statement. The three cases cover Kings, Pawns, and other pieces. Here is the general approach behind the code used for each. It uses the ?L function for the piece to determine which spaces to test it's ability to move to. For each space a piece can move to, it stores the current position, which it does before the switch statement, makes the move, tests whether it puts the King in check, restores the current position, then verifies that the piece is in check. If it is not in check, verify exits the subroutine and returns a value of false. Otherwise, it continues. The King is done separately, because it has to test whether the position the King moves to is checked, whereas for other pieces it has to test whether the King's original position is checked. For most pieces, including the King, the ?L function used to get the spaces it might move to exactly matches the spaces that the function for it would recognize legal moves to. So, for most pieces, it is not necessary to also check the legality of the move with the function matching the piece's label. But it is necessary for the Pawn. Because it is a divergent piece with special moves, there may be spaces in its ?L function array it cannot legally move to. Although the King also has a special move, namely castling, it is not necessary to check for it, because a King cannot castle unless it can legally move to the space it would pass over while castling. If it can castle, it can already make another legal move, and that is sufficient for telling that it is not stalemate. Now let's look at the complete subroutine:

sub stalemated kingpos:
	local from to;
	if isupper #kingpos:
		def friends onlyupper;
		def friend isupper #0;
	else:
		def friends onlylower;
		def friend islower #0;
	endif;
	store;
	for (from piece) fn friends:
		switch #piece:
			case K k:
				for to fn join #piece L #kingpos:
					if not fn friend space #to:
						move #kingpos #to;
						set incheck sub checked #to;
						restore;
						verify #incheck;
					endif;
				next;
				break;
			case P p:
				for to fn join #piece L #from:
					if fn #piece #from #to and not fn friend space #to:
						move #from #to;
						set incheck sub checked #kingpos;
						restore;
						verify #incheck;
					endif;
				next;
				break;
			default:
				for to fn join #piece L #from:
					if not fn friend space #to:
						move #from #to;
						set incheck sub checked #kingpos;
						restore;
						verify #incheck;
					endif;
				next;
				break;
		endswitch;
	next;
	return true;
endsub;

The last thing added to the subroutine is return true at the end. If it gets this far, not a single move has been found legal, and it is stalemate.

Writing the Post-Game Code for Chess

We are going to test for check, checkmate, and stalemate in the Post-Game sections. Although we could do it in the Post-Move sections, it would slow things down, and it is not necessary. Once a game ends, players will not be able to continue it. Until then, all it will notice is check, which is just a convenience for actual players. There is no need to note it for past moves. What we want to do in the post game code is test for check, then test for stalemate, reporting when a move places a King in Check, wins the game through checkmate, or draws the game through stalemate. It is fairly straightforward and looks like this for the first player:

if sub checked #k:
  if sub stalemated #k:
    say Checkmate! White has won!;
    won;
  else:
    say Check!;
  endif;
elseif sub stalemated #k:
  say Stalemate! The game is drawn.;
  drawn;
endif;

A few new commands appear here. The say command writes a message above the board after all the moves have been calculated. Unlike echo, which immediately prints to the screen, say waits, and it prints only one message. If you use say more than once, it will print only the last message given to the command. The won command ends the game and marks the current player as the winner, while the drawn command ends the game and marks it as drawn. The code for the second player is just the same but replaces #k with #K and White with Black, as shown below:

if sub checked #K:
  if sub stalemated #K:
    say Checkmate! Black has won!;
    won;
  else:
    say Check!;
  endif;
elseif sub stalemated #K:
  say Stalemate! The game is drawn.;
  drawn;
endif;

Handling 3-Times Repetition and the 50-Moves Rule

Stalemate is not the only way Chess can end in a draw. It can also be drawn when the position repeats itself 3 times or when 50 moves have been made without a Pawn move or a capture. These can be handled by keeping count of things in the Post-Move sections. To keep track of the position, we will just use some very long variable names, like so:

set posvar join "w" join fencode boardflags;
inc #posvar;

The first line creates a string that has three pieces of information: who is moving, the board position, and what flags are set. The next line treats this string as a variable name by preceding it with #. The inc command increments its value. If it hasn't appeared before, it's value will be 0, and this command will increment it to 1. This should be placed at the very end of the Post-Game code, so that changes to the flags have been accounted for. To check for three-times repetition, this can be added to the Post-Game section:

if >= var #posvar 3:
  say Three Times Repetition! Drawn Game!;
  drawn;
endif;

In the Post-Game code, posvar should already be set to what it was last set to. When var is used in an expression, what follows it is treated as a variable name, it and returns the value of the variable. This is the same as what you would get by preceding the variable name with #, but it is available only in expressions. One of the benefits of var is that it can be used multiple times in a row. Here, we are already using # before the variable name posvar, because we want to use the value of posvar as a variable name. But to retrieve its value, we can't just do ##posvar. So we do var #posvar instead. When this is greater than or equal to three, the game is drawn.

To check for the 50-moves rule, we will use a single counter. This will be set to 0 in the Pre-Game code, incremented for each move that is not a Pawn move or capture, and reset to 0 for any move that is a Pawn move or capture. First, let's set it in the Pre-Game code:

set nopvc 0;

The name means no Pawn or capture, the v being a logical symbol for or. Next, we will insert code to increment or reset the counter at appropriate places in the Post-Move code.:

set legal false;
if islower moved:
  die You may not move one of your opponent's pieces.;
elseif isupper old:
  die You may not capture your own pieces.;
elseif == moved P:
    set legal sub P origin dest;
	set nopvc 0;
else:
  set ep false;
  if capture:
    set nopvc 0;
  else
    inc nopvc;
  endif;
  if != space dest moved:
    die You may not change the type of this piece.;
  elseif == moved K:
    set legal sub castle origin dest or fn K origin dest;
    set K dest;
  elseif match moved Q R B N:  
    set legal fn moved origin dest;
  endif;
endif;
if not var legal:
  die You may not move a moved from origin to dest;
elseif sub checked #K:
  die You may not move into check.;
endif;
unsetflag origin dest;
set posvar join "w" join fencode boardflags;
inc #posvar;

Finally, this can be added to the Post-Game sections:

if >= #nopvc 100:
  say Fifty Moves Without Moving a Pawn or Capturing! Game Drawn!;
  drawn;
endif;

Since it is 50 moves per side, not 50 moves total, the comparison is actually made with 100. This should appear at the very end, just in case the last move won through checkmate or already drew through stalemate. So the Post-Game code for the first player would look this this:

if >= var #posvar 3:
  say Three Times Repetition! Drawn Game!;
  drawn;
endif;
if sub checked #k:
  if sub stalemated #k:
    say Checkmate! White has won!;
    won;
  else:
    say Check!;
  endif;
elseif sub stalemated #k:
  say Stalemate! The game is drawn.;
  drawn;
endif;
if >= #nopvc 100:
  say Fifty Moves Without Moving a Pawn or Capturing! Game Drawn!;
  drawn;
endif;

Closing One Last Loophole for Illegal Moves

We are almost done programming the rules of Chess. The last detail to take care of is to limit user input, so that long moves and GAME Code commands cannot be entered as moves. By default, Game Courier will let you enter any series of GAME Code instructions as a single move. This is useful when the game you're playing hasn't been programmed, but when the rules have been programmed, it leaves a loophole for breaking the rules, and this loophole needs to be closed. These lines, placed in the Pre-Game section, close this loophole:

setsystem maxmove 2;
ban commands allmoves;
allow moves 1 captures 1 promotions 2;

The first line sets a system variable to limit the number of moves that can be entered at once to two. The next line bans all commands and moves, essentially all user input altogether. Actually, banning commands doesn't ban the resign command, which any player may use any time to resign. The last line makes some exceptions. It allows moves and captures on the first move, and it allows promotions on the second.

This ends the tutorial on programming Chess for Game Courier. The code used in this tutorial can be seen in context in at http://play.chessvariants.com/pbm/play.php?game=Chess&settings=example. Click on Edit to view the code. The include file used for this tutorial can be seen at example.txt.


Written by Fergus Duniho
WWW Page Created: 7 April 2013.