You are here
Chess
Contents |
Play Chess![]() Chrome, FireFox or Internet Explorer Required. |
Introduction
The Chess webapp is a project of my own which I developed whilst undertaking my degree at Southern Cross University. The app demonstrates my abilities in a number of web languages including PHP, XHTML, JavaScript, CSS and Java, and utilises technologies such as HTML mail and Java Servlets. The project itself played an important role in establishing the Chess Club at Southern Cross University.
A secure login validates users in order to allocate the various games in play to each player. An encrypted password is generated upon initial registration and sent to their email account for verification which has a link back to the site with their details attached to the URL. Once registered, players can then challenge existing members or introduce new friends.
Upon each move submitted an email notification is sent to the opponent which again has a link back to the site. Games can be played live or move by move. A score board facility keeps track of wins etc.
Each game in play is associated with a pair of boards one for white and one for black. The board is simply an HTML table. The pieces are constructed as gif file images arranged in CSS divs and then superimposed over the squares on the board. Legal moves are validated using JavaScript and when a move is submitted PHP functions do the job of storing the game status.
The moves are detected and executed by tracking three mouse events: mousedown, mousemove and mouseup. The mousedown event loads the boards’ current position into a JavaScript variable along with the identity of the active piece. JavaScript functions are then able to determine a list of legal moves for that piece and highlight green dots embedded in the board indicating to the user which moves are legal. The mousemove event then sends mouse co-ordinates to the CSS div which contains the piece and its position on the board is altered according to where the cursor is moved. When the button is released the mouseup event fires and if the new position of the piece correlates to one of the legal moves generated by the mousedown event the new position is held in memory. Upon submission, PHP functions update the status of the board and then send an email notification to the opponent.
In contrast the move generator which I’ve named “Autochess” is a Java implementation and is deployed as a Servlet on a Tomcat server. The move generator has no interface of its own and plays no part in games between human players and was merely added for the sake of the challenge. The current boards' position is passed via the URL to the servlet which acts as a web service and returns a calculated move back to the board. PHP functions then detect and interpret the incoming move and update the game status accordingly.
The responsibility of the interface (the board), is to keep track of the current and previous positions together with the castling status, any enpassant pawns, calculate legal moves and detection of check, checkmate and stalemate etc. and also to graphically display and inform to the users the status of the game and provide a means for them to interact with it and also with each other. This only involves one move at a time and the boards’ current state is recorded in a string of about 350 characters which is appended to a data file as the game progresses.
Move Generation
The move generator “Autochess” by comparison is completely different. Autochess employs many of the classic algorithms and constructs which have been used in chess move generators since their inception such as MiniMax, AlphaBeta pruning, a transposition table, move evaluation and the concept of bitboards.
Considering that from the starting position, white has 20 possible moves and black has 20 possible moves in response then we’re looking at 400 different board positions 1 ply deep, i.e. 1 ply = 1 move by white and 1 move by black. Taking 20 as a conservative estimate each of these positions then has a further possible 20 moves by white and 20 by black. This means that after only 2 ply moves we’re looking at 160,000 different board positions (most of which represent no advantage to either side). To store board positions in memory for evaluation in the form of a string means 5.6 million characters and only represents the storage. This data still needs to be evaluated and we’re only 4 moves into the game.
The above suggests that we need a means to minimize the size of the boards’ representation. Using the concept of bitboards an entire board can be represented using 16 x 8 byte integers. Because there are 64 squares on the board and 8 bytes = 64 bits, each bit position in the 8 byte integer can then represent a square on the board. So we can represent pieces on the board by setting the bits in the appropriate position.
At this point I should mention that JavaScript has bitwise operators which only work on 32 bits, isn’t that good at arrays and although Javscript can be object oriented its methods of creating objects has limitations. PHP has bitwise operators but also limited to 32 bits, is better at arrays than JavaScript but not so good at multi-dimensional arrays and although it can be object oriented it also has some limitations and the object oriented syntax in PHP is somewhat cumbersome and verbose. What I’m suggesting is we need an object oriented language. Why? So we can clone the board and iteratively evaluate a multitude of moves! C++ would be an ideal choice however C++ is not that web friendly and moreover is platform specific. This is where Java comes into the equation. Java is purely object oriented, is web friendly, its bitwise operators can operate on 64 bit integers and very good at multi-dimensional arrays and platform independent.
Bitboards
The following example is an attempt to explain the concept of using bitboards:
Assuming we number the squares 1 to 64 starting from the Black Queens’ Rook from left to right.
The following represents square 1 as a binary number:
0000000000000000000000000000000000000000000000000000000000000001
Bit 1 is set indicating square 1.
This equals:
1 as a decimal.
The following represents square 64 as a binary number:
1000000000000000000000000000000000000000000000000000000000000000
Bit 64 is set indicating square 64.
This equals:
9223372036854776028 as a decimal.
So to represent the starting position for the two Black Rooks we would use:
0000000000000000000000000000000000000000000000000000000010000001
Bits 1 & 8 are set indicating squares 1 & 8.
This equals:
129 as a decimal.
To represent the starting position for the two Black Knights we would use:
0000000000000000000000000000000000000000000000000000000001000010
Bits 2 & 7 are set indicating squares 2 & 7.
This equals:
66 as a decimal.
To represent the starting position for the two Black Bishops we would use:
0000000000000000000000000000000000000000000000000000000000100100
Bits 3 & 6 are set indicating squares 3 & 6.
This equals:
36 as a decimal.
To represent the starting position for the Black pawns we would use:
0000000000000000000000000000000000000000000000001111111100000000
Bits 9 to 16 are set indicating squares 9 to 16.
This equals:
65280 as a decimal.
If you’re still following the starting position for the entire board would be represented by:
BLACK_ROOKS = 129;
BLACK_KNIGHTS = 66;
BLACK_BISHOPS = 36;
BLACK_QUEEN = 8;
BLACK_KING = 16;
BLACK_PAWNS = 65280
WHITE_ROOKS = 9295429630892704004;
WHITE_KNIGHTS = 4755801206503243226;
WHITE_BISHOPS = 2594073385365405666;
WHITE_QUEEN = 576460752303423528;
WHITE_KING = 1152921504606847046;
WHITE_PAWNS = 71776119061217280
To represent all occupied squares we need only bitwise ‘and’, all of these together thus:
OCCUPIED_SQUARES =
(BLACK_ROOKS & BLACK_KNIGHTS & BLACK_BISHOPS & BLACK_QUEEN & BLACK_KING & BLACK_PAWNS & WHITE_ROOKS & WHITE_KNIGHTS & WHITE_BISHOPS & WHITE_QUEEN & WHITE_KING & WHITE_PAWNS);
= 18446462598732908826;
To represent all empty squares we need only bitwise ‘or’, all of these together thus:
EMPTY_SQUARES =
(BLACK_ROOKS | BLACK_KNIGHTS | BLACK_BISHOPS | BLACK_QUEEN | BLACK_KING | BLACK_PAWNS | WHITE_ROOKS | WHITE_KNIGHTS | WHITE_BISHOPS | WHITE_QUEEN | WHITE_KING | WHITE_PAWNS);
And then apply the bitwise ‘not’ operator thus:
EMPTY_SQUARES = ~ EMPTY_SQUARES;
= 281474976645120
To represent all black occupied squares we need only bitwise ‘and’, all black together thus:
BLACK_OCCUPIED_SQUARES =
(BLACK_ROOKS & BLACK_KNIGHTS & BLACK_BISHOPS & BLACK_QUEEN & BLACK_KING & BLACK_PAWNS);
= 65535;
To represent all white occupied squares we need only bitwise ‘and’, all white together thus:
WHITE_OCCUPIED_SQUARES =
(WHITE_ROOKS & WHITE_KNIGHTS & WHITE_BISHOPS & WHITE_QUEEN & WHITE_KING & WHITE_PAWNS);
= 18446462598732840660
At first the numbers may seem daunting but this is merely how a computer stores the data representing the state of the board. In fact binary numbers may make more sense to a human not for their values but more so for the patterns they display. Everything is binary to a computer anyway. The point is that only 128 bytes are required in order to represent an entire board. Another benefit to using this technique is we can also use the low level bitwise operators to move the pieces on the board without having to resort to resource intensive looping. Bit shifting comes to the rescue.
Example:
Say we wanted to move the White Kings’ Pawn 1 square forward. This means we are moving the piece on square 53 to square 45. That’s a difference of 8 for each rank.
In binary this means:
The 53rd bit is set representing the white pawn.
WHITE_PAWN =
0000000000010000000000000000000000000000000000000000000000000000
i.e. WHITE_PAWN = 4503599627370496 decimal.
And we’re moving it to square 45.
WHITE_PAWN =
0000000000000000000100000000000000000000000000000000000000000000
The 45th bit is now set after the move.
WHITE_PAWN = 17592186044416 decimal.
To do this all we need do is shift the bit eight places to the right thus:
WHITE_PAWN = WHITE_PAWN >> 8;
If it were the Black Kings’ Pawn then it would go the other way.
The 13th bit is set representing the black pawn.
BLACK_PAWN =
0000000000000000000000000000000000000000000000000001000000000000
i.e. BLACK_PAWN = 4096 decimal.
And we’re moving it to square 21.
BLACK_PAWN =
0000000000000000000000000000000000000000000100000000000000000000
The 21st bit is now set after the move.
i.e. BLACK_PAWN = 1048576 decimal.
To do this all we need do is shift the bit eight places to the left thus:
BLACK_PAWN = BLACK_PAWN << 8;
Each shift to the left simply means multiplying by 2.
Example:
4096 x 2 = 8192
8192 x 2 = 16384
16384 x 2 = 32768
32768 x 2 = 65536
65536 x 2 = 131072
131072 x 2 = 262144
262144 x 2 = 524288
524288 x 2 = 1048576
Shifting to the right therefore means dividing by 2.
OK this is how we set a piece on a square.
Setting the Black King on square 5:
Starting from square 1 we need to shift 4 places to the left to get to square 5.
BLACK_KING = 1 << 4;
1 x 2 = 2
2 x 2 = 4
4 x 2 = 8
8 x 2 = 16
In Binary16 =
0000000000000000000000000000000000000000000000000000000000010000
Or without the leading zeros
16 = 10000
The 5th bit is set representing square 5.
If we number our squares from 0 to 63 in pseudo code it looks like this:
PIECE = 1 << SQUARE;
So to set up the starting board:
BLACK_QUEEN_ROOK = 1 << 0
BLACK_QUEEN_KNIGHT = 1 << 1;
BLACK_QUEEN_BISHOP = 1 << 2;
BLACK_QUEEN = 1 << 3;
BLACK_KING = 1 << 4;
BLACK_KING_BISHOP = 1 << 5;
BLACK_KING_KNIGHT = 1 << 6;
BLACK_KING_ROOK = 1 << 7;
BLACK_QUEEN_ROOK_PAWN = 1 << 8;
BLACK_QUEEN_KNIGHT_PAWN = 1 << 9;
BLACK_QUEEN_BISHOP_PAWN = 1 << 10
BLACK_QUEEN_PAWN = 1 << 11;
BLACK_KING_PAWN = 1 << 12;
BLACK_KING_BISHOP_PAWN = 1 << 13;
BLACK_KING_KNIGHT_PAWN = 1 << 14;
BLACK_KING_ROOK_PAWN = 1 << 15;
WHITE_QUEEN_ROOK_PAWN = 1 << 48;
WHITE_QUEEN_KNIGHT_PAWN = 1 << 49;
WHITE_QUEEN_BISHOP_PAWN = 1 << 50
WHITE_QUEEN_PAWN = 1 << 51;
WHITE_KING_PAWN = 1 << 52;
WHITE_KING_BISHOP_PAWN = 1 << 53;
WHITE_KING_KNIGHT_PAWN = 1 << 54;
WHITE_KING_ROOK_PAWN = 1 << 55;
WHITE_QUEEN_ROOK = 1 << 56;
WHITE_QUEEN_KNIGHT = 1 << 57;
WHITE_QUEEN_BISHOP = 1 << 58;
WHITE_QUEEN = 1 << 59;
WHITE_KING = 1 << 60
WHITE_KING_BISHOP = 1 << 61;
WHITE_KING_KNIGHT = 1 << 62;
WHITE_KING_ROOK = 1 << 63;
We then bitwise ‘or’ the Black Rooks together, the Black Knights together and so on and do the same for the white pieces and we have our 12 x 8 byte integers for all our pieces and from there we can calculate our empty squares, occupied squares, black occupied squares and white occupied squares, 128 bytes in total.
We can simplify this even further with a function that reads from a file or some other form of input with the start position of the pieces that looks something like this:
BR 00
BN 01
BB 02
BQ 03
BK 04
BB 05
BN 06
BR 07
BP 08
BP 09
BP 10
BP 11
BP 12
BP 13
BP 14
BP 15
WP 48
WP 49
WP 50
WP 51
WP 52
WP 53
WP 54
WP 55
WR 56
WN 57
WB 58
WQ 59
WK 60
WB 61
WN 62
WR 63
Add a few flags like whose turn it is, how many pieces there are on the board, the castling status plus any enpassant pawns that may apply and we can then start the game from any legal position on the board. Moreover if we encapsulate all of this into a board class then we can instantiate as many objects of that class as we like (within the limits of our operating environment) and then execute and evaluate any possible legal moves upon those board objects. In fact we don’t evaluate the moves themselves but what we do is evaluate the strength of the position that each move leaves each board object in.
Legal Moves
This leads us to legal moves. In the interface, legal moves are validated in JavaScript (client side) however this took around 30,000 lines of code which admittedly could be reduced to perhaps 10,000 after compression and perhaps even further after optimization. Having said this because of JavaScript’s limitations with regards multi-dimensional arrays and my own need to be able to read the code for debugging purposes the 30,000 lines of code needed to be written anyway. Optimization which I haven’t yet carried out, would involve considerably more work and testing only to achieve the same outcome without much gain in speed of execution.
Regarding the client side move validation in JavaScript we are only concerned with the current move by the active piece clicked on and also the detection of check, checkmate, stalemate and the castling and enpassant pawn status. This doesn’t represent much of an overhead on the client machine as it only involves the next move.
Autochess poses a completely different scenario. In order to provide any reasonably viable move in response to an opponent’s move we need to test and evaluate all the possible moves that can be legally made from that position and also a multitude of moves that may eventuate from each of those. I’ll talk about evaluation later here the focus is on determining pseudo legal moves for each piece from each square. We first need to differentiate moves from pseudo legal moves then from semi-legal moves and finally from legal moves.
The set of all possible moves means, moving any piece from any square to any other square. These are not necessarily legal. Of course only one side can move if it is that sides turn. Pseudo legal moves means, moving any piece from any square to any appropriate square defined by the rules of that piece. Still this is not necessarily legal. In the case that the destination square is occupied by a piece of the same side or is blocked by a piece of either side or is occupied by the king of the opposing side (which would also mean check) then the move is not legal. Other pseudo legal moves which may or may not be legal involve the rules of castling, enpassant pawns, pawn attacks, pawn captures and also pawn promotions. More pseudo legal moves concern whether or not the moving sides’ King is in check in order for the move in question to be classified as legal.
Pseudo Legal Moves
So let’s first consider the set of pseudo legal moves. We’ve chosen Java as our development language for the move generator and so we are able to create a number of multi-dimensional arrays containing all possible moves by all possible pieces from all possible squares. This involves hard coding a number of data structures (arrays) containing all possible source and destination squares for each of the piece types. By making these arrays static they only need to be instantiated once and then any board objects which may be instantiated need only look up any possible moves in this table to find a pseudo legal move without having to loop through thousands of lines of code over and over again. We only need 4 x 2 dimensional arrays and 2 x 3 dimensional arrays comprising around 3400 lines of code to do this.
The next step is to determine legal moves from the pseudo legal moves. This involves similar logic as applies to determining legal moves in the interface. Each piece type has a fairly unique set of rules which determine their behavior. As we evaluate the legal moves we add each of them to a list. In the client side interface in JavaScript we can simply concatenate a comma delimited string and then use string functions to search the string. The same string can then be easily passed to PHP when needed and split into an array. In Java we can make further use of bitboards which can be added to an ArrayList.
King Moves
Let’s consider Kings first. With the exception of a castling move (we’ll discuss castling later), Kings can only move 1 square in any direction, except of course when they are on the edge of the board. So our array of King’s pseudo legal moves needs to be re-evaluated in order to determine the legal moves that apply from any given square.
The rules that apply here, firstly concern whether or not the destination square is occupied. If it is occupied by a friendly piece then the move is not legal and is excluded from the list. If the destination square is vacant or occupied by an opposing piece then we need to test if that square is under attack. If it is under attack by the opposing side then the move is not legal as in this case it is a King and would imply a move into check. If the destination square is not under attack we can then add the move to the list of legal moves.
To determine if a square is occupied. In the interface we know the positions of all the pieces from the start which is stored in a record and updated after each move. We only need to reference this in order to determine if a square is occupied. The interface passes this start position to the servlet which generates a set of bitboards representing the state of the game. From there as mentioned earlier bitwise operators can bitwise ‘or’ and/or bitwise ‘and’ the various bitboards together in order to determine which squares are occupied by which side.
To determine if a square is under attack we need to know the set of squares that all opposing pieces can legally move to. If the square is in this set then the said square is under attack. Before we can determine this we need to have all of our rules in place and functional for all of our pieces.
Pawn Moves
Now let’s consider pawns. Pawn moves are more complex than may be initially perceived.
- Pawns can only move in one direction which is opposite for both sides.
- Pawns can move either 1 or 2 steps forward from their initial rank otherwise only 1 step forward.
- Pawns can capture 1 square diagonally forward only if that square is occupied by an opposing piece.
- Pawns attack squares 1 square diagonally forward which needs to be evaluated when considering moving a King to such a square.
- Pawns are promoted to higher ranking piece in the event that they reach the opposing side’s back rank.
- The Enpassant Pawn rule applies to any pawn moving from its initial rank when an opposing pawn occupies an adjacent file and is 2 ranks deeper when such a pawn pushes 2 ranks forward form its initial position. This rule comes into play only for the next immediate move, after which the rule is negated for that particular pawn.
- After applying the above rules to pawn moves they are only legal if the said move does not leave or place the moving sides King in check.
When moving a pawn 1 rank forward we simply take its current square and add or subtract 8 depending on whether it is a Black or White pawn. For moving 2 ranks forward which is only possible from its start rank we add or subtract 16. We also need to determine if pushing 2 steps forward is possible i.e. if the fist square is occupied then we can’t push to the second square. When capturing, providing the square is occupied by an opposing piece, we add or subtract 7 or 9 to the square (we also need to determine if the capturing pawn is on one of the outside files and then compensate accordingly). We also need to define the rules of pawn promotions, the Enpassant Pawn rule and finally if the move does not leave the moving sides king in check.
Because of the complexity of Pawn moves we encapsulate their rules into two separate functions one for black and one for white and add their attacks only to our static arrays. The attack moves are not added to our list of legal moves but only considered when moving either King into squares attacked by opposing pawns. We then need functions to calculate pawn captures which are added to the list. The rules defined in the interface in JavaScript for Pawns are very similar to that used in Autochess in Java.
Knight Moves
Knight moves are perhaps the easiest to define. Because Knights cannot be blocked their pseudo legal moves defined in our static arrays can be easily accessed in Autochess. In the interface we need functions defining their rules in a similar method we used for the pawns. We only need to determine if the destination square is vacant or occupied by an opponent’s piece other that the opponents King and that the move does not leave the moving side’s King in check.
Bishop Moves
Bishops are different again and can only move diagonally. Moreover for each side there is one bishop set on a black square and one on a white square which means that each Bishop is limited to squares of its set colour. This is not that difficult as we can define our pseudo legal moves in our static arrays for Autochess and use functions for the interface. Because bishops can move multiple squares we need to be able to determine if a square along one of its rays of movement is occupied thus canceling further movement along such a ray. We also need to apply the rules of check etc.
Rook Moves
Rooks are very similar to bishops except that they move horizontally and vertically and are not limited to a set colour square. As with bishops we also need to determine if a square along one of its rays of movement is occupied and cancel any further movement along that ray. Again we need to apply the rules of check.
Queen Moves
Queens do not need a specific set of defining rules of movement as we can apply the same sets of rules which define both bishops and rooks to the queens and also the rules of check.
Determining Check
Having briefly outlined our methods of determining legal moves for our pieces we need to be able to determine the status of check. The status of check is all important in chess and plays a major role in determining if a move is legal or not. For this we need to find which pieces are attacking the King and also which pieces would be attacking the King if they were not currently being blocked.
We can now do this by taking our functions defining the rules of movement for each of our piece types and redefining them in new functions to find specific attacks upon the King. We then in turn pass the position of the King to each function which temporarily gives the King the same rules of movement for each piece and returns a Boolean flag indicating whether or not the King is in check. At the same time we build an array of squares which contains pieces of the opposing side which are attacking the King. In JavaScript we can use strings and string functions which can be passed to PHP and split into arrays and in Java we can use bitboards.
Generally speaking we are only looking for 1 square occupied by an opposing piece which is attacking the King as any more that this would constitute an illegal position as the King would have to still be in check from a previous move for this to happen. However these functions provide further functionality when determining if the moving side is moving a piece which will leave the King in check as a result of such a move. So for this we need to determine which squares are occupied by friendly pieces which are currently blocking opposing pieces from a direct attack on the King and then cancel their moves.
We then need to determine which moves can be made which would block any current attack on the King. For this we need a set of squares which are also covered by the piece of the opposing side which is attacking the King and a set of squares which the friendly side can move to without placing the King in check and then look for the intersection of these two sets, representing squares which can be legally moved to and thus block these attacks. Finally we need to find any moves which would allow the King to escape such an attack.
In the interface whilst searching for these legal moves we can at the same time set the visible property of the green dots to ‘true’ indicating legal moves for each piece clicked on. If any legal moves for each piece can not be found we can flag a set of dots indicating the King and the opposing piece which has the King in check and flash these dots accordingly.
To briefly summarise for evaluating the status of check we need to construct strings, arrays and bitboards representing the following sets:
- Squares containing opposing pieces attacking the King.
- Squares which are occupied by friendly pieces which are currently blocking attacks upon the King.
- Squares which the attacking piece also covers in an attack upon the King.
- Squares which the friendly side can move to which would block the current attack.
- Squares which the King may move to in order to escape such an attack.
It can be seen that this is an iterative process with regards to determining legal moves. First we identify the pseudo-legal moves then semi-legal moves and finally genuine legal moves based upon the rules of check. In the same way we iteratively develop our functions which will eventually include the rules of Checkmate, Stalemate and finally rules defining Resignation and Forfeiture.
Castling Moves
Having briefly outlined how we determine our legal moves we need also to define the rules of castling and then re-integrate these rules into the rules defining our legal moves. Castling rules are often misconceived and/or partially understood or adhered to.
Let’s consider the rules of castling as they apply as follows:
- You cannot castle out of check.
- You cannot castle into check.
- You can no longer castle either side if the King has moved.
- You can no longer castle King’s side if the King’s Rook has moved or been taken.
- You can no longer castle Queen’s side if the Queen’s Rook has moved or been taken.
- You temporarily cannot castle either side unless the squares on the back rank between the King and its relevant castling side’s Rook are vacant.
- You temporarily cannot castle King’s side whilst the King’s Rook is under attack.
- You temporarily cannot castle Queen’s side whilst the Queen’s Rook is under attack.
- You temporarily cannot castle whilst the King’s fly over square on the castling side is under attack.
All of the above rules must be taken into account in order to satisfy all the rules of castling. For this we need to flag the permanent castling status in our current position as ‘true’ or ‘false’ for both players on each of their respective King’s and Queen’s sides and then to test for the temporary conditions as the case may be before a castling move can be classified as legal.
This type of move is associated with the King and not so much with the Rooks. Although it can be validated client side in JavaScript in the interface, because it involves the movements of both a King and a Rook simultaneously, it needs to be detected server side in PHP if such a move has been made so that the board’s position can be updated accordingly before the current position of the board is passed to the servlet in the case that the current player is playing against Autochess, our move generator. A similar condition occurs when Autochess is returning a castling move in which case the interface needs to be able to detect such a move and update the board’s position accordingly.
This illustrates to some degree that it is not enough to simply validate legal moves in JavaScript from the client sides’ point of view we also need to validate moves from our server sides’ point of view and also from our servlets’ point of view which resides on a separate Tomcat Server which hosts our servlet. At this point it should be noted that Autochess is also defined on the site as though it were another player but without the full status of a member or a temporary guest player.
The servlet needs to be full functionality and independent of the interface in order to calculate and evaluate a multitude of moves several ply deeper into the game if it is to return any substantial move that might be considered competitive.
Pawn Promotions
Pawns are promoted to a higher piece if they reach the opponents back rank. Having said this from the evaluation of over 1800 of Kasparov’s games played in official tournaments, which upon a pawn reaching the opponents back rank that pawn has always been promoted to a Queen. In every case the said Queen had always been previously taken.
Official rules state that there can only be one Queen of either side on the board at any one time. On this basis the rules of pawn promotion on the site stipulate that when a Pawn reaches the back rank of the opponents’ side it is automatically promoted to the highest ranking piece which has previously been taken without offering a choice. This may be changed at a later date however until so it is considered that it would have minimal impact upon the games outcome.
It stands to reason that when playing with a physical set of pieces that there are only 1 Queen, 2 Rooks, 2 Bishops, 2 Knights and 8 Pawns plus the King for each side. Excluding the Pawns and the King a pawn promotion could only possibly eventuate into one of the other piece types and providing that such a piece has already been taken then a pawn can be only be promoted to one of these pieces without the need for another set of physical pieces.
In the same way the logic inherent in the game on this site can only allow for a piece which has previously been taken to be reinstated. This is a reflection of what is possible with a physical board and also reflects through research the official rules of the game. Although it is possible that a pawn may reach the back rank without any higher ranking piece having been previously taken it is implausible that such a game be taken seriously.
To this end if a pawn were to reach the back rank under such circumstances the pawn is not promoted and thus renders it useless as it will no longer be able to move nor attack nor be promoted if a subsequent higher piece be taken later on. It is assumed that this will never happen.
Enpassant Pawns
The Enpassant rule was finally added to the game which utilises another flag similar to the castling flags. However in this case the enpassant pawn is flagged with the square that it resides on in the current position. If the enpassant pawn rule is enforced then the move is added to the set of legal moves and the flag is set back to zero after the move. The same applies in the case that if the rule is not enforced or that there was no current enpassant pawn set at in the first place.
This concludes the outline of our rules for legal moves.