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

Anatomy of a Preset: Chess

NOTE: The contents of this article became outdated soon after I wrote it. What it explains will work, and some of the code described here is still used, but since writing it, I have expanded the capabilities of the GAME Code language, and I have written new presets for Chess. This article no longer describes how the Chess preset works. For an up-to-date list of all tutorials, look in the Developer's Guide: Tutorials.

On this page, I will explain in detail the code used for enforcing the rules of Chess in Game Courier. This should help provide you with a good understanding of how to write rule-enforcement code and what goes into it.

Pre-Game Code

This code will be evaluated before the game starts.

These flags represent the positions of the Kings and Rooks. They are all set to true here, and by default all flags corresponding to the other board positions are false. Whenever a King or Rook moves, the flag for its position will be set to false. This is to make sure that no King or Rook castles after moving. The code for castling will require the flags for the King and Rook spaces to both be true for castling to occur.

setflag a1 a8 h1 h8 e1 e8;

These two variables represent the positions of the two Kings. The lowercase k is for the Black King, and the uppercase K is for the White King. Whenever a King moves, the contents of the appropriate variable will be updated to its current position. These variables are used for checking whether a player's move has placed his King in check. This is done by checking whether the space the King is on is attacked by any enemy piece.

set k e8; set K e1;

This variable is used for en passant. Whenever a Pawn makes a double move, ep will be set to the location of the space the Pawn moved to. Otherwise, it will be set to null, as it is at the beginning of the game. When it is not set to null, an enemy Pawn next to the position in ep may capture by en passant.

set ep null;

Post Game 1 Code

This code is executed each time after White moves. It is for checking whether the move White has made is legal. If it is not legal, it will report this back to the player and not allow his move to go through. It does not check whether a player is in checkmate, but it will not allow a checkmated player to make any move.

This first piece of code is a conditional statement that makes sure that White does not capture any of his own pieces. The word old refers to an internal variable that contains the old contents of the space that a piece has just moved to. This tells what piece was on the space a piece has just moved to. Since White pieces are all represented by uppercase letters, and Black's are all lowercase, an uppercase letter here means that it was a capture of one's own piece. Since this is illegal, it uses the die command to stop execution of the script and to report back the message "You may not capture your own pieces." The "endif;" line closes off the scope of the "if" statement.

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

This piece of code checks if the piece that has moved is not a Pawn. The word moved is an internal variable that identifies the piece that has just moved, and P is the label used for a White Pawn. It is checking whether the moved piece is not a Pawn, because some operations are common to all non-Pawn pieces. First, it sets ep to null, which indicates that no piece may be captured by en passant on the next turn. It then makes sure that White has not changed the type of the piece that has moved. It does this by comparing the value of moved with the current contents of the space moved to. The word dest is an internal variable representing the coordinates of the destination space, and "space dest" is an expression that reports back the contents of the destination space. If they are unequal to each other, then an illegal promotion or demotion has been attempted. In that case, execution is halted with the die command, giving the message "You may not change the type of the piece." It ends with two "endif;" statements, because there were two "if" statements, one nested within the other. The first "endif;" closed the scope of the second if, and the second "endif;" closed the scope of the first "if".

if not equal moved P;
  set ep null;
  if unequal space dest moved;
    die You may not change the type of this piece.;
  endif;
endif;

This next line sets the variable legal to false. This variable is used for checking whether the move made was legal. If it is found to be legal, this variable will be set to true.

set legal false;

This line checks whether the piece that has moved is a Pawn. It is the first in a series of if-elseif statements that continue until the type of piece that has moved is found.

if equal moved P;

This code is executed if a Pawn has moved. This if-statement checks if the Pawn has remained within its file, which it will do when not capturing, and that it has not captured. If it has done this much, it might be a legal move, and the code proceeds to check on this.

  if and not capture equal file origin file dest;

This code checks whether the Pawn has made a legal one-step move. If it has, then ep gets set to null, because no en passant capture will be legal on the next move.

    set legal checkaleap origin dest 0 1;
    if var legal;
      set ep null;

