Next: Type $PLAYERand Related Up: Sather 1.0 Tutorial Previous: Class Main

Type $CHESS_DISPLAYand Related Classes

Type $CHESS_DISPLAY

Sather differentiates between concrete types and abstract types. In Sather each object has a unique concrete type that determines the operations that may be performed on it. Classes define concrete types and give implementations for the operations. Abstract types however, only specify a set of operations without providing an implementation. This set of operations is called the interface of the type. An abstract type corresponds to a set of concrete types which obey that interface.

$CHESS_DISPLAY is an abstract type. Names of abstract types must be in capital letters. The leading $ differentiates abstract from concrete types.

In the body of the type declaration (lines 2-14), the operations are given without any implementation. Formal parameters must have names. However, since these are not used, the names serve only documentary purposes.

For example, consider the case where you want to have a simple integer variable in all concrete types/classes that are subtypes of an abstract type. An integer attribute a has two implicit routines, a reader which has the signature a:INT and a writer with the signature a(new_value:INT). Since the abstract type hides implementation details from the interface, one has to put both signatures in the body of the type. This gives room for changing the implementation of a in the classes. (In the abstract type below, there are however no attributes.)


type $CHESS_DISPLAY is

  -- Display the state of the board
  redraw(board:ARRAY{CHAR});
  update(board:ARRAY{CHAR});
  showmove(text:STR);

  -- Inform player about certain conditions
  invalid_move;
  thinking(white_to_move:BOOL);
  king_check(white_to_move:BOOL);
  
  -- Interact with the player
  getmove(white_to_move:BOOL):MOVE;
  ask_pawn_xchg:CHAR;

  -- Close   
  close;

end; -- of abstract type $CHESS_DISPLAY

The string interface (ARRAY{CHAR}) to board needs some explanation: The board is represented by 64 characters. Each character specifies the piece on a particular position of the board.

Capital characters represent white pieces, small characters stand for black pieces. The first character in board specifies board position ``a1", the last ``h8".

All concrete classes that are subtype of $CHESS_DISPLAY must at least have all the above routines (or implicitly declared routines.)

Class CHESS_DISPLAY

This is a concrete type or class which is a subtype of $CHESS_DISPLAY. The subtype relation is expressed by the < symbol in line 16. This concrete class however will not be used to instantiate objects, i.e., there will be no objects of type CHESS_DISPLAY. The main purpose of this class is to declare attributes and routines that are common to other classes of type $CHESS_DISPLAY, which include the implementation of this class. Hence, whereas $CHESS_DISPLAY is used to express the subtype relation, the class CHESS_DISPLAY is used for code inheritance.

The first two routines are included unchanged in ASCII_DISPLAY and replaced in X_DISPLAY.

A create routine has to be provided if objects of that concrete type are created. SAME denotes the type of the class in which it appears. As explained in ASCII_DISPLAY below, it is a good idea to return SAME instead of CHESS_DISPLAY, if the create routine is meant to be included.

The expression new is used in line 18 to allocate space for (reference) objects (and may only appear in reference classes.) New returns a (reference) object of type SAME. All attributes and array elements are initialized to void.


class CHESS_DISPLAY < $CHESS_DISPLAY is

  create:SAME is
    return new;
  end;

  update(board:ARRAY{CHAR}) is
    redraw(board);
  end;

The following two routines do not provide a basic implementation. However, for consistency with the interface required by $CHESS_DISPLAY, they have to exist. When the code of class CHESS_DISPLAY is included, special implementations of redraw and getmove must be provided that replace the dummies given here.

To make sure that these implementations of redraw and getmove are not called erroneously, an exception is raised by the raise statement (lines 24 and 27). Since redraw does not have a return parameter, the body of the routine could have been empty. In getmove either a return or a raise is required because getmove has a return parameter.

  

  redraw(board:ARRAY{CHAR}) is
    raise "INTERFACE: invalid call to redraw\n";
  end;

  getmove(white_to_move:BOOL):MOVE is
    raise "INTERFACE: invalid call to getmove\n";
  end;

