Next: Suggested Execises Up: Sather 1.0 Tutorial Previous: Class BOARD

Type $PIECEand Related Classes

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



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


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

Class BISHOP

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

Class ROOK

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

Class QUEEN

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

Class KNIGHT

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

Class PAWN

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

Class KING

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



Next: Suggested Execises Up: Sather 1.0 Tutorial Previous: Class BOARD


gomes@ICSI.Berkeley.EDU
Tue May 30 21:15:13 PDT 1995