This code gets executed if the Pawn did not make a legal one-step move. It checks whether the Pawn has made a legal double move. If it has, then ep gets set to the coordinates of the destination, found in the internal variable called dest.

    else;
      set legal and equal rankname origin 2 checkatwostep origin dest 0 1 0 1;
    	  set ep dest;

This endif ends the scope of the if-statement started before the else-statement.

    endif;

If the Pawn's move was not a non-capturing move in the same file, then it should have been a capture. The capture function will be true if it was a regular capture by displacement, but it won't be true if it was an en passant capture. The expression "equal rankname origin 5" checks whether it might have been an en passant capture, since White will move to rank 5 when capturing by en passant.

  elseif or capture equal rankname origin 5;

The first step in checking the legality of a capturing move is to make sure it was a legal move to one of the spaces a Pawn could move to while capturing.

    set legal or checkaleap origin dest neg 1 1 checkaleap origin dest 1 1;

If it was a legal move, but it wasn't a capture by displacement, then it might have been an en passant capture. The following code will check for this possibility.

    if and var legal not capture;

This sets the variable cd to the name of the coordinate behind the space the Pawn has just moved to. I have forgotten what cd stands for. Maybe it was capture destination.

      set cd join filename dest 5;

If the space behind the Pawn is one that Black has just made a double move to, then it is a legal en passant move, and Game Courier removes the Black Pawn at this location.

      set legal equal var cd var ep;
      if var legal;
      	capture #ep;
      endif;
    endif;      

Since a capture is not a double move, ep gets set to null, meaning that en passant will not be legal on the next turn.

    set ep null;

The expression "endif 2;" is the same as two "endif" commands. It closes off the two most recently opened if statements that haven't been closed yet. This closes off the series of if-elseif statements that were checking on the legality of the Pawn move.

  endif 2;

Before finishing with the Pawn, promotions must be accounted for. This code prevents White from promoting his Pawn before reaching the 8th rank.

  if nor equal space dest moved equal rankname dest 8;
    die You may not promote a Pawn that is not on your last rank.;
  endif;

This code makes sure that White does promote his Pawn on reaching the 8th rank, and it makes sure that it promotes to nothing but a White Queen, Rook, Bishop, or Knight.

  if equal rankname dest 8;
    if equal P space dest;
      die You must promote a Pawn when it reaches your last rank.;
    elseif not match space dest Q R B N;
      set np space dest;
      die You may not promote your Pawn to a #np;
    endif 2;
  endif;

This bit of code is all we need to check the legality of the Knight's move. The checkleap function used here checks whether the move can be made as a 1-2 leap, which is either 1 file and 2 ranks away, or 2 files and 1 rank away.

elseif equal moved N;
  set legal checkleap origin dest 1 2;

This bit of code checks the legality of the Bishop's move. The checkride function used checks whether the move can be made as a series of one-step leaps in the same direction, with that direction being a simultaneous change of 1 rank and 1 file.

elseif equal moved B;
  set legal checkride origin dest 1 1;

This bit of code checks the legality of the Rook's move. The checkride function used checks whether the move can be made as a series of one-step leaps in the same direction, with that direction being a single change of one rank or one file, but not both. Assuming a legal move, it unsets the flag for the Rook's destination. This stops any Rook that moves to that space later from castling from it.

elseif equal moved R;
  set legal checkride origin dest 1 0;
  if var legal;
    unsetflag origin;
  endif;

This code checks the legality of the Queen's move. It uses an or on the code previously used to check the legality of the Bishop's move and the Rook's move. Its move is legal if it has moved like a Bishop or a Rook.

elseif equal moved Q;
  set legal or checkride origin dest 1 1 checkride origin dest 1 0;

This code checks the legality of the King's move. It is longer, because it will be checking the legality of the King's castling move. But we begin by checking whether the King has moved one step in any direction. We use the checkleap function for this, and we use it twice in the same way we used the checkride function to check the legality of the Queen's move. If it has moved one space, it has not castled, and it is okay to set legal to true right away. The code for the King's move will not be used for checking whether the King has moved into check. This will be checked for separately after all types of piece movement have been checked for legality, since we also need to make sure no other piece has revealed a check on the King or let in remain in check.

elseif equal moved K;
  set legal or checkleap origin dest 1 1 checkleap origin dest 1 0;