The following four routines provide code that is meant to be included unchanged in other implementations of classes that are subtypes of $CHESS_DISPLAY. Each of the four routines makes use of a private routine showtext which is not completely coded here. Classes that include the implementation of CHESS_DISPLAY must provide complete implementations of showtext.


  invalid_move is
    text : STR;
    text := "ERROR: Invalid move....try again";
    showtext(text);
  end;

  thinking(white_to_move:BOOL) is
    text : STR;
    if white_to_move then
      text := "White";
    else
      text := "Black";
    end;
    text := text + " is thinking ... please wait ...";
    showtext(text);
  end; -- of thinking

  king_check(white_to_move:BOOL) is
    text : STR;
    if white_to_move then
      text := "--> White";
    else
      text := "--> Black";
    end;
    text := text + " is in check!";
    showtext(text);
  end; -- of king_check

  showmove(text:STR) is
    showtext(text);
  end;

A routine declared private can only be called from code that is in the same class as the routine.


  private showtext(text:STR) is
    -- Optional protection against implementation errors
    raise "INTERFACE: invalid call to showtext\n";
  end;

The following routine ask_pawn_xchg is included in both ASCII_DISPLAY and X_DISPLAY without change. The loop (line 64-72) is not terminated by means of an iter. Instead, the termination is done by the return statement in line 69.

In line 66 is an example of user input. The class IN is specified in the file Library/in.sa. Among others, IN provides a routine get_str that accepts a string input from the use via the standard I/O-device. Calls like CLASS::<routine> do not refer to a particular object of the class but call the routine on a void object.


  ask_pawn_xchg:CHAR is
    newpiece : STR;
    ret : CHAR;
    loop
      #OUT+"Do you prefer a QUEEN or a KNIGHT?\n";
      newpiece := IN::get_str.upper;
      ret := newpiece.char(0);
      if ret = 'Q' or ret = 'K' then
        return ret;
      end;
      #OUT+"Please enter QUEEN or KNIGHT.\n"
    end;
  end; -- of ask_pawn_xchg

  -- The following routine is included unchanged in ASCII_DISPLAY
  -- and replaced in X_DISPLAY.

  close is
  end;

end; -- of CHESS_DISPLAY

Class ASCII_DISPLAY

This concrete class is a subtype of $CHESS_DISPLAY. It provides an implementation for at least the signatures given in the specification of $CHESS_DISPLAY.

ASCII_DISPLAY inherits the implementation of class CHESS_DISPLAY by the include statement. The include statement is semantically equivalent to the following editor operation: replace the include statement by the implementation code of the included class. (Includes have to be resolved recursively.)

Without code duplication, ASCII_DISPLAY inherits the implementation of the following routines, at the include statement.


         create:SAME
         redraw(board:ARRAY{CHAR})  --> is replaced below
         update(board:ARRAY{CHAR})
         getmove(white_to_move:BOOL):MOVE  --> is replaced below
         invalid_move
         thinking(white_to_move:BOOL)
         king_check(white_to_move:BOOL)
         showmove(text:STR)
         private showtext  --> is replaced below
         ask_pawn_xchg:CHAR
         close
Only the routines marked with ``->" are replaced by a specific implementation. To make the idea of textual inclusion even more understandable consider the included version of create.

        create:SAME;
Although originally written in CHESS_DISPLAY, the routine create does not return an object of type CHESS_DISPLAY after being included in ASCII_DISPLAY. Instead, create returns an object of type ASCII_DISPLAY.

  
class ASCII_DISPLAY < $CHESS_DISPLAY is

  include CHESS_DISPLAY;

Redrawing the board on the ASCII_DISPLAY is an excellent example of two nested loops, both of which are governed by iters (lines 88-91 and lines 87-89).

The iter downto! in line 85 is another iter from the INT class, which can be found in file Library/int.sa. As expected, 7.downto(0) iteratively returns the integer value 7, 6, 5, ..., 0 and with the next call terminates the surrounding loop, i.e., the loop from line 85 to line 91.

The iter step! in line 87 is just another iter the INT class provides. Beginning at the integer it is called upon, it will return as many integers as indicated by its first argument. The difference between two subsequent return values is given by the second argument. If step! is called for the ninth time, it will quit and immediately terminate the surrounding loop (line 87-89). Note, that for the two nested loops, only the innermost loop is terminated.

                
  redraw(board:ARRAY{CHAR}) is
    #OUT+"The current board: (small characters = black pieces)\n";
    #OUT+"   a  b  c  d  e  f  g  h \n";
    #OUT+"  ------------------------\n";
    
    loop i::=7.downto!(0);

      #OUT+(i+1)+"|";

      loop j::=(8*i).step!(8,1);
        
        #OUT+" "+board[j]+" "
      end;

      #OUT+"|"+(i+1)+"\n";
    end;
    #OUT+"  ------------------------\n";
    #OUT+"   a  b  c  d  e  f  g  h \n";
  end; -- of redraw

