Next: Type $CHESS_DISPLAYand Related Up: Sather 1.0 Tutorial Previous: Sather Tutorial Chess

Class Main

The class MAIN has a special purpose in Sather. Unless altered by compiler flags, the routine main of MAIN is started when a compiled Sather program is invoked by the user. Class names must be in capital letters.

Although it is possible, it is unusual to create objects of class MAIN. Therefore, attributes should be declared shared. Shared attributes of a class exist and can be accessed even if no objects are created. Above that, shared attributes are globally accessible by all objects of a given type.

Here we declare shared variables that can hold pointers to the chess board, the display object, and to the players. The variable board can hold an object of type BOARD, which is specified by the implementation of class BOARD, see section 8 for details. The other four variables can hold objects of the abstract type $CHESS_DISPLAY or $PLAYER, respectively. These objects can be created by classes that are explicitly declared to be subtypes of the abstract types. The difference between classes and abstract types that is visible here by the use of the $ symbol in the type identifiers and will be explained in more detail in section 4.


class MAIN is
  shared board : BOARD;
  shared display : $CHESS_DISPLAY;
  shared white, black, player : $PLAYER;

This is a good point to introduce Sather's ubiquitous basic data types. Upon declaration of basic types, these are initialized automatically.

There are more basic data types. Since these are irrelevant for this Tutorial, the interested reader is referred to the manual [].

Sather distinguishes between reference objects and value objects. (Other types of objects are not mentioned in this tutorial.) Experienced C programmers immediately catch the difference when told about the internal representation: Value types are C structs and reference types are pointers to structs. Because of that difference, reference objects can be referred to from more than one variable. Value objects can not. The basic types mentioned above (except arrays) are value classes. Reference objects must be explicitly allocated with new. Variables have the value void until an object is assigned to them. Void for reference objects is similar to a void pointer in C. Void for value objects means that a predefined value is assigned (0 for INT*, \0 for CHAR, false for BOOL, 0.0 for FLT*). Accessing a void value object will always work. Accessing a void reference object usually will be a fatal error.

There are some more differences between value types and reference types but they are beyond the scope of this tutorial.

Routine main

The routine main of MAIN is started when Sather Tutorial Chess is invoked. Similar to C, the parameter args returns the command line which is used to invoke the program. If main is declared without parameters, the command line and any arguments are ignored. Since the routine main is declared to return an integer, this will specify the exit code of the program when it finishes execution. If main is declared without return parameter, no exit code will be returned.


  main(args:ARRAY{STR}):INT is
    if ~setup(args) then  -- ~ is the boolean NOT
      -- If the given command line arguments are not acceptable, setup
      -- returns false. Then the program terminates and returns -1.
      return -1;
    end;

After invocation, the routine setup analyzes the given command line arguments. It returns true if the given parameters are acceptable and false otherwise. If acceptable, setup has some side effects: it creates objects for the players, for display, and for board. Later on these objects are accessible via the variables declared in lines 2-4.

If setup had returned true, the board, the display, and the players have been created when execution reaches line 11 where the game starts. The game is essentially a loop (lines 11-32) in which the current player is asked to enter/generate a move. The result is then assigned to the implicitly declared local variable move (line 12). The type of move is derived from the return type of player.getmove because of ``::=". The type could also have been specified explicitly as follows:


                 move : MOVE := player.getmove(board);

Another way could be to declare the variable first and then assign in a second statement:


                 move : MOVE;
                 move := player.getmove(board);

The scope of move is defined by the surrounding block, i.e., the loop statement.

Later we will find out that player.getmove is a dispatched call. But let's skip this for now.

The loop is terminated if the move is a quit. The test occurs in line 13 in the until! expression, which is a call to a special iter: each time until! is called, the given boolean expression is evaluated. If false, until! ``quits" which breaks the immediately surrounding loop, i.e., terminates the game.

If the program flow reaches the statement after until! the latter did not terminate the loop. Since some move has been returned from player.getmove it must be checked and applied to the board. This is done in line 14 by the routine check_n_apply_move which returns false if the move could not be applied properly.

After application of the move to the board in line 15, the display object is called to update the view of the board.

Later we will find out that the calls to display.update in line 15, to display.king_check() in line 25, to displayvalid_move in line 30, and to display.close in line 35 all are dispatched calls. But again, let's skip this for now.


    loop
      move ::= player.getmove(board);
      until!(move.isquit);

      if board.check_n_apply_move(move) then
        display.update(board.str);
        -- Set player to the next player
        if board.white_to_play then
          player := white;
        else
          player := black;
        end;
        -- Find out whether the king of the current player is in
        -- check. If so, have the display talk about the situation.
        if board.my_king_isin_check then
          display.king_check(board.white_to_play);
        end
      else
        -- The move was invalid. Display this. By not changing
        -- the current player, the same player is asked to try again.
        display.invalid_move;
      end;
    end; -- of loop
    -- The game is over, since the current player issued a "quit-move".
    -- Close the display.
    display.close;
    return 0;
  end;

Routine setup

This setup routine gets the command line arguments and returns a BOOL. The return value of setup is true, iff the parameters have been acceptable.