We now begin the code for checking the legality of a castling move. A castling move will be recognized by a move to c1 or g1, the two spaces a King may legally castle to. If the King has moved to one of these two spaces, then the legality of the move is checked into. Otherwise, the value of legal remains what it just got set to.

  if and equal origin e1 match dest c1 g1;

The simplest thing to check is whether the King has moved. So that is done first. If the e1 flag is no longer set, the King has moved.

    if not flag e1;
      die You can't castle, because you have already moved your King;
    endif;

The next thing to check is whether the King is in check, since a King may not castle out of check. These first two lines check whether Black Pawns and Black Knights are at locations which attack e1, the square the King must be on if it hasn't moved. It will be unnecessary to check for attacks from the Black King on d2, e2, and f2, because it could not check an unmoved King without moving into check, which it can't do.

    if match p space d2 space f2;
    elseif match n space c2 space d3 space f3 space g2;

The insight function checks returns the contents of the first non-empty space in the singular direction specified. This value gets compared with the labels for pieces that could attack along that line.

    elseif match insight e1 neg 1 0 r q;
    elseif match insight e1 0 1 r q;
    elseif match insight e1 1 0 r q;
    elseif match insight e1 neg 1 1 b q;
    elseif match insight e1 1 1 b q;

This series of if-elseif lines began with the assumption that legal was false, for any move to c1 or g1 would have been found illegal in the last statement to give legal a value. If it failed any test in the if-elseif series, it would not reach the else. So, if it reaches the else, it has passed a series of tests, and legal gets set to true, at least temporarily. The "endif 8" ends the whole series for checking whether e1 was attacked.

    else;
      set legal true;
    endif 7;

At this point, the value of legal is checked. If any test in the previous series of tests has failed, legal will be false, and Game Courier stops with the message "You can't castle when you're in check."

    if not var legal;
    	die You can't castle when you're in check;
    endif;

The variable legal now gets reset to false in preparation for a new series of tests. The tests could be done as one long anyfalse statement, but this method of using if-elseif statements with a concluding else saves processing time by ending the calculations once one test fails.

    set legal false;

We will now go to one of two sets of tests, depending upon which space the King has moved to. We begin here with the move to g1.

    if equal dest g1;

This code here gives a series of conditions of the factors that would make the King's move to g1 illegal. If any of the following conditions is true, the move to g1 is illegal, and the King cannot castle. In that event, legal will keep its value of false, and the code following the else won't be executed. The first line checks whether the Rook has moved and whether the King's path from e1 to g1 is blocked. The remaining conditions check whether f1 is attacked by any Black piece. Attacks from the Black King don't need to be checked for, because the Black King won't be on e2 or f2, and if it is on g2, the King will be moving into check, which is taken care of later.

      if anyfalse flag h1 empty f1 not capture;
      elseif match p space e2 space g2;
      elseif match n space d2 space e3 space g3 space h2;
      elseif match insight f1 0 1 r q;
      elseif match insight f1 neg 1 1 b q;
      elseif match insight f1 1 1 b q;
      else;
        set legal true;
        unsetflag h1;
        move h1 f1;
      endif 6;

If the castling move wasn't to g1, it must be to c1. Since we would get to this part of the code only if the King tried to move to either g1 or c1, there is no need to check here that the destination was indeed c1. The first condition checks for something more than what the first line in the previous bit of code checked for. Besides checking whether the Rook has moved and whether that the King's path is blocked, it checks whether the Rook's path is blocked to d1 by a piece on b1. This is because a Rook castling Queen-side passes over a space the King never moves to or passes over. Then, like before, the remaining code checks whether the King has passed through check at d1.

    else;
      if anyfalse flag a1 empty b1 empty d1 not capture;
      elseif match p space c2 space e2;
      elseif match n space f2 space e3 space c3 space b2 space f2;
      elseif match insight d1 0 1 r q;
      elseif match insight d1 neg 1 1 b q;
      elseif match insight d1 1 1 b q;
      else;
        set legal true;
        unsetflag a1;
        move a1 d1;
      endif 6;
    endif;