The following OUT::flush in line 106 tells the OUT class, that all characters that are buffered should be output immediately. Normally, the buffer is only flushed, if a \n is seen in the character stream.


  getmove(white_to_move:BOOL):MOVE is
    move : MOVE;
    move_str : STR;
    loop
      #OUT+"Please enter a move for";
      if white_to_move then
        #OUT+" white: ";
      else
        #OUT+" black: ";
      end;
      #OUT+"(e.g. d2-d3 or help) ";

      OUT::flush;
      move_str := IN::get_str.lower;

      -- The string class provides a routine head(x), which returns the first
      -- x characters of a string.

      if move_str.size >= 4 and move_str.head(4) = "help" then
        #OUT+"Valid moves are:\n";
        #OUT+"  ordinary move: d2-d3\n";
        #OUT+"  king castle  : o-o\n";
        #OUT+"  queen castle : o-o-o\n";
        #OUT+"  quit         : quit\n";
      else
        move := #MOVE(move_str, white_to_move);

        -- If the create routine of MOVE could not correctly deal with
        -- the given move_str move.isok returns false. If a move turns
        -- out not to be quit or ok, the player is asked to try again.
        
        until! (move.isquit or move.isok);
        #OUT+"ERROR: Invalid syntax....try again\n";
      end;
    end;
    return move;
  end; -- of getmove

  private showtext(text:STR) is
    #OUT+text+"\n";
  end;

end; -- of ASCII_DISPLAY

Class X_DISPLAY

The following code is kept in a separate Sather code file (XInterf.sa). There the class X_DISPLAY is implemented. The implementation is in a different file, to show how spreading of source code across several files works in Sather.

This concrete class is a subtype of $CHESS_DISPLAY. It provides an implementation for at least the signatures given in the specification of $CHESS_DISPLAY.

Due to the include statement, X_DISPLAY inherits the implementation of CHESS_DISPLAY in then same way as ASCII_DISPLAY has done before. Without code duplication, X_DISPLAY now has


         create:SAME  --> is replaced below
         redraw(board:ARRAY{CHAR})  -->* is replaced below 
         update(board:ARRAY{CHAR})  --> is replaced below
         getmove(white_to_move:BOOL):MOVE  -->* is replaced below
         invalid_move
         thinking(white_to_move:BOOL)
         king_check(white_to_move:BOOL)
         showmove(text:STR)
         private showtext  -->* is replaced below
         ask_pawn_xchg:CHAR
         close  --> is replaced below
Only the routines marked with ``->" are replaced by a specific implementation. The arrows marked with * indicate those routines that have been replaced in the ASCII_DISPLAY explained above.

The implementation of X_DISPLAY makes heavy use of the external Chess Window (XCW) implementation. The Sather compiler is informed about the existence of the external routines in the external class XCW which is explained on page .


class X_DISPLAY < $CHESS_DISPLAY is  
  
  include CHESS_DISPLAY;

  create:SAME is
    XCW::OpenCW("Sather Tutorial Chess");
    return new;
  end;

  redraw(board:ARRAY{CHAR}) is
    XCW::RedrawCW(board);
  end;

  update(board:ARRAY{CHAR}) is
    XCW::UpdateCW(board);
  end;
  
  showmove(text:STR) is
    XCW::ShowMoveCW(text);
  end;
  
  private showtext(text:STR) is
    XCW::TextCW(text);
  end;  
    
  close is
    XCW::CloseCW;
  end;

The implementation of getmove is slightly more complicated. The external Chess Window implementation has a routine called GetMoveInCW. This routine has an array of characters as formal parameter. This array is kept in the variable move_chars. To pass the result to the create routine of class MOVE in line 36, it must be converted into a string. The latter is stored in the variable move_str.