To start Sather Tutorial Chess use:


         SChess  [<white> <black>] [<Displ>]
                  <white> can be either H for Human Player
                                 or     C for Computer Player
                  <black> dito
                  <Displ> can be either X for X Interface
                                 or     A for ASCII Terminal
         The default behavior is SChess H C X

The type of the args parameter, ARRAY{STR}, is an instantiation of the parameterized basic type ARRAY{T}. The source code can be found in file Library/array.sa. An c of type ARRAY{T} stores elements of type T. If c is not void, the first element can be accessed by c[0]. c.size returns the number of elements stored in the array. c[c.size-1] accesses the last element.

  
  setup(args:ARRAY{STR}):BOOL is
  
    -- set defaults
    ret  : BOOL := true;  -- the default is that the parameters are ok
    p    ::= #ARRAY{CHAR}(2);
    p[0] := 'H';          -- default: human player
    p[1] := 'C';          -- default: computer player
    d    : CHAR := 'X';   -- type of display

First of all, setup creates a few variables that will hold the result of the evaluation of the command line arguments. A novelty is in line 41, where p is declared to be a character array and space is allocated for it. The array is created and initialized by calling the create routine of the class ARRAY. The #symbols is syntactic sugar for calls of create routines. If the create routine need additional arguments, they must be supplied behind the #symbol. Here the array has two characters which can be accessed as p[0] and p[1].

In the following code segment, the arguments get processed in a loop (lines 47-65). The first argument, args[0] is left out, since this contains the name of the running program. Here, loop termination is implemented in line 47 by the use of the iter upto! which is declared in the INT library. (The INT class is implemented in the file Library/int.sa.) The iter upto! returns an integer value each time it is called. Here the first call will return 1, the argument specifies the upper bound. In the second call upto! will return 2, then 3, , and finally args.size-1. The next call will quit the iter and terminate the immediately surrounding loop, i.e., program execution will continue in line 72.

For analysis of single parameters we use routines, provided by the STR class. The string class, which is implemented in the file Library/str.sa offers a routine char(int) that returns the character with the specified number. Since strings are arrays of characters, the first character of a string can be accessed by char(0). The character class which is implemented in the file Library/char.sa has routines upper and lower that return an upper case or lower case version of the character they are called upon. The routine head(k) returns the first k characters of a string.


    if args.size > 1 and args.size <= 4 then
      player_cnt : INT := 0;
      loop i::=1.upto!(args.size-1);
        if args[i].size >= 4 and args[i].head(4).lower="help" then
          ret := false;
        end;
        tmp : CHAR := args[i].char(0).upper;

        case tmp
        when 'A', 'X' then   -- ASCII- or X-Display if available
          d := tmp;
        when 'H', 'C' then   -- Human or Computer player
          if player_cnt < 2 then
            p[player_cnt] := tmp;
            player_cnt := player_cnt + 1;
          else
            ret := false;
          end;
        else
          ret := false;
        end;
      end; -- of loop

    elsif args.size /= 1 then   -- not equal
      -- The parameters are not acceptable.
      ret := false;
    else
      -- use defaults. The else could have been omitted.
    end;

Boolean expressions are evaluated with short-circuit semantics. For an and this means that the second operand is only evaluated if the first operand was true. For an or the second operand is evaluated if the first one was false. Lines 45 and 48 are good examples.

Sather's case statement (lines 52-64) is used for processing the command line parameters other than ``help". The variable tmp is evaluated and depending on the result, the first matching when branch is taken. Note, that multiple expressions can be given for comparison in each branch.

Depending on the analysis of the command line arguments either all global objects needed for the chess program are created in lines 79-88 or the user is informed about the correct parameter syntax in lines 90-96. The Output class OUT is defined in file Library/out.sa. The idea of using the class is to create an output object and ``add" the things that should be output to this object. The plus is overloaded so that all basic types can be output in this fashion. As usual, \n indicates a carriage return/line feed.


    
    if ret then
      display := DEFAULT::display(d);  -- Creates Display object. Described below.
      board   := #BOARD;
      if p[0] = 'H' then
        -- An object of type HUMAN is created. In contrast to BOARD, 
        -- this object has a special create routine, that needs an argument.
        white   := #HUMAN(board.white_to_play);
      else
        white   := #MINMAX(board.white_to_play);
      end;
      if p[1] = 'H' then
        black   := #HUMAN(~board.white_to_play);
      else
        black   := #MINMAX(~board.white_to_play);
      end;

      -- the first player is White
      player  := white;

    else

      #OUT+"To start Sather Tutorial Chess use: \n";
      #OUT+"args[0] [<white> <black>] [<Displ>]\n";
      #OUT+"         <white> can be either H for Human Player\n";
      #OUT+"                        or     C for Computer Player\n";
      #OUT+"         <black> dito\n";
      #OUT+"         <Displ> can be either X for X Interface\n";
      #OUT+"                        or     A for ASCII Terminal\n";
    end;

    -- Since setup has a return parameter, a result
    -- has to be returned to the caller.

    return ret;
  end;  -- of setup

end; -- of class MAIN



Next: Type $CHESS_DISPLAYand Related Up: Sather 1.0 Tutorial Previous: Sather Tutorial Chess


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