If castling has been found to be illegal, Game Courier halts execution with the message "You can't castle to c1" or the message "You can't castle to g1".

    if not var legal;
      die You can't castle to dest;
    endif;

This next endif closes off all the conditions that were checking whether the King could castle.

  endif;

Now that the legality of the King's move has been checked, the e1 flag is unset to indicate that the King has moved, and K gets the value of the King's new destination. This value will be used for checking whether the King has moved into check.

  unsetflag e1;
  set K dest;

The "endif all" expression ends every if. It is safe to use here, because if-elseif set it was closing off began at the top level.

endif all;

Now that we have found the piece which moved and checked its move for legality, it's time to halt execution if the move was found to be illegal. Also, if White tried to move a Black piece, no condition in this series will have been met, and the value of legal will have never changed from false. So the following bit of code also stops White from moving Black pieces.

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

At this point, the move is known to have moved by the rules governing its movement, but we still don't know whether White has moved into check. Since a move by any piece could put or leave one's King in check, this is done after the code that looks into the movement of specific pieces. The legal variable is reset to false. The general technique behind this code has been used in many places. There is a series of conditions with if and elseif without any code to execute if the condition is true. Each condition is a test in a series of tests. Every test has to be false for the code following the else to be executed. If any test proves true, then the King is in check. If no test proves true, the King is not in check, and the move is legal. If all tests fail, the else code gets executed, setting legal to true. Although all tests could be done in one long conditional, it is done this way to halt execution of the tests once any test proves true. Notice the use of #K in each condition. This represents the position of the King. The what function tells what piece is on the space a certain relative position away from the position at #K. It is used to check for attacks from Pawns, Knights, and Kings. The insight function is used to check for attacks from Bishops, Rook, and Queens.

set legal false;
if match p what #K 1 1 what #K neg 1 1;
elseif match n what #K 1 2 what #K neg 1 2 what #K 1 neg 2 what #K neg 1 neg 2; 
elseif match n what #K 2 1 what #K neg 2 1 what #K 2 neg 1 what #K neg 2 neg 1;
elseif match insight #K 0 neg 1 r q;
elseif match insight #K neg 1 0 r q;
elseif match insight #K 0 1 r q;
elseif match insight #K 1 0 r q;
elseif match insight #K neg 1 neg 1 b q;
elseif match insight #K neg 1 1 b q;
elseif match insight #K 1 neg 1 b q;
elseif match insight #K 1 1 b q;
elseif match k what #K 1 neg 1 what #K what 1 0 what #K 1 1 what #K 0 1;
elseif match k what #K neg 1 neg 1 what #K what neg 1 0 what #K neg 1 1 what #K 0 neg 1;
else;
  set legal true;
endif all;

If legal has remained false, then the King is in check, and the move was illegal. This ends the code used to check whether White's move was legal.

if not var legal;
  die You may not move into check;
endif;

Post-Move 2 Code

This code is executed every time after Black moves. It is used to check whether Black has moved illegally, and to automate parts of castling and en passant. It is closely parallel to the code used for White. The main changes are in the case used for pieces and in some particular coordinates mentioned in the code. I will break this up into fewer chunks and give less commentary. This will help you get a broader perspective on what the code is doing.

This code checks for a condition that would make a move by any piece illegal.

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

This code is for any piece except a Pawn. It sets ep to null to prevent en passant, and it makes sure the piece hasn't promoted.

if not equal moved p;
  set ep null;
  if unequal space dest moved;
    die You may not change the type of this piece.;
  endif;
endif;

Here is a long piece of code for checking the legality of moves for each type of piece that the player may move. The legal variable is initially set to false in case the player tries to move a White piece, which is a type of piece he cannot legally move. If the player has moved a piece he can legally move in a legal manner, then legal will be set to true by the time this block of code finishes.