Several library routines are helpful here. In line 35 routine to_val of class ARRAY{T} is used to set each array element to the given value. The loop in lines 39-41 iteratively adds characters of move_char to the string variable move_str. The iter elt! returns all array elements in order and quits at the end of the array, hence terminating the loop. Note, how elegantly both loop control and work can be combined by use of iters.


    
  getmove(white_to_move:BOOL):MOVE is
    text : STR;
    text := "Please move a";
    if white_to_move then
      text := text+" white"
    else
      text := text+" black";
    end;
    text := text+" piece.";
    XCW::TextCW(text);
    
    move_chars ::= #ARRAY{CHAR}(5);  -- create a character array with 5 chars.
    move_str   ::= #STR;             -- create a string.
    move       : MOVE;
    
    move_chars.to_val(' ');          -- set all 5 chars to ' '
    XCW::GetMoveInCW(move_chars);
    
    -- Construct string out of char array. The iter elt! returns all 5
    -- characters of move_chars, then quits and terminates the loop.
    loop
      move_str := move_str+move_chars.elt!;
    end;
    
    -- Since XCW::GetMoveInCW is guaranteed to return only
    -- syntactically correct moves, no further plausibility tests
    -- are required.
    
    move := #MOVE(move_str.lower,white_to_move);
    return move;
  end; -- of getmove

end; -- of X_DISPLAY

External Class XCW

XCW provides an X Window interface for chess. The corresponding C code can be found in XCW.c. The routines are used by the implementation of X_DISPLAY.

In this external class definition the interface to routines of XCW.c are specified. The main purpose of this class is to tell the Sather compiler the names and parameters of routines that can be called. The syntax for a call is XCW::<routine_call>.



external class XCW is
  OpenCW(title:STR);
  RedrawCW(board:ARRAY{CHAR});
  UpdateCW(board:ARRAY{CHAR});
  GetMoveInCW(move:ARRAY{CHAR});
  ShowMoveCW(move:STR);
  TextCW(text:STR);
  CloseCW;
end;

Each external class is typically associated with an object file compiled from a language like C or Fortran. External classes do not support subtyping, implementation inheritance, or overloading. External classes bodies consist of a list of routine definitions. Routines with no body specify the interface for Sather code to call external code. Routines with a body specify the interface for external code to call Sather code.

Each routine name without a body may only appear once in any external class and the corresponding external object file must provide a conforming function definition. Sather code may call these external routines using a class call expression of the form EXT_CLASS::ext_rout(5). External code may refer to an external routine with a body by concatenating the class name, an underscore, and the routine name (e.g., EXT_CLASS_sather_rout).

Only a restricted set of types are allowed for the arguments and return values of these calls. The built-in value types BOOL, CHAR, INT, FLT, FLTD, FLTX, and FLTDX are allowed anywhere and on each machine have the format supported by the C compiler used to compile Sather for that machine.

Moreover, arrays of the above basic types (except BOOL) can be passed as routine parameters. When a Sather program calls such a routine, the external routine is passed a pointer into just the array portion of the object. The external routine may modify the contents of this array portion, but must not store the pointer. There is no guarantee that the pointer will remain valid after the external routine returns.

Class DEFAULT

One of the design decisions of Sather Tutorial Chess has been to provide both an ASCII interface and an interface to the X Window system. To represent that in the code, there are two implementations of a class called DEFAULT. The first implementation which is in the file DefaultX.sa, can handle both an interface to X and to the ASCII terminal:


class DEFAULT is

  display(d:CHAR):$CHESS_DISPLAY is
    ret : $CHESS_DISPLAY;
    if d = 'X' then
      -- Create an object of type X_DISPLAY and return it.
      -- To be more specific: # is a short-hand for a call to
      -- the the routine create of type that follows the #.

      ret := #X_DISPLAY;
    else
      ret := #ASCII_DISPLAY;
    end;
    return ret;
  end;      
  
end;

Depending on the value of d either an object of type X_DISPLAY or of type ASCII_DISPLAY is returned to the caller. The call can be found in line 73 of the setup routine of class MAIN, see page .

If X is not available, the following implementation which is kept in Sather code file DefaultA.sa, is used instead:


class DEFAULT is
  
  display(d:CHAR):$CHESS_DISPLAY is
    ret : $CHESS_DISPLAY;
    -- Since X is not available, create ASCII-Interface only.
    ret  := #ASCII_DISPLAY;
    return ret;
  end;      
  
end;

The value of d is ignored here. In either case, an ASCII display is created and returned to the caller. Since no reference to class X_DISPLAY is in the code, the Sather compiler ignores any implementation of that class. The Makefile makes the dependencies visible.



Next: Type $PLAYERand Related Up: Sather 1.0 Tutorial Previous: Class Main


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