For the pieces the same structure of abstract and concrete types is used that has been used before for players and displays. The abstract type $PIECE specifies the common interface. The concrete type or class PIECE is not used to create objects, but provides common implementations that are inherited by the real pieces (i.e., by classes PAWN, ROOK, KNIGHT, BISHOP, QUEEN, and KING).
type $PIECE is alive:BOOL; alive(set:BOOL); worth:INT; iswhite:BOOL; position:POS; valid_move(to:POS,board:BOARD):BOOL; update_position(position:POS); update_position(position:STR); move!(b:BOARD,to_piece:BOOL):POS; fig:CHAR; ispawn : BOOL; isrook : BOOL; isking : BOOL; end; -- of type $PIECE
class PIECE < $PIECE is -- General constants that are used throughout the descendants of $PIECE const white : BOOL := true; const black : BOOL := ~white; const ordinary : BOOL := false; const for_check_test : BOOL := true; -- alters behavior of move! -- Attributes that are specific to a PIECE attr alive : BOOL; attr iswhite : BOOL; attr position : POS; -- Constants that are specific to a PIECE const worth : INT := 0; const fig : CHAR := ' '; const ispawn : BOOL := false; const isking : BOOL := false; const isrook : BOOL := false; create(pos:POS,iswhite:BOOL):SAME is ret ::= new; ret.position := #POS; ret.position.pos := pos.str; ret.iswhite := iswhite; ret.alive := true; return ret; end; private same_color(b:BOARD,p:POS):BOOL pre b.has_piece(p) is white_piece_on_pos :BOOL:= b.has_white_piece(p); if ( iswhite and white_piece_on_pos) or (~iswhite and ~white_piece_on_pos) then return true; else return false; end; end;
The following routine valid_move checks whether a given move is valid for a given board situation This is done as follows. For the from position, all valid moves are generated by calling the iter move! in line 53. It is then checked, whether the given move is in the returned set of valid moves.
valid_move(to:POS,board:BOARD):BOOL is ret : BOOL := false; loop valid_to::=move!(board,ordinary); if to=valid_to then ret:=true; break!; end; end; return ret; end; update_position(p:POS) is position.pos:=p.str; end; update_position(pos:STR) is position.pos:=pos; end; move!(b:BOARD,mode:BOOL):POS is raise "PIECE:dummy code (move!) called"; end; end; -- of class PIECE
First, constants are redefined that have values which differ from those given in the PIECE implementation. The iter move! returns all valid moves given a board with other pieces. The outer loop (lines 75-86) will check the following directions: diag_up_right, diag_up_left, diag_dn_right, and diag_dn_left. In the inner loop (lines 76-85) all positions are computed a piece could reach in a direction set by the outer loop. A position returned by way! in line 76 is valid as long as there is no other piece occupying that position.
If there is another piece on the position returned by way! this cannot be a piece of the same color. However, for a check-test, the occupied position is checked by the moving piece.
class BISHOP < $PIECE is include PIECE; -- Constants that are different from PIECE implementation: const worth : INT := 3; const fig : CHAR := 'B'; move!(b:BOARD,mode:BOOL):POS is to : POS; loop direction::=POS::diag_up_right.upto!(POS::diag_dn_left); loop to:=position.way!(direction); if ~b.has_piece(to) then yield to; elsif same_color(b,to) and mode=ordinary then break!; else yield to; break! end; end; end; end; -- of move! end; -- of class BISHOP
The implementation of class ROOK is very similar to the code of BISHOP.
class ROOK < $PIECE is include PIECE; -- Constants that are different from PIECE implementation: const worth : INT := 5; const fig : CHAR := 'R'; const isrook : BOOL := true; move!(b:BOARD,mode:BOOL):POS is -- returns all valid moves given a board with other pieces to : POS; -- This loop will check the following directions: -- horizontal_right, horizontal_left, vertical_up, vertical_dn loop direction::=POS::horizontal_right.upto!(POS::vertical_dn); loop to:=position.way!(direction); if ~b.has_piece(to) then yield to; elsif same_color(b,to) and mode=ordinary then break!; else yield to; break! end; end; end; end; -- of move! end; -- of class ROOK
The implementation of class QUEEN is very similar to the code of BISHOP.
class QUEEN < $PIECE is include PIECE; -- Constants that are different from PIECE implementation: const worth : INT := 9; const fig : CHAR := 'Q'; move!(b:BOARD,mode:BOOL):POS is -- returns all valid moves given a board with other pieces to : POS; -- This loop will check the following directions: -- diag_up_right, diag_up_left, diag_dn_right, diag_dn_left -- horizontal_right, horizontal_left, vertical_up, vertical_dn -- It is a combination of rook and bishop. loop direction::=POS::diag_up_right.upto!(POS::vertical_dn); loop to:=position.way!(direction); if ~b.has_piece(to) then yield to; elsif same_color(b,to) and mode = ordinary then break!; else yield to; break! end; end; end; end; -- of move! end; -- of class QUEEN
The body of the loop is slightly different to the one used for ROOK, BISHOP and QUEEN. Above, the inner loop terminated as soon as a position was encountered that was blocked by another piece. For KNIGHT (and later on for KING) all potential position have to be considered.
class KNIGHT < $PIECE is include PIECE; -- Constants that are different from PIECE implementation: const worth : INT := 3; const fig : CHAR := 'N'; move!(b:BOARD,mode:BOOL):POS is -- returns all valid moves given a board with other pieces to : POS; loop to:=position.way!(POS::knight); if b.has_piece(to) and same_color(b,to) and mode = ordinary then -- skip this move else yield to; end; end; end; -- of move! end; -- of class KNIGHT
The iter move! is different for the pawns: In ordinary mode, straight moves, diagonal moves and ``en passant" moves must be considered. In check_test mode, straight moves are irrelevant. The implementation of move! is divided in two sections by an if statement. In the then branch (line 164-215) the potential moves of white pawns are computed. The else branch (lines 216-267) is devoted to the black pawns.
class PAWN < $PIECE is include PIECE; -- Constants that are different from PIECE implementation: const worth : INT := 1; const fig : CHAR := 'P'; const ispawn : BOOL := true; move!(b:BOARD,mode:BOOL):POS is -- returns all valid moves given a board with other pieces to : POS; if iswhite then if mode = ordinary then -- vertical steps loop to:=position.way!(POS::north_two); if b.has_piece(to) then -- position and continued way blocked break!; end; yield to; end; end; -- diag_up if position.column < 'h' then to:=#POS; to.pos := position.northeast; if mode = for_check_test then yield to; else if b.has_black_piece(to) then yield to; end; end; end; -- diag_dn if position.column > 'a' then to:=#POS; to.pos := position.northwest; if mode = for_check_test then yield to; else if b.has_black_piece(to) then yield to; end; end; end; -- en passant if position.row = '5' and ~void(b.last_move) and b.last_move.from.row = '7' and ( b.last_move.to = position.east or b.last_move.to = position.west) and ~void(b.last_move.piece) and b.last_move.piece.ispawn then if mode = for_check_test then yield b.last_move.to; else to := #POS; to.pos := b.last_move.to.north; yield to; end; end; -- no more moves; quit; else -- i.e. if isblack if mode = ordinary then -- vertical steps loop to:=position.way!(POS::south_two); if b.has_piece(to) then -- position and continued way blocked break!; end; yield to; end; end; -- diag_up if position.column > 'a' then to:=#POS; to.pos := position.southwest; if mode = for_check_test then yield to; else if b.has_white_piece(to) then yield to; end; end; end; -- diag_dn if position.column< 'h' then to:=#POS; to.pos := position.southeast; if mode = for_check_test then yield to; else if b.has_white_piece(to) then yield to; end; end; end; -- en passant if position.row = '4' and ~void(b.last_move) and b.last_move.from.row = '2' and ( b.last_move.to = position.east or b.last_move.to = position.west) and ~void(b.last_move.piece) and b.last_move.piece.ispawn then if mode = for_check_test then yield b.last_move.to; else to := #POS; to.pos := b.last_move.to.south; yield to; end; end; quit; end; end; -- of move! end; -- of class PAWN
In the iter move! of the KING up to 8 neighboring positions have to be analyzed. As usual, this is done by using the way! iter provided by the POS class. Furthermore, the king might be able to do a castle move. If the preconditions of castle moves are fulfilled, the new position of the king is yield. Castle moves are analyzed separately for the white king in lines 290-321 and for the black king in lines 322-352.
class KING < $PIECE is include PIECE; -- Constants that are different from PIECE implementation: const worth : INT := 100; -- compared to the worth of other pieces -- the king has an infinite worth const fig : CHAR := 'K'; const isking : BOOL := true; move!(b:BOARD,mode:BOOL):POS is -- returns all valid moves given a board with other pieces to : POS; loop to:=position.way!(POS::ring); if b.has_piece(to) and same_color(b,to) and mode = ordinary then -- skip this move else if mode = for_check_test or ~b.pos_in_check(to) then yield to; end; end; end; -- castle moves spot1, spot2, spot3, rook : $PIECE; if b.white_to_play and ~b.white_K_moved and position = "e1" then -- q-castle spot1:= b.piece("d1"); spot2:= b.piece("c1"); spot3:= b.piece("b1"); rook := b.piece("a1"); if ~void(rook) and rook.isrook and rook.alive and void(spot1) and void(spot2) and void(spot3) then to := #POS; to.pos := "d1"; if ~b.pos_in_check(to) then to.pos := "c1"; if ~b.pos_in_check(to) then yield to; end; end; end; -- k-castle spot1:= b.piece("f1"); spot2:= b.piece("g1"); rook := b.piece("h1"); if ~void(rook) and rook.isrook and rook.alive and void(spot1) and void(spot2) then to := #POS; to.pos := "f1"; if ~b.pos_in_check(to) then to.pos := "g1"; if ~b.pos_in_check(to) then yield to; end; end; end; end; -- castle moves of white king if ~b.white_to_play and ~b.black_K_moved and position = "e8" then -- q-castle spot1:= b.piece("d8"); spot2:= b.piece("c8"); spot3:= b.piece("b8"); rook := b.piece("a8"); if ~void(rook) and rook.isrook and rook.alive and void(spot1) and void(spot2) and void(spot3) then to := #POS; to.pos := "d8"; if ~b.pos_in_check(to) then to.pos := "c8"; if ~b.pos_in_check(to) then yield to; end; end; end; -- k-castle spot1:= b.piece("f8"); spot2:= b.piece("g8"); rook := b.piece("h8"); if ~void(rook) and rook.isrook and rook.alive and void(spot1) and void(spot2) then to := #POS; to.pos := "f8"; if ~b.pos_in_check(to) then to.pos := "g8"; if ~b.pos_in_check(to) then yield to; end; end; end; end; -- castle move of black king end; -- of move! end; -- of class KING