set legal false;
if equal moved p;
  if and not capture equal file origin file dest;
    set legal checkaleap origin dest 0 neg 1;
    if var legal;
      set ep null;
    else;
      set legal and equal rankname origin 7 checkatwostep origin dest 0 neg 1 0 neg 1;
      set ep dest;
    endif;
  elseif or capture equal rankname origin 4;
    set legal or checkaleap origin dest neg 1 neg 1 checkaleap origin dest 1 neg 1;
    if and var legal not capture;
      set cd join filename dest 4;
      set legal equal var cd var ep;
      if var legal;
      	capture #ep;
      endif;
    endif;      
    set ep null;
  endif 2;
  if nor equal space dest moved equal rankname dest 1;
    die You may not promote a Pawn that is not on your last rank.;
  endif;
  if equal rankname dest 1;
    if equal p space dest;
      die You must promote a Pawn when it reaches your last rank.;
    elseif not match space dest q r b n;
      set np space dest;
      die You may not promote your Pawn to a #np;
    endif 2;
  endif;
elseif equal moved n;
  set legal checkleap origin dest 1 2;
elseif equal moved b;
  set legal checkride origin dest 1 1;
elseif equal moved r;
  set legal checkride origin dest 1 0;
  if var legal;
    unsetflag origin;
  endif;
elseif equal moved q;
  set legal or checkride origin dest 1 1 checkride origin dest 1 0;
elseif equal moved k;
  set legal or checkleap origin dest 1 1 checkleap origin dest 1 0;
  if and equal origin e8 match dest c8 g8;
    if not flag e8;
      die You can't castle, because you have already moved your King;
    endif;
    if match P space d7 space f7;
    elseif match N space c7 space d6 space f6 space g7;
    elseif match insight e8 neg 1 0 R Q;
    elseif match insight e8 0 neg 1 R Q;
    elseif match insight e8 1 0 R Q;
    elseif match insight e8 neg 1 neg 1 B Q;
    elseif match insight e8 1 neg 1 B Q;
    else;
      set legal true;
    endif 7;
    if not var legal;
    	die You can't castle when you're in check;
    endif;
    set legal false;
    if equal dest g8;
      if anyfalse flag h8 empty f8 not capture;
      elseif match P space e7 space g7;
      elseif match N space d7 space e6 space g6 space h7;
      elseif match insight f8 0 neg 1 R Q;
	  elseif match insight f8 neg 1 neg 1 B Q;
      elseif match insight f8 1 neg 1 B Q;
      else;
        set legal true;
        unsetflag h8;
        move h8 f8;
      endif 6;
    else;
      if anyfalse flag a8 empty b8 empty d8 not capture;
      elseif match P space c7 space e7;
      elseif match N space f7 space e6 space c6 space b7 space f7;
      elseif match insight d8 0 neg 1 R Q;
	  elseif match insight d8 neg 1 neg 1 B Q;
      elseif match insight d8 1 neg 1 B Q;
      else;
        set legal true;
        unsetflag a8;
        move a8 d8;
      endif 6;
    endif;
    if not var legal;
      die You may not castle to dest;
    endif;
  endif;
  unsetflag e8;
  set k dest;
endif all;
if not var legal;
  die You may not move a moved from origin to dest;
endif;

This code checks whether the King has moved into check. It is the last thing to check when checking whether Black has made an illegal move. It needs to be checked no matter what piece has moved, since a piece could unblock a check, or a move could fail to stop a check made by the other player on the previous move.

set legal false;
if match P what #k 1 neg 1 what #k neg 1 neg 1;
elseif match N what #k 1 2 what #k neg 1 2 what #k 1 neg 2 what #k neg 1 neg 2; 
elseif match N what #k 2 1 what #k neg 2 1 what #k 2 neg 1 what #k neg 2 neg 1;
elseif match insight #k 0 neg 1 R Q;
elseif match insight #k neg 1 0 R Q;
elseif match insight #k 0 1 R Q;
elseif match insight #k 1 0 R Q;
elseif match insight #k neg 1 neg 1 B Q;
elseif match insight #k neg 1 1 B Q;
elseif match insight #k 1 neg 1 B Q;
elseif match insight #k 1 1 B Q;
elseif match K what #k 1 neg 1 what #k what 1 0 what #k 1 1 what #k 0 1;
elseif match K what #k neg 1 neg 1 what #k what neg 1 0 what #k neg 1 1 what #k 0 neg 1;
else;
set legal true;
endif all;
if not var legal;
  die You may not move into check;
endif;


Written by Fergus Duniho
WWW Page Created: 26 November 2004.