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