Sather Programming Tutorial Guide
Sather Programming Tutorial Guide
1947 Center St. Suite 600 Berkeley, California 94704-1198 (510) 643-9153 FAX (510) 643-7684 I
Sather 1.0 Tutorial
Michael Philippsen
phlipp @ [Link]
TR-94-062
Version 0.1, December 1994
Abstract
This document provides basic information on how to obtain your copy of the Sather 1.0
system and gives several pointers to articles discussing Sather 1.0 in more detail.
We thoroughly describe the implementation of a basic chess program. By carefully
reading this document and the discussed example program, you will learn enough
about Sather 1.0 to start programming in Sather 1.0 yourself. This document is
intended for programmers familiar with object oriented languages such as Ei el or
C++. General information on object oriented programming can be found in 5].
The main features of Sather 1.0 are explained in detail: we cover the di erence
between subtyping and implementation inheritance and explain the implementation
and usage of iters. Moreover, the example program introduces all the class elements
(constants, shared and object attributes, routines and iters) are introduced. Most
statements and most expressions are also discussed. Where appropriate, the usage of
some basic features which are provided by the Sather 1.0 libraries are demonstrated.
The Tutorial is completed by showing how an external class can be used to interface
to a C program.
ii
1 About Sather 1.0
Sather is an object oriented language which aims to be simple, ecient, safe, and non-proprietary.
One way of placing it in the \space of languages" is to say that it aims to be as ecient as C, C++,
or Fortran, as elegant and safe as Eiel or CLU, and support higher-order functions and iteration
abstraction as well as Common Lisp, Scheme, or Smalltalk.
Sather has parameterized classes, object-oriented dispatch, statically-checked strong (contravari-
ant) typing, separate implementation and type inheritance, multiple inheritance, garbage collection,
iteration abstraction, higher-order routines and iters, exception handling, assertions, preconditions,
postconditions, and class invariants. Sather programs can be compiled into portable C code and
can eciently link with C object les. Sather has a very unrestrictive license which allows its use in
proprietary projects but encourages contribution to the public library.
1.1 Where can I nd Sather?
Information on Sather can be found on the Mosaic page [Link]
From that page, you can reach various documents related to Sather. There also is a list of frequently
asked questions. Another source of information is the newsgroup [Link] that is devoted
to discussion of Sather related issues.
There is a Sather mailing list maintained at the International Computer Science Institute (ICSI).
Since the formation of the newsgroup, this list is primarily used for announcements. To be added
to or deleted from the Sather list, send a message to sather-request@[Link].
If you have problems with Sather or if you want to discuss Sather related questions that are
not of general interest, mail to sather-bugs@[Link]. This is also where to send bug
reports and suggestions for improvements.
The current ICSI Sather 1.0 compiler, the manual, this tutorial, and the Sather FAQ can be
obtained by anonymous ftp from
[Link] /pub/sather
The distribution le is called Sather-1.0.*.tar.Z. The wildcard is to be replaced by the number
of the latest release. At the time this tutorial was written three sites have mirrored the Sather
distribution:
[Link] /programming/languages/sather
[Link] /pub/languages/sather
[Link] /pub/csp
1
Sather has been analyzed from an external point of view. Comments and comparisons can be
found in 1, 3, 12].
1.3 Related Work: Sather-K
Although we know a lot about Sather-K, which is being developed in Karlsruhe, Germany, it is not
yet available online. Future versions of this Technical Report, which can be accessed from anonymous
ftp will have some more details.
2
2 Sather Tutorial Chess
Sather Tutorial Chess is not an expert chess program. In fact, it is quite easy to win against the
computer. Moreover, the implementation is very inecient in certain parts of the code. The idea is
to simply provide a context for demonstrating and explaining various features of Sather and not to
show a world class chess program.
To make the best use of this tutorial, the Sather 1.0 system should be properly installed and the
following les should be available online:
[Link] This le contains is the standard Hello World program. It does not belong to Sather
Tutorial Chess but is included as an initial exercise.
Make le This is the Makele for Sather Tutorial Chess.
[Link] This is the main Sather le.
[Link] This is an additional Sather le. Although the code could have been in [Link], it is
kept in a separate le for explanatory reasons.
[Link] If your system is not running the X11 window system, this le is used for compilation
and linking.
[Link] Otherwise, this le is used instead.
XCW.c This C le provides the interface to the X11 window system. If you do not use X11, the
Makele will detect this and generate an executable that does not depend on or use XCW.c.
bitmaps This directory has bitmaps for all the chess pieces which are used in XCW.c.
2.1 Hello World Program
The le [Link] is the standard Hello World program. Sather programs usually have le names
with the extension .sa. To compile it, simply enter cs [Link]. The command for invoking the
compiler is easy to remember, since cs stands for \Compile Sather". After successful compilation
you can execute it by entering [Link]. If the current directory is not in your search path, enter
./[Link].
Only proceed after having successfully compiled and executed the Hello World program. If
something went wrong, check your installation of the Sather 1.0 system. The le Doc/Installation
might be helpful for diagnosing problems.
-- This is the standard Hello World program 1
-- implemented in Sather 1.0 2
class MAIN is 3
main is 4
#OUT + "Hello Worldnn" 5
end 6
end 7
The rst two lines of the le are comments. Comments start with two minus signs. The comment
cannot be explicitly closed, they end at the end of the line. The class MAIN has a special purpose
in Sather. Unless altered by compiler ags, the routine main of MAIN is started when a compiled
Sather program is invoked by the user. In main there is only one statement. This statement is
responsible for several things: At rst #OUT creates a new object of class OUT. Class OUT is a
3
basic class provided by Sather. In the implementation of class OUT which can be found in the
library le Library/[Link] there are several routines that can be invoked on an object of that class.
One of these routines has the signature
plus(s:STR)
Make sure that you look at the library le Library/[Link] and nd the routine used in the Hello
World program. It is necessary for using the Sather 1.0 system that you are familiar with the libraries
and the routines provided by them. The routine plus takes one string argument and \adds" this
argument to the object before returning the modied object. In line 5 of the program the routine
plus is called implicitly, by the operator + which itself is syntactic sugar for the call of plus.
In Sather 1.0 a string is enclosed in double quotes ("). Similar to C, \n stands for the carriage
return/line feed.
2.2 Getting Started
The other les mentioned above are needed for Sather Tutorial Chess. They could be derived
from this document by extracting and concatenating the code segments explained in the remainder.
Unless otherwise noted, the code segments go to the le [Link].
For the presentation, code segments are numbered on the right of the code. Numbering is
restarted with line 1 either when a new Sather code le is started or with the beginning of a new
section.
You can create an executable Sather Tutorial Chess program by invoking the compiler. This is
done by staring the execution of the Makele:
make
The Makele nds out whether your system runs then X Windows. Depending on the result, the
appropriate Sather code les are compiled and linked together. The executable is called
SChess
After invoking Sather Tutorial Chess, you are the white player. The computer is responsible for the
moves of black. Later, in section 3.2 we will show how this default behavior can be changed.
2.3 Class Hierarchy of Sather Tutorial Chess
Let us rst discuss the basic design decisions that led to our implementation of Sather Tutorial Chess.
The central object is the board. The board knows about its state, which is { roughly speaking { the
set of pieces, and is capable of applying moves to itself. Moves and pieces are other types of objects.
A \moves" knows about the piece that is moved and knows both the starting and the nal position
of the move. Pieces and moves use position objects to represent the position on the board.
Besides those objects that are used for representing and handling the chess game, there are
several helper objects that are necessary for interfacing with the user. For both players there is
a player object. This player objects hides the origin of a move from the chess engine. The player
object is asked to return a move. This call is either forwarded to the user or to the searching strategy
of the computer player. Hence, the same chess engine can be used for all four possible pairings of
human and automatic players.
Another object is used for handling the display of the chess board. If required, this interface can
ask the user to enter a move in standard chess notation. The implementation provides both a plain
ASCII interface and an interface to the X Window system.
The description will start with the class MAIN which contains the basic loop of the game. In
section 4 we discuss the display objects. After that, section 5 deals with the players. Then the
other classes are presented in the following order: move in section 6, position in section 7, board in
section 8 and nally pieces in section 9.
4
3 Class Main
The class MAIN has a special purpose in Sather. Unless altered by compiler ags, 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 specied 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 dierence between
classes and abstract types that is visible here by the use of the $ symbol in the type identiers and
will be explained in more detail in section 4.
class MAIN is 1
shared board : BOARD 2
shared display : $CHESS DISPLAY 3
shared white, black, player : $PLAYER 4
This is a good point to introduce Sather's ubiquitous basic data types. Upon declaration of basic
types, these are initialized automatically.
BOOL denes value objects which represent boolean values. The initial value is false.
CHAR denes value objects which represent characters. The initial value is '\0'.
STR denes reference objects which represent strings.
INT denes value objects which represent machine-dependent integers. The size is implemen-
tation dependent but must be at least 32 bits. The two's complement representation is used to
represent negative values. Bit operations are supported in addition to numerical operations.
INTI denes reference objects which represent innite precision integers.
FLT, FLTD, FLTX, and FLTDX dene value objects which represent oating point values ac-
cording to the single, double, extended, and double extended representations dened by the
IEEE-754-1985 standard.
FLTI denes reference objects which represent arbitrary precision oating point objects.
The parameterized type ARRAYfTg denes general purpose array objects of type T. For ex-
ample, ARRAYfSTRg represents an array whose elements are strings of type STR.
TUP names a set of parameterized value types called \tuples", one for each number of param-
eters. Each has as many attributes as parameters and they are named \t1", \t2", etc. Each is
declared by the type of the corresponding parameter (e.g. TUPfINT,FLTg has attributes t1:INT
and t2:FLT). It denes a create routine with an argument corresponding to each attribute.
There are more basic data types. Since these are irrelevant for this Tutorial, the interested reader
is referred to the manual 10].
Sather distinguishes between reference objects and value objects. (Other types of objects are
not mentioned in this tutorial.) Experienced C programmers immediately catch the dierence when
5
told about the internal representation: Value types are C structs and reference types are pointers
to structs.1 Because of that dierence, 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 predened 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 dierences between value types and reference types but they are beyond
the scope of this tutorial2.
3.1 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 nishes execution.
If main is declared without return parameter, no exit code will be returned.
main(args:ARRAYfSTRg):INT is 5
if ~setup(args) then -- ~ is the boolean NOT 6
-- If the given command line arguments are not acceptable, setup 7
-- returns false. Then the program terminates and returns -1. 8
return -1 9
end 10
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 eects:
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 [Link] because of \::=". The type could also have been specied explicitly as follows:
move : MOVE := [Link](board)
Another way could be to declare the variable rst and then assign in a second statement:
move : MOVE
move := [Link](board)
The scope of move is dened by the surrounding block, i.e., the loop statement.
Later we will nd out that [Link] is a dispatched call. But let's skip this for now.
1 Furthermore, you are not allowed to have pointers directly to elds of structs.
2 Some other dierence are named here because of completeness:
Value type must inherit from AVALfTg instead of AREFfTg.
The writer routine takes dierent forms for reference and value types. For reference types, it takes a single
argument whose type is the attribute's type and has no return value. Its eect is to modify the object by
setting the value of the attribute. For value types, it takes a single argument whose type is the attribute's type,
and returns a copy of the object with the attribute set to the speci ed new value, and whose type is the type of
the object. This dierence arises because it is not possible to modify value objects once they are constructed.
Study the complex number library in le Library/[Link].
6
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 ow reaches the statement after until! the latter did not terminate the loop. Since
some move has been returned from [Link] 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 nd out that the calls to [Link] in line 15, to [Link] check() in line 25,
to [Link] move in line 30, and to [Link] in line 35 all are dispatched calls. But again,
let's skip this for now.
loop 11
move ::= [Link](board) 12
until!([Link]) 13
if [Link] n apply move(move) then 14
[Link]([Link]) 15
-- Set player to the next player 16
if [Link] to play then 17
player := white 18
else 19
player := black 20
end 21
-- Find out whether the king of the current player is in 22
-- check. If so, have the display talk about the situation. 23
if [Link] king isin check then 24
[Link] check([Link] to play) 25
end 26
else 27
-- The move was invalid. Display this. By not changing 28
-- the current player, the same player is asked to try again. 29
[Link] move 30
end 31
end -- of loop 32
-- The game is over, since the current player issued a "quit-move". 33
-- Close the display. 34
[Link] 35
return 0 36
end 37
7
<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, ARRAYfSTRg, is an instantiation of the parameterized basic type
ARRAYfTg. The source code can be found in le Library/[Link]. An c of type ARRAYfTg stores
elements of type T. If c is not void, the rst element can be accessed by c0]. [Link] returns the
number of elements stored in the array. c[Link]-1] accesses the last element.
setup(args:ARRAYfSTRg):BOOL is 38
-- set defaults 39
ret : BOOL := true -- the default is that the parameters are ok 40
p ::= #ARRAYfCHARg(2) 41
p0] := 'H' -- default: human player 42
p1] := 'C' -- default: computer player 43
d : CHAR := 'X' -- type of display 44
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 p0] and p1].
In the following code segment, the arguments get processed in a loop (lines 47{65). The rst
argument, args0] 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 le Library/[Link].) The iter upto! returns an integer
value each time it is called. Here the rst call will return 1, the argument species the upper bound.
In the second call upto! will return 2, then 3, , and nally [Link]-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 le Library/[Link] oers a routine char(int) that returns the character
with the specied number. Since strings are arrays of characters, the rst character of a string can
be accessed by char(0). The character class which is implemented in the le Library/[Link] 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 rst k characters of a string.
if [Link] 1 and [Link] = 4 then
> < 45
player cnt : INT := 0 46
loop i::=[Link]!([Link]-1) 47
if argsi].size = 4 and argsi].head(4).lower="help" then
> 48
ret := false 49
end 50
tmp : CHAR := argsi].char(0).upper 51
case tmp 52
when 'A', 'X' then -- ASCII- or X-Display if available 53
d := tmp 54
when 'H', 'C' then -- Human or Computer player 55
if player cnt 2 then
< 56
8
pplayer cnt] := tmp 57
player cnt := player cnt + 1 58
else 59
ret := false 60
end 61
else 62
ret := false 63
end 64
end -- of loop 65
elsif [Link] /= 1 then -- not equal 66
-- The parameters are not acceptable. 67
ret := false 68
else 69
-- use defaults. The else could have been omitted. 70
end 71
Boolean expressions are evaluated with short-circuit semantics. For an and this means that the
second operand is only evaluated if the rst operand was true. For an or the second operand is
evaluated if the rst 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 rst 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 dened in le Library/[Link]. 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 72
display := DEFAULT::display(d) -- Creates Display object. Described below. 73
board := #BOARD 74
if p0] = 'H' then 75
-- An object of type HUMAN is created. In contrast to BOARD, 76
-- this object has a special create routine, that needs an argument. 77
white := #HUMAN([Link] to play) 78
else 79
white := #MINMAX([Link] to play) 80
end 81
if p1] = 'H' then 82
black := #HUMAN(~[Link] to play) 83
else 84
black := #MINMAX(~[Link] to play) 85
end 86
-- the first player is White 87
player := white 88
else 89
#OUT+"To start Sather Tutorial Chess use: nn" 90
#OUT+"args 0] <white> <black>] <Displ>]nn" 91
#OUT+" <white> can be either H for Human Playernn" 92
9
#OUT+" or C for Computer Playernn" 93
#OUT+" <black> ditonn" 94
#OUT+" <Displ> can be either X for X Interfacenn" 95
#OUT+" or A for ASCII Terminalnn" 96
end 97
-- Since setup has a return parameter, a result 98
-- has to be returned to the caller. 99
return ret 100
end -- of setup 101
end -- of class MAIN 102
10
4 Type $CHESS DISPLAY and Related Classes
4.1 Type $CHESS DISPLAY
Sather dierentiates 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 dene 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 $ dierentiates abstract from concrete types.
In the body of the type declaration (lines 2{14), the operations are given without any implemen-
tation. 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.)
The string interface (ARRAYfCHARg) to board needs some explanation: The board is represented
by 64 characters. Each character species the piece on a particular position of the board.
'' no piece 'P' Pawn
'B' Bishop 'Q' Queen
'K' King 'R' Rook
'N' Knight
Capital characters represent white pieces, small characters stand for black pieces. The rst character
in board species 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.)
11
4.2 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 rst 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
< 16
create:SAME is 17
return new 18
end 19
update(board:ARRAYfCHARg) is 20
redraw(board) 21
end 22
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 dum-
mies 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:ARRAYfCHARg) is 23
raise "INTERFACE: invalid call to redrawnn" 24
end 25
getmove(white to move:BOOL):MOVE is 26
raise "INTERFACE: invalid call to getmovenn" 27
end 28
The following four routines provide code that is meant to be included unchanged in other imple-
mentations 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 imple-
mentation of CHESS DISPLAY must provide complete implementations of showtext.
invalid move is 29
text : STR 30
text := "ERROR: Invalid move....try again" 31
showtext(text) 32
end 33
12
thinking(white to move:BOOL) is 34
text : STR 35
if white to move then 36
text := "White" 37
else 38
text := "Black" 39
end 40
text := text + " is thinking ... please wait ..." 41
showtext(text) 42
end -- of thinking 43
king check(white to move:BOOL) is 44
text : STR 45
if white to move then 46
text := "--> White" 47
else 48
text := "--> Black" 49
end 50
text := text + " is in check!" 51
showtext(text) 52
end -- of king check 53
showmove(text:STR) is 54
showtext(text) 55
end 56
A routine declared private can only be called from code that is in the same class as the routine.
private showtext(text:STR) is 57
-- Optional protection against implementation errors 58
raise "INTERFACE: invalid call to showtextnn" 59
end 60
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 specied in the le Library/[Link]. 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
< >
13
end 72
end -- of ask pawn xchg 73
-- The following routine is included unchanged in ASCII DISPLAY 74
-- and replaced in X DISPLAY. 75
close is 76
end 77
end -- of CHESS DISPLAY 78
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 le Li-
brary/[Link]. As expected, [Link](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 rst argument. The dierence
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.
14
redraw(board:ARRAYfCHARg) is 81
#OUT+"The current board: (small characters = black pieces)nn" 82
#OUT+" a b c d e f g h nn" 83
#OUT+" ------------------------nn" 84
loop i::=[Link]!(0) 85
#OUT+(i+1)+"j" 86
loop j::=(8i).step!(8,1) 87
#OUT+" "+boardj]+" " 88
end 89
#OUT+"j"+(i+1)+"nn" 90
end 91
#OUT+" ------------------------nn" 92
#OUT+" a b c d e f g h nn" 93
end -- of redraw 94
The following OUT::ush in line 106 tells the OUT class, that all characters that are buered should
be output immediately. Normally, the buer is only ushed, if a \n is seen in the character stream.
getmove(white to move:BOOL):MOVE is 95
move : MOVE 96
move str : STR 97
loop 98
#OUT+"Please enter a move for" 99
if white to move then 100
#OUT+" white: " 101
else 102
#OUT+" black: " 103
end 104
#OUT+"(e.g. d2-d3 or help) " 105
OUT::ush 106
move str := IN::get [Link] 107
-- The string class provides a routine head(x), which returns the first 108
-- x characters of a string. 109
if move [Link] >= 4 and move [Link](4) = "help" then 110
#OUT+"Valid moves are:nn" 111
#OUT+" ordinary move: d2-d3nn" 112
#OUT+" king castle : o-onn" 113
#OUT+" queen castle : o-o-onn" 114
#OUT+" quit : quitnn" 115
else 116
move := #MOVE(move str, white to move) 117
-- If the create routine of MOVE could not correctly deal with 118
-- the given move str [Link] returns false. If a move turns 119
-- out not to be quit or ok, the player is asked to try again. 120
until! ([Link] or [Link]) 121
#OUT+"ERROR: Invalid syntax....try againnn" 122
end 123
end 124
return move 125
15
end -- of getmove 126
private showtext(text:STR) is 127
#OUT+text+"nn" 128
end 129
end -- of ASCII DISPLAY 130
16
close is 19
XCW::CloseCW 20
end 21
The implementation of getmove is slightly more complicated. The external Chess Window imple-
mentation 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 ARRAYfTg 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 22
text : STR 23
text := "Please move a" 24
if white to move then 25
text := text+" white" 26
else 27
text := text+" black" 28
end 29
text := text+" piece." 30
XCW::TextCW(text) 31
move chars ::= #ARRAYfCHARg(5) -- create a character array with 5 chars. 32
move str ::= #STR -- create a string. 33
move : MOVE 34
move [Link] val(' ') -- set all 5 chars to ' ' 35
XCW::GetMoveInCW(move chars) 36
-- Construct string out of char array. The iter elt! returns all 5 37
-- characters of move chars, then quits and terminates the loop. 38
loop 39
move str := move str+move [Link]! 40
end 41
-- Since XCW::GetMoveInCW is guaranteed to return only 42
-- syntactically correct moves, no further plausibility tests 43
-- are required. 44
move := #MOVE(move [Link],white to move) 45
return move 46
end -- of getmove 47
end -- of X DISPLAY 48
17
In this external class denition the interface to routines of XCW.c are specied. 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 .
< >
18
end 13
end 14
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 9.
If X is not available, the following implementation which is kept in Sather code le [Link],
is used instead:
class DEFAULT is 1
display(d:CHAR):$CHESS DISPLAY is 2
ret : $CHESS DISPLAY 3
-- Since X is not available, create ASCII-Interface only. 4
ret := #ASCII DISPLAY 5
return ret 6
end 7
end 8
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 Makele makes the dependencies visible.
19
5 Type $PLAYER and Related Classes
5.1 $PLAYER
Similar to the situation between the abstract type $CHESS DISPLAY and the classes ASCII DISPLAY
and X DISPLAY, the players are organized with subtyping and include as well. The abstract type
$PLAYER species the common interface.
type $PLAYER is 1
getmove(b:BOARD):MOVE 2
ask pawn xchg:CHAR 3
end 4
This is a good place to look at the list of available class elements. We have already encountered
routine denitions and include statements. Iter denitions are similar to routine denitions. All
class elements can be declared private. Private elements can only be accessed from within the
implementation of the class. Per default, class elements are public. It is worthwhile to take a closer
look at the other class elements:
const Constant attributes are accessible by all objects in a class and may not be assigned to.
Constant attributes are initialized. They are accessible even if no object of the class is created.
shared Shared attributes are variables that are directly accessible to all objects of a given type.
They are accessible even if no object of the class is created. When only a single shared
attribute is dened by a clause, it may be provided with an initializing expression which must
20
be a constant expression. If no initialization is given, shared variables are initialized to the
default.
attr Attributes are connected with objects. Each object of a class has an individual set of attribute
variables which reect the state of the object. Attributes are only accessible when an object
has been created.
5.3 Class HUMAN PLAYER
A human player will enter his move via the interface. This is coded in the routine getmove that
replaces the inherited dummy implementation.
If a human player has the chance to exchange one of his pawns with a queen or a knight, the
human player will enter his decision via the interface in routine ask pawn xchg.
class HUMAN $PLAYER is
< 19
include PLAYER 20
getmove(b:BOARD):MOVE is 21
return MAIN::[Link](iswhite) 22
end 23
ask pawn xchg:CHAR is 24
MAIN::[Link](MAIN::[Link]) 25
return MAIN::[Link] pawn xchg 26
end 27
end -- of class HUMAN 28
21
[Link] := #FLISTfMOVEg 39
rnd := #MS RANDOM GEN 40
[Link](4711) 41
return ret 42
end 43
The getmove routine at rst tells the viewing user that it is \thinking" (line 46). Then it uses the
routine minmax, which is described below, to nd the best move. There might be more than one
move that is considered to be \best". The list bestmoves stores all of these. If there are no available
moves, i.e., if the list of bestmoves is empty, then the player is mate { the game is over. This is
checked in line 54.
Otherwise the random number generator returns a value in 0 1). This is multiplied by the size
of the list of available best moves. Before multiplication, size, which is an integer value, is cast to be
of type FLTD. The product is rounded to the oor and then cast into an integer value by the routine
int. The result is then used to index into the list of possible best moves.
Before returning the move to the caller, it is displayed in line 61.
getmove(board:BOARD):MOVE is 44
ret : MOVE 45
MAIN::[Link]([Link] to play) 46
if [Link] to play then 47
-- minmax returns a value, that is nor needed. However, Sather does 48
-- require to use the return value somehow. 49
dummy ::= minmax(board,max,max depth) 50
else 51
dummy ::= minmax(board,min,max depth) 52
end 53
if [Link] = 0 then 54
return #MOVE("quit",[Link] to play) 55
else 56
ret := bestmoves([Link].td [Link]).[Link]] 57
[Link] 58
text : STR 59
text := [Link] + "-" + [Link] 60
MAIN::[Link](text) 61
return ret 62
end 63
end -- of getmove 64
The private routine minmax returns a oating point value, FLT. FLT is specied in the library class
FLT. See le Library/[Link] for details.
The body of minmax has a good example of nested iter calls: The rst loop (lines 74{103)
considers all pieces on the board of my color. The inner loop (lines 75{102) then for each of these
pieces considers target positions of potential moves. (It is explained later on, what an ordinary move
is. Just ignore this ag for the time being.)
The move created in line 77 is guaranteed to be correct, i.e., the piece is of the correct color and
the target position is correct with respect to the basic movement rules of chess. The only condition
that is not guaranteed to hold is whether the own king is exposed to be in check after the piece is
moved. This is checked in apply move with own check test. See line 79.
22
After a move has been applied successfully, we either consider the possible reactions recursively
(line 83), or evaluate the value of the board in line 81.
The depth-rst search requires backtracking. This is done in line 100 by calling [Link] move.
private minmax(board:BOARD,minmax:BOOL,depth:INT):FLT is 65
move : MOVE 66
val,bv : FLT 67
pos : POS 68
if minmax = max then 69
val := -1000.0 70
else 71
val := 1000.0 72
end 73
loop piece::=[Link] piece! 74
loop 75
pos :=[Link]!(board,PIECE::ordinary) 76
move := #MOVE(piece,pos) 77
[Link] := piece 78
if [Link] move with own check test(move) then 79
if depth = 1 then 80
bv := [Link] value 81
else 82
bv := minmax(board,~minmax,depth - 1) 83
end 84
-- If this move really is better than previous ones, 85
-- the list of best moves found so far is erased. 86
if depth = max depth and ( (minmax = max and bv > val) 87
or (minmax = min and bv < val)) 88
then 89
[Link] 90
end 91
-- If this move is not worse than previous ones, the move 92
-- is added to the list of best moves found so far. 93
if depth = max depth and ( (minmax = max and bv >= val) 94
or (minmax = min and bv <= val)) 95
then 96
val := bv 97
bestmoves := [Link](move) 98
end 99
[Link] move 100
end 101
end 102
end 103
return val 104
end -- of minmax 105
end -- of class MINMAX 106
The following remark will be completely understandable only after the type $PIECE and the concrete
subtypes have been presented in section 9. For reasons of completeness note that line 76 is a
dispatched iter call. Depending on the concrete type of the piece:$PIECE a dierent iter is called.
23
In Sather 1.0.2 dispatched iters are not implemented. The typecase statement can be used to
implement the intended behavior:
typecase piece
when PAWN then pos:=[Link]!(board,PIECE::ordinary)
when ROOK then pos:=[Link]!(board,PIECE::ordinary)
when KNIGHT then pos:=[Link]!(board,PIECE::ordinary)
when BISHOP then pos:=[Link]!(board,PIECE::ordinary)
when KING then pos:=[Link]!(board,PIECE::ordinary)
when QUEEN then pos:=[Link]!(board,PIECE::ordinary)
else
end
24
6 Class MOVE
A move, i.e., an object of class MOVE stores several facts. First of all there are the from and the
to position which are objects of class POS. The move knows about it being a castle move. Castle
moves have from and to positions that refer to the movement of the king.
During the process of analyzing a move, further information is gathered and stored in the move
object. This information is necessary to later on un-do a move. The attribute piece stores a pointer
to the piece that is moved by a move. If the move kills an opponent piece, that piece can be reached
by the attribute kills. The fact whether the kings have moved belongs to the status of the board. A
move of a king might change that status. To preserve the fact that a particular move has changed
that status, the king chg ag has been introduced. Another ag for un-doing moves is pawn chg. If
a pawn reaches the base line of the opponent, the pawn can be exchanged to a knight or a queen.
The pawn chg ag indicates such an exchange. Although a board knows about the last move, the
previous move is kept in the move object.
class MOVE is 1
attr from, to : POS 2
attr isk castle : BOOL 3
attr isq castle : BOOL 4
attr isquit : BOOL 5
attr piece : $PIECE 6
attr kills : $PIECE 7
attr king chg : BOOL 8
attr pawn chg : BOOL 9
attr prev move : MOVE 10
The MOVE class oers two create routines and is thus a good example of overloading. The rst
version of the create routine, accepts a move in standard chess notation, e.g. \a2-a3". For this version
of create it does not matter, whether the board actually has a piece on the from position since this
is checked later on. In contrast to the rst version of the create routine, the second version deals
with an existing $PIECE object. Since a piece has an actual position, only the destination position
is required as parameter.
This code of the create routine is written rather fail safe. The given string is checked for
conforming syntax. If there is an error, the from and to position of the move object remain void.
The rst branch of the if{elsif cascade handles the q-castle (lines 21{28). The second branch
handles the k-castle (lines 29{36) Then the \quit" case is considered. The fourth case (lines 39{47)
and fth case (lines 48{57) both deal with ordinary moves: They check for syntax \ p1 - p2 "
< > < >
and test whether p1 and p2 refer to existing positions of the board. The string class oers a substring
routine which has two parameters. It is used for example in line 40. The rst argument refers to
the starting position of the substring, the second argument species the number of characters to be
returned. The dierence between the fourth and the fth case is that in the latter the the separating
\-" can be omitted so that \ p1 p2 " is accepted.
< >< >
25
[Link] chg := false 18
[Link] chg := false 19
if void(move) then return ret end 20
if [Link] >= 5 and [Link](5) = "o-o-o" then 21
[Link] := #POS [Link] := #POS 22
[Link] castle := true 23
if white to move then 24
[Link] := "e1" [Link] := "c1" 25
else 26
[Link] := "e8" [Link] := "c8" 27
end 28
elsif [Link] = 3 and [Link](3) = "o-o" then
> 29
[Link] := #POS [Link] := #POS 30
[Link] castle := true 31
if white to move then 32
[Link] := "e1" [Link] := "g1" 33
else 34
[Link] := "e8" [Link] := "g8" 35
end 36
elsif [Link] = 4 and [Link](4) = "quit" then
> 37
[Link] := true 38
elsif [Link] = 5 then
> 39
str from ::= [Link](0,2) 40
if POS::check pos(str from) then 41
[Link] := #POS [Link] := str from 42
end 43
str to ::= [Link](3,2) 44
if POS::check pos(str to) then 45
[Link] := #POS [Link] := str to 46
end 47
elsif [Link] >=4 then 48
str from ::= [Link](0,2) 49
if POS::check pos(str from) then 50
[Link] := #POS [Link] := str from 51
end 52
str to ::= [Link](2,2) 53
if POS::check pos(str to) then 54
[Link] := #POS [Link] := str to 55
end 56
end 57
return ret 58
end -- of first version of create 59
The routine create is overloaded in class MOVE, i.e., there are two routines called create that
are distinguished by their list of formal parameters and/or return parameter. Whereas the create
routine given above expects a string and a boolean value as parameters, the second create routine
expects a piece and a (target) position.
26
create(piece:$PIECE, to:POS):SAME is 60
ret ::= new 61
[Link] castle := false 62
[Link] castle := false 63
[Link] := false 64
[Link] := #POS 65
[Link] := [Link] 66
[Link] := #POS 67
[Link] := [Link] 68
[Link] := void 69
[Link] := void 70
[Link] chg := false 71
[Link] chg := false 72
if [Link] then 73
if [Link] then 74
if [Link] = "e1" and to = "c1" then 75
[Link] castle := true 76
end 77
if [Link] = "e1" and to = "g1" then 78
[Link] castle := true 79
end 80
else 81
if [Link] = "e8" and to = "c8" then 82
[Link] castle := true 83
end 84
if [Link] = "e8" and to = "g8" then 85
[Link] castle := true 86
end 87
end 88
end 89
return ret 90
end -- of second version of create 91
isok:BOOL is 92
return ~void(from) and ~void(to) 93
end 94
end -- of class MOVE 95
27
7 Class POS
The main secret of class POS is the internal addressing scheme for a chess board. From outside,
board positions are addressed in standard chess notation, e.g., the position in the lower left corner
is called \a1". Internally, POS numbers the positions row-wise from 0 to 63 which eases addressing
computations. The correspondence is shown in the following tables:
External addressing scheme:
column 'a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' row
a8 b8 c8 d8 e8 f8 g8 h8 '8'
a7 b7 c7 d7 e7 f7 g7 h7 '7'
a6 b6 c6 d6 e6 f6 g6 h6 '6'
a5 b5 c5 d5 e5 f5 g5 h5 '5'
a4 b4 c4 d4 e4 f4 g4 h4 '4'
a3 b3 c3 d3 e3 f3 g3 h3 '3'
a2 b2 c2 d2 e2 f2 g2 h2 '2'
a1 b1 c1 d1 e1 f1 g1 h1 '1'
Internal addressing scheme:
column 0 1 2 3 4 5 6 7 row
56 57 58 59 60 61 62 63 7
48 49 50 51 52 53 54 55 6
40 41 42 43 44 45 46 47 5
32 33 34 35 36 37 38 39 4
24 25 26 27 28 29 30 31 3
16 17 18 19 20 21 22 23 2
8 9 10 11 12 13 14 15 1
0 1 2 3 4 5 6 7 0
POS is capable of returning all board positions which are reachable from an POS object's position by
moves in various directions. The way! iter is used for this purpose. Possible directions are vertical,
horizontal, diagonal, knight jumps and so on.
POS is declared to be a subtype of $IS EQfPOSg. The $IS EQ type is specied in the library le
Library/[Link]. The essential meaning of this subtype declaration is that POS is required to oer
a routine with the signature is eq(x:SAME):BOOL. The existence of this routine is checked during
compilation. The analogous situation holds for $STR. This abstract type requires the existence of a
routine str:STR that prints out a reasonable string representation of the object.
In lines 2{4 is an example of a rather weird form of constant declaration. All together 12 integer
constants are declared. The rst one (knight) is assigned the value 1, the next one (diag up right) is
set to 2 and so on. This form of constant declaration only works for integers and requires that there
is no type identier INT. Both
knight:INT:=1, ...
and
knight := 'a', ...
result in errors at compile time.
The internal address of a position is stored in the private attribute absolute declared in line 8.
class POS $IS EQfPOSg, $STR is
< 1
const knight := 1, diag up right, diag up left, diag dn right, diag dn left, 2
horizontal right, horizontal left, vertical up, vertical dn, 3
28
north two, south two, ring 4
-- The correct funtionality relies on the fact that diag up right to 5
-- vertical dn are in that order. The implementation of $PIECE::move! may 6
-- depend on it. 7
private attr absolute : INT 8
create:SAME is 9
return new 10
end 11
The following routines are used to handle \internal" addresses of board positions.
private pos(position:INT) is -- write routine 12
absolute := position 13
end 14
pos:INT is -- reader routine 15
return absolute 16
end 17
private row(p:POS):INT is 18
return ([Link]/8) 19
end 20
private column(p:POS):INT is 21
return ([Link]%8) 22
end 23
29
return false 31
end 32
col : CHAR := [Link](0) 33
if col < 'a' or col > 'h' then 34
return false 35
end 36
return true 37
end -- of check pos 38
pos(position:STR) -- overloaded writer routine 39
pre check pos(position) 40
is 41
str : STR := [Link] 42
row : INT := [Link](1).digit value - 1 43
col : CHAR := [Link](0) 44
case col 45
when 'a' then absolute := 0 46
when 'b' then absolute := 1 47
when 'c' then absolute := 2 48
when 'd' then absolute := 3 49
when 'e' then absolute := 4 50
when 'f' then absolute := 5 51
when 'g' then absolute := 6 52
when 'h' then absolute := 7 53
end 54
absolute := absolute + 8 row 55
end -- of pos(STR) 56
str:STR is 57
ret ::= #STR 58
ret := ret + column 59
ret := ret + row 60
return ret 61
end 62
The routine row in line 63 is overloaded as well. The compiler can dierentiate between row(INT):INT
of line 18 and row:CHAR because of the dierent number of parameters.
In the statement in line 64 the result of the computation is an integer value. The library class
INT oers two ways to convert integers into characters. The dierence is best shown by means of
an example. Consider the integer value 0. The conversion done by digit char returns the character
'0'. The other conversion is done by a routine called char which has the result that [Link] = '\0'.
The routine str(POS) is used internally to map an internal address, which might be dierent
from self, to standard chess notation.
row:CHAR is 63
return ((absolute/8) + 1).digit char 64
end 65
column:CHAR is 66
col ::= absolute%8 67
case col 68
when 0 then return 'a' 69
when 1 then return 'b' 70
30
when 2 then return 'c' 71
when 3 then return 'd' 72
when 4 then return 'e' 73
when 5 then return 'f' 74
when 6 then return 'g' 75
when 7 then return 'h' 76
end 77
end 78
private str(pos:INT):STR is 79
ret ::= #STR 80
col ::= pos % 8 81
row ::= (pos / 8) + 1 82
case col 83
when 0 then ret := "a" 84
when 1 then ret := "b" 85
when 2 then ret := "c" 86
when 3 then ret := "d" 87
when 4 then ret := "e" 88
when 5 then ret := "f" 89
when 6 then ret := "g" 90
when 7 then ret := "h" 91
end 92
ret := ret + row 93
return ret 94
end -- of str(INT) 95
The following routines return neighboring addresses in standard chess notation. If there is no existing
neighboring position for a given direction, the current address is returned.
east:STR is 96
ret ::= absolute + 1 97
if ret/8 /= absolute/8 then ret := absolute end 98
return str(ret) 99
end 100
west:STR is 101
ret ::= absolute - 1 102
if ret/8 /= absolute/8 then ret := absolute end 103
return str(ret) 104
end 105
north:STR is 106
ret ::= absolute + 8 107
if ret > 63 then ret := absolute end 108
return str(ret) 109
end 110
south:STR is 111
ret ::= absolute - 8 112
if ret < 0 then ret := absolute end 113
return str(ret) 114
end 115
31
In addition to routines that return the address of neighboring positions in horizontal and vertical
directions, there are four routines for neighboring positions on the diagonal axes.
northeast:STR is 116
err : BOOL := false 117
ret ::= absolute + 8 118
if ret > 63 then ret := absolute err := true end 119
if ~err then 120
ret := absolute + 1 121
if ret/8 /= absolute/8 then ret := absolute err := true end 122
end 123
if ~err then 124
ret := absolute + 9 125
end 126
return str(ret) 127
end 128
northwest:STR is 129
err : BOOL := false 130
ret ::= absolute + 8 131
if ret > 63 then ret := absolute err := true end 132
if ~err then 133
ret := absolute - 1 134
if ret/8 /= absolute/8 then ret := absolute err := true end 135
end 136
if ~err then 137
ret := absolute + 7 138
end 139
return str(ret) 140
end 141
southeast:STR is 142
err : BOOL := false 143
ret ::= absolute - 8 144
if ret < 0 then ret := absolute err := true end 145
if ~err then 146
ret := absolute + 1 147
if ret/8 /= absolute/8 then ret := absolute err := true end 148
end 149
if ~err then 150
ret := absolute - 7 151
end 152
return str(ret) 153
end 154
southwest:STR is 155
err : BOOL := false 156
ret ::= absolute - 8 157
if ret < 0 then ret := absolute err := true end 158
if ~err then 159
ret := absolute - 1 160
if ret/8 /= absolute/8 then ret := absolute err := true end 161
end 162
if ~err then 163
32
ret := absolute - 9 164
end 165
return str(ret) 166
end 167
Here are some equality tests. The rst one is required because POS has been declared to be a subtype
of $IS EQfPOSg. The Sather compiler considers a boolean expression p=q to be syntactic sugar for
the routine call [Link] eq(q). Analogously, p/=q is taken to be [Link] neq(q). If these expressions are
found somewhere in the code, the corresponding routine has to be provided.
is eq(p:SAME):BOOL is 168
return (absolute = [Link]) 169
end 170
is neq(p:STR):BOOL is 171
return ~is eq(p) 172
end 173
is eq(p:STR):BOOL is 174
tmp ::= #POS 175
[Link] := p 176
return is eq(tmp) 177
end 178
The iter way! returns all reachable positions on an otherwise empty board in the specied direction.
Since this is the rst occurrence of an iter declaration, some explanations are appropriate. Iters
are declared similar to routines. The dierence is that their name has to end with an exclamation
point \!". Iters may only be called from within loop statements.
For each textual iter call, en execution state is maintained. When a loop is entered, the execution
state of all iter calls is initialized. When an iter is called for the rst time, the expressions for self
and for each argument are evaluated3.
When the iter is called, it executes the statements in its body in order. If it executes a yield
statement, control and a value are returned to the caller. Subsequent calls to the iter resume
execution with the statement following the yield statement. If an iter executes a quit statement or
reaches the end of its body, control passes immediately to the end of the innermost enclosing loop
statement in the caller and no value is returned from the iter.
The code in lines 180{183 is evaluated only at the time of the rst invocation. If there are two
dierent textual calls of way!, each one has a separate state and each will execute these code lines
at the rst invocation.
In line 182 the starting position of the stepping is initialized. Note that this assignment is actually
a call of the private routine pos(INT). The compiler considers this expression to be equivalent to
[Link](absolute).
The loop in lines 184{348 is the main part of the iter. From inside the loop potential positions
are returned to the caller. If no more positions are available, then a \quit" ends this loop, ends the
iter and ends the loop surrounding the call to the iter.
Since most branches of the case statement are similar only the rst case (lines 186{198) is
explained in some detail. Later we will point out the dierences of the branches for knight, pawn,
and king moves. From the current position which is kept in stepped, the northeast neighbor is
3 An exception are arguments which have a trailing exclamation mark themselves. These are evaluated for every
call of the iter. But since this kind of argument is not used in Sather Tutorial Chess, the reader is referred to the
Sather Manual 10] for further discussion.
33
checked. If this position is still on the board it is returned to the caller. This is done in line 192 by
the yield statement.
After the caller has processed the new position, the next call to the iter will resume after line 192.
The status is still available, i.e., stepped keeps the position, which has been returned previously. Since
the only statement of the loop is this case, the iter will next re-execute the case and automatically
re-enter this branch. (Note, the direction is not re-evaluated and remains unchanged.)
If the end of the board has been reached by moving into the northeast direction, the iter cannot
return further valid position. Hence, the iter quits in the else branch (line 194 or 196). It does not
return any position, and immediately terminates the loop in the caller.
way!(direction:INT):POS is 179
ret, stepped : POS 180
stepped := #POS 181
[Link] := absolute -- starting position 182
count : INT := 0 183
loop 184
case direction 185
when diag up right then 186
if [Link] 'h' then
< 187
if [Link] '8' then
< 188
[Link] := [Link] 189
ret := #POS 190
[Link] := [Link] 191
yield ret 192
else 193
quit 194
end 195
else 196
quit 197
end 198
when diag up left then 199
if [Link] 'a' then
> 200
if [Link] '1' then
> 201
[Link] := [Link] 202
ret := #POS 203
[Link] := [Link] 204
yield ret 205
else 206
quit 207
end 208
else 209
quit 210
end 211
when diag dn right then 212
if [Link] 'h' then
< 213
if [Link] '1' then
> 214
[Link] := [Link] 215
ret := #POS 216
[Link] := [Link] 217
yield ret 218
else 219
34
quit 220
end 221
else 222
quit 223
end 224
when diag dn left then 225
if [Link] 'a' then
> 226
if [Link] '8' then
< 227
[Link] := [Link] 228
ret := #POS 229
[Link] := [Link] 230
yield ret 231
else 232
quit 233
end 234
else 235
quit 236
end 237
when vertical up then 238
if [Link] '8' then
< 239
[Link] := [Link] 240
ret := #POS 241
[Link] := [Link] 242
yield ret 243
else 244
quit 245
end 246
when vertical dn then 247
if [Link] '1' then
> 248
[Link] := [Link] 249
ret := #POS 250
[Link] := [Link] 251
yield ret 252
else 253
quit 254
end 255
when horizontal right then 256
if [Link] 'h' then
< 257
[Link] := [Link] 258
ret := #POS 259
[Link] := [Link] 260
yield ret 261
else 262
quit 263
end 264
when horizontal left then 265
if [Link] 'a' then
> 266
[Link] := [Link] 267
ret := #POS 268
[Link] := [Link] 269
yield ret 270
35
else 271
quit 272
end -- way! will be continued ... 273
The branch of the case statement that computes the new position of a knight in lines 274{296 is
somewhat dierent. Instead of using a current position (called stepped), the new positions are always
computed relative to the starting position.
A white pawn (case north two, lines 297{307) may move one or to steps to the north depending
on the staring row. A black pawn (case south two, lines 308{318) may move one or to steps to the
south depending on the staring row. A king (case ring, lines 319{342) can reach all 8 positions on
the ring around his staring position.
when knight then 274
ret := #POS 275
case count 276
when 0 then [Link] := absolute + 6 277
when 1 then [Link] := absolute - 6 278
when 2 then [Link] := absolute + 10 279
when 3 then [Link] := absolute - 10 280
when 4 then [Link] := absolute + 15 281
when 5 then [Link] := absolute - 15 282
when 6 then [Link] := absolute + 17 283
when 7 then [Link] := absolute - 17 284
else 285
quit 286
end 287
count := count + 1 288
if [Link] <= 63 and [Link] >= 0 289
and column(ret) <= column(self) + 2 290
and column(self) - 2 <= column(ret) 291
and row(ret) <= row(self) + 2 292
and row(self) - 2 <= row(ret) 293
then 294
yield ret 295
end 296
when north two then 297
if count 2 and stepped /= [Link] then
< 298
[Link] := [Link] 299
ret := #POS 300
[Link] := [Link] 301
count := count + 1 302
yield ret 303
if row /= '2' then quit end 304
else 305
quit 306
end 307
when south two then 308
if count 2 and stepped /= [Link] then
< 309
[Link] := [Link] 310
ret := #POS 311
36
[Link] := [Link] 312
count := count + 1 313
yield ret 314
if row /= '7' then quit end 315
else 316
quit 317
end 318
when ring then 319
ret := #POS 320
case count 321
when 0 then [Link] := north 322
when 1 then [Link] := south 323
when 2 then [Link] := east 324
when 3 then [Link] := west 325
when 4 then [Link] := northeast 326
when 5 then [Link] := northwest 327
when 6 then [Link] := southeast 328
when 7 then [Link] := southwest 329
else 330
quit 331
end 332
count := count + 1 333
if [Link] = 63 and [Link] = 0
< > 334
and [Link] /= absolute 335
and column(ret) = column(self) + 1
< 336
and column(self) - 1 = column(ret)
< 337
and row(ret) = row(self) + 1
< 338
and row(self) - 1 = row(ret)
< 339
then 340
yield ret 341
end 342
else 343
-- The else case was put in for reasons of 344
-- fail safe program development. 345
raise "POS:way! invalid casenn" 346
end -- of case 347
end -- of loop 348
end -- of way! 349
end -- of class POS 350
37
8 Class BOARD
The two array whitexpieces and blackpieces store the pieces in the game. A piece is an object of
type $PIECE which is explained below. Since both arrays are private, it is a secret of the board
implementation in which way pieces are stored.
The board stores information about which color is to play (white to play) and about the last
move (last move). Moreover, the board knows whether the white or black king has been moved.
This information is necessary, because castle moves are only allowed if the king has not been moved
before.
class BOARD is 1
private attr whitepieces : ARRAYf$PIECEg 2
private attr blackpieces : ARRAYf$PIECEg 3
attr white to play : BOOL 4
attr last move : MOVE 5
attr white K moved : BOOL 6
attr black K moved : BOOL 7
create:SAME is 8
ret::=new 9
[Link] up 10
return ret 11
end 12
The private routine set up initializes the board. 16 white and 16 black pieces are placed onto the
board, the rst player is set to be white, both kings have not moved.
private set up is 13
position ::= #POS 14
white to play := true 15
-- set up white pieces 16
whitepieces := #(16) 17
[Link] := "a2" 18
loop i::=[Link]!(7) 19
whitepiecesi] := #PAWN(position,PIECE::white) 20
[Link] := [Link] 21
end 22
[Link] := "a1" whitepieces8] := #ROOK(position,PIECE::white) 23
[Link] := "b1" whitepieces9] := #KNIGHT(position,PIECE::white) 24
[Link] := "c1" whitepieces10] := #BISHOP(position,PIECE::white) 25
[Link] := "d1" whitepieces11] := #QUEEN(position,PIECE::white) 26
[Link] := "e1" whitepieces12] := #KING(position,PIECE::white) 27
[Link] := "f1" whitepieces13] := #BISHOP(position,PIECE::white) 28
[Link] := "g1" whitepieces14] := #KNIGHT(position,PIECE::white) 29
[Link] := "h1" whitepieces15] := #ROOK(position,PIECE::white) 30
-- set up black pieces 31
blackpieces := #(16) 32
[Link] := "a7" 33
loop i::=[Link]!(7) 34
blackpiecesi] := #PAWN(position,PIECE::black) 35
[Link] := [Link] 36
38
end 37
[Link] := "a8" blackpieces8] := #ROOK(position,PIECE::black) 38
[Link] := "b8" blackpieces9] := #KNIGHT(position,PIECE::black) 39
[Link] := "c8" blackpieces10] := #BISHOP(position,PIECE::black) 40
[Link] := "d8" blackpieces11] := #QUEEN(position,PIECE::black) 41
[Link] := "e8" blackpieces12] := #KING(position,PIECE::black) 42
[Link] := "f8" blackpieces13] := #BISHOP(position,PIECE::black) 43
[Link] := "g8" blackpieces14] := #KNIGHT(position,PIECE::black) 44
[Link] := "h8" blackpieces15] := #ROOK(position,PIECE::black) 45
white K moved := false 46
black K moved := false 47
last move := void 48
MAIN::[Link]([Link]) 49
end 50
Several iters are needed to return all pieces on the board that fulll a certain condition.
The rst iter whitepiece! returns all white pieces, which are still alive. For this purpose, it
makes use of the iter elt! in line 52. The iter is provided by the ARRAY library class (see le
Library/[Link]). If elt! yields an element, this element is yield to the caller if it fullls the conditions.
However, if elt! quits, this loop is terminated as well, no element is returned to the caller.
private whitepiece!:$PIECE is 51
loop p ::= [Link]! 52
if ~void(p) and [Link] then yield p end 53
end 54
end 55
private blackpiece!:$PIECE is 56
loop 57
p ::= [Link]! 58
if ~void(p) and [Link] then yield p end 59
end 60
end 61
The nesting depth of iters can be increased even further, as shown in my piece below: Within
whitepiece! the iter elt! is used. An element found by elt! is returned via whitepiece! and then
returned to the caller of my piece!. Similarly, a quit of elt!, induces a quit of whitepiece!, which in
turn results in a quit of my piece!. The latter terminates the loop, that must surround every call of
my piece! in the caller.
my piece!:$PIECE is 62
if white to play then 63
loop 64
yield whitepiece! 65
end 66
else 67
loop 68
yield blackpiece! 69
end 70
end 71
end 72
39
private opp piece!:$PIECE is 73
if white to play then 74
loop 75
yield blackpiece! 76
end 77
else 78
loop 79
yield whitepiece! 80
end 81
end 82
end 83
piece!:$PIECE is 84
loop 85
yield whitepiece! 86
end 87
loop 88
yield blackpiece! 89
end 90
end 91
One of the secrets of the BOARD implementation is the way pieces are stored. For internal purposes
it is necessary, to nd out at which position of the arrays a particular piece is stored.
In the private routine index we use a post condition. To assure that the piece p is (dead or
alive) on board we test whether the return value result is set appropriately, i.e., whether result is
between 0 and 15 upon return. Note, that there may not be a semicolon behind a post condition.
The conditions get checked before the routine returns. To access the value that will be returned,
Sather provides the predened results expression. The type of results is determined by the result
type of the routine. If checking is desired, it has to be activated with the compiler flag -post
<classes>.
The loop (line 97{104) is an excellent example of a loop that is controlled by multiple iters. The
rst two iters are dened in the ARRAY library class. The iter ind! (line 98) returns the existing
indexes of array. As explained above, elt! (line 99) returns the corresponding array elements. For each
iteration of the loop the following condition holds: whitepiecesi] = q. Both iters can be expected to
yield the same number of times. If the end of the array is encountered, the call to ind! will terminate
the loop" elt! will not be called.
However, if the desired piece is found, it is not necessary, to continue the search. To terminate
the loop immediately, the predened iter break! is called in line 102, which will always execute a
quit statement.
The same search is implemented dierently in the else branch (line 106). Here we use the library
routine index of provided in the ARRAY class. (See le Library/[Link] for details.)
private index(p:$PIECE):INT 92
post [Link] bet(0,15) 93
is 94
ret : INT := -1 95
if [Link] then 96
loop 97
i::= [Link]! 98
q::= [Link]! 99
if [Link] = [Link] then 100
40
ret := i 101
break! 102
end 103
end -- of loop 104
else 105
ret := [Link] of(p) 106
end 107
return ret 108
end -- private index 109
To check whether there is a piece on a given position of the board the following routines are provided:
has piece(pos:POS):BOOL is 110
ret : BOOL := false 111
loop p::=piece! 112
if [Link] = pos then ret := true end 113
end 114
return ret 115
end 116
has white piece(pos:POS):BOOL is 117
ret : BOOL := false 118
loop p::=whitepiece! 119
if [Link] = pos then ret := true end 120
end 121
return ret 122
end 123
has black piece(pos:POS):BOOL is 124
ret : BOOL := false 125
loop p::=blackpiece! 126
if [Link] = pos then ret := true end 127
end 128
return ret 129
end 130
has my piece(pos:POS):BOOL is 131
if white to play then 132
return has white piece(pos) 133
else 134
return has black piece(pos) 135
end 136
end 137
The following two routines return a pointer to a piece at a given position of the board. The routine
comes in two versions. The latter can process POS arguments by reducing them to STR parameters
which are then processed by the rst version.
piece(str:STR):$PIECE is 138
ret : $PIECE 139
position ::= #POS 140
[Link] := str 141
loop p::=piece! 142
41
if [Link] = position then ret := p end 143
end 144
return ret 145
end 146
piece(p:POS):$PIECE is 147
return piece([Link]) 148
end 149
For interface purposes, a board can represent the status of all pieces in an ASCII representation.
The character array is used to transmit the board situation to the ASCII DISPLAY and via the
X DISPLAY to the external class XCW.
str:ARRAYfCHARg is 150
ret::=#ARRAYfCHARg(65) 151
loop 152
ret[Link]!(63)] := ' ' 153
end 154
ret64] := 'n0' 155
loop p::=[Link]! 156
if ~void(p) and [Link] then 157
ret[Link]] := p. g 158
end 159
end 160
loop p::=[Link]! 161
if ~void(p) and [Link] then 162
ret[Link]] := p. [Link] 163
end 164
end 165
return ret 166
end 167
After these helper routines and iters have been implemented, the central routines are presented.
The routine pos in check tests whether a given position could be reached in the next move by the
opponent.
In this routine there is again a good example of nested iter calls: The rst loop (line 172{179)
considers all pieces of the opponent player. The inner loop (line 173{178) then for each of these
pieces considers target positions of potential moves. (Is is explained later on, what a move is if the
ag for check test is set. Just ignore the ag for the time being.)
The call [Link]!() in line 174is a dispatched iter. See page 24 for an alternative implemen-
tation that works with earlier releases of the Sather 1.0 compiler.
pos in check(p:POS):BOOL is 168
ret : BOOL 169
pos : POS 170
ret := false 171
loop piece::=opp piece! 172
loop 173
pos :=[Link]!(self, PIECE::for check test) 174
if p=pos then 175
ret := true 176
42
break! 177
end 178
end 179
if ret then break! end 180
end 181
return ret 182
end -- of pos in check 183
The routine my king isin check returns true if the king of the current color (white to play) is in check.
After an otherwise valid move of a piece, the own king is not allowed to be exposed and to be in
check.
my king isin check:BOOL is 184
piece : $PIECE 185
loop 186
piece := my piece! 187
until!([Link]) 188
end 189
return pos in check([Link]) 190
end -- of my king isin check 191
Boolean expressions are evaluated with a short-circuit semantics. For an and this means that the
second operand is only evaluated if the rst operand was true. For an or the second operand is
evaluated only if the rst one was false. In routine check n apply move we make use of this to ensure
that a move is applied to a board only if it is valid.
Routine move valid so far checks whether a given move is valid with respect to the current state
of the board. The only circumstance which is not checked is whether the move would expose the
own king to be in check.
check n apply move(move:MOVE):BOOL is 192
return (move valid so far(move) and apply move with own check test(move)) 193
end -- of check n apply move 194
private move valid so far(move:MOVE):BOOL 195
pre ~[Link] 196
is 197
ret : BOOL := false 198
-- A valid move must start at a position where one of my pieces is.... 199
if has my piece([Link]) then 200
p::=piece([Link]) 201
-- ... and it must be a valid move with respect to the mobility of the 202
-- piece at the current state of the board. 203
if [Link] move([Link],self) then 204
ret := true 205
-- Since the move seems to be valid, the moving piece is stored 206
-- in the move object. That eases future access to the moving piece 207
-- and allows for un-doing of moves. 208
[Link] := p 209
end 210
end 211
return ret 212
43
end -- of move valif 213
The move is applied to the board in routine apply move with own check test. The routine returns
false, and leaves the state of the board unchanged, if an otherwise valid move would expose the own
king to be in check.
First of all in lines 221-241 it is checked whether the move would kill an opponent piece. The
normal circumstances for this are that the moving piece moves to a position that is occupied by an
opponent piece. Chess has one special rule due to which a piece can be killed without moving to its
former position. It is called an \en passant" move. This special case can only occur if two pawns
are involved. My pawn can kill an opponent pawn that sits immediately east or west of my pawn,
if the other pawn has done an initial double move in the immediately preceding move. (That's why
the last move is considered to be part of the state of a board.). If these conditions hold, my pawn
can move diagonal so that his new position is \behind" the opponent pawn.
Special action is required in case of castle moves. A castle move works as follows. If the king
and a rook both are in their initial positions, if there is no piece in between them, if the king has
not been moved in the game, and if the two positions next to the king in the direction toward the
rook are not in check, then the king moves two positions towards the rook and then the rook jumps
over the king and is put immediately next to the king. A castle move is a k-castle, if the king moves
to the rook whose initial position is closer. Otherwise it is called q-castle, because due to the initial
queen position, the distance to the rook is larger. Chess only allows castle moves, if the king has not
been moved earlier in the game. The board keeps track of king moves in the two ags white K moved
and black K moved. To enable un-doing of moves, a move knows whether it causes a change of a
K moved ag. See lines 254{268 for the K moved ags and lines 269{286 for the implementation of
castle moves.
Another special rule in chess allows to exchange a pawn against a queen or a knight when it
reaches the base line of the opponent. Theoretically, a player could have 9 queens. This rule is
implemented in lines 254{268.
apply move with own check test(move:MOVE):BOOL 214
pre ~[Link] and move valid so far(move) and ~void([Link]) 215
is 216
ret : BOOL := true -- Will be false if the move is invalid due to 217
-- exposure of own "king in chess" 218
p:$PIECE:=[Link] -- to be moved 219
r:$PIECE -- may be killed 220
-- Case 1: Kill with normal move 221
r := piece([Link]) -- If it exists, it can only be opponent piece. 222
-- Otherwise the move would not be valid. 223
-- Case 2: En Passant. 224
if void(r) and ~void(last move) and [Link] 225
and ~void(last [Link]) and last [Link] 226
and ( last [Link] = [Link] 227
or last [Link] = [Link]) 228
then 229
if ( [Link] and white to play 230
and [Link] = '5' and last [Link] = '7') 231
or ( ~[Link] and ~white to play 232
and [Link] = '4' and last [Link] = '2') 233
then 234
r := last [Link] 235
44
end 236
end 237
if ~void(r) then 238
[Link] := r 239
[Link] := false 240
end 241
[Link] position([Link]) 242
-- Deal with king moves. 243
if [Link] then 244
if white to play and ~white K moved then 245
white K moved := true 246
[Link] chg := true 247
end 248
if ~white to play and ~black K moved then 249
black K moved := true 250
[Link] chg := true 251
end 252
end 253
-- Deal with pawn exchange. 254
if ([Link] and [Link] and white to play and [Link]='8') 255
or ([Link] and ~[Link] and ~white to play and [Link]='1') 256
then 257
case MAIN::[Link] pawn xchg 258
when 'Q' then 259
whitepiecesindex(p)] := #QUEEN([Link],PIECE::white) 260
when 'K' then 261
whitepiecesindex(p)] := #KNIGHT([Link],PIECE::white) 262
else 263
-- Do it fails safe. 264
raise "BOARD:apply move:pawn exchange invalid case entrynn" 265
end 266
[Link] chg := true 267
end 268
-- Deal with castles. 269
if [Link] castle then 270
if white to play then 271
rook ::= piece("a1") 272
[Link] position("d1") 273
else 274
rook ::= piece("a8") 275
[Link] position("d8") 276
end 277
elsif [Link] castle then 278
if white to play then 279
rook ::= piece("h1") 280
[Link] position("f1") 281
else 282
rook ::= piece("h8") 283
[Link] position("f8") 284
end 285
end 286
45
[Link] move := last move 287
last move := move 288
-- Check whether my king is in check after application of the move 289
if my king isin check then 290
-- Although otherwise correct this is an invalid move. 291
-- The original state of the board is reconstructed by calling 292
-- unapply move. 293
ret := false 294
unapply move 295
end 296
white to play := ~white to play 297
return ret 298
end -- of apply move 299
The routine unapply move uses the information that is stored in last move to replay the move, i.e.,
restore the board to the state it had before the application of that move. It depends on the fact
that last move is a valid move except that the king might be in check afterwards.
unapply move is 300
-- Restore killed opponent piece 301
if ~void(last [Link]) then 302
last [Link] := true 303
end 304
-- Restore pawn exchange 305
if last [Link] chg then 306
newpiece ::= piece(last [Link]) 307
whitepiecesindex(newpiece)] := last [Link] 308
end 309
-- Restore move 310
last [Link] position(last [Link]) 311
if last [Link] chg then 312
if white to play then 313
white K moved := false 314
else 315
black K moved := false 316
end 317
end 318
-- Restore castle 319
if last [Link] castle then 320
if white to play then 321
rook ::= piece("d1") 322
[Link] position("a1") 323
else 324
rook ::= piece("d8") 325
[Link] position("a8") 326
end 327
elsif last [Link] castle then 328
if white to play then 329
rook ::= piece("f1") 330
[Link] position("h1") 331
46
else 332
rook ::= piece("f8") 333
[Link] position("h8") 334
end 335
end 336
last move := last [Link] move 337
white to play := ~white to play 338
end -- of unapply move 339
For the automatic player, there must be a way to assign a worth to a board. This is done as follows.
Compute the sum of the worths of all white pieces on the board. Similar, compute the worth of all
black pieces. The value of the board is the ratio of the two values.
The routine board value returns a oating point value, FLT, which is specied in the FLT library.
(See le Library/[Link] for details.)
More complex evaluation functions are known and can be used to replace the simple function
board value. For example, the degree of freedom the pieces have in their movement is an interesting
aspect that might be considered in the evaluation function.
board value:FLT is 340
white value : INT := 0 341
black value : INT := 0 342
loop p::= whitepiece! 343
white value := white value + [Link] 344
end 345
loop p::= blackpiece! 346
black value := black value + [Link] 347
end 348
return white value.t/black value.t 349
end -- of board value 350
end -- of class BOARD 351
47
9 Type $PIECE and 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 species 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).
9.1 Type $PIECE
type $PIECE is 1
alive:BOOL 2
alive(set:BOOL) 3
worth:INT 4
iswhite:BOOL 5
position:POS 6
valid move(to:POS,board:BOARD):BOOL 7
update position(position:POS) 8
update position(position:STR) 9
move!(b:BOARD,to piece:BOOL):POS 10
g:CHAR 11
ispawn : BOOL 12
isrook : BOOL 13
isking : BOOL 14
end -- of type $PIECE 15
48
[Link] := true 37
return ret 38
end 39
private same color(b:BOARD,p:POS):BOOL 40
pre [Link] piece(p) 41
is 42
white piece on pos :BOOL:= [Link] white piece(p) 43
if ( iswhite and white piece on pos) 44
or (~iswhite and ~white piece on pos) then 45
return true 46
else 47
return false 48
end 49
end 50
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 51
ret : BOOL := false 52
loop valid to::=move!(board,ordinary) 53
if to=valid to then ret:=true break! end 54
end 55
return ret 56
end 57
update position(p:POS) is 58
[Link]:=[Link] 59
end 60
update position(pos:STR) is 61
[Link]:=pos 62
end 63
move!(b:BOARD,mode:BOOL):POS is 64
raise "PIECE:dummy code (move!) called" 65
end 66
end -- of class PIECE 67
49
class BISHOP $PIECE is
< 68
include PIECE 69
-- Constants that are different from PIECE implementation: 70
const worth : INT := 3 71
const g : CHAR := 'B' 72
move!(b:BOARD,mode:BOOL):POS is 73
to : POS 74
loop direction::=POS::diag up [Link]!(POS::diag dn left) 75
loop to:=[Link]!(direction) 76
if ~[Link] piece(to) then 77
yield to 78
elsif same color(b,to) and mode=ordinary then 79
break! 80
else 81
yield to 82
break! 83
end 84
end 85
end 86
end -- of move! 87
end -- of class BISHOP 88
50
end -- of class ROOK 112
51
-- skip this move 148
else 149
yield to 150
end 151
end 152
end -- of move! 153
end -- of class KNIGHT 154
52
[Link] := [Link] 189
if mode = for check test then 190
yield to 191
else 192
if [Link] black piece(to) then 193
yield to 194
end 195
end 196
end 197
-- en passant 198
if [Link] = '5' 199
and ~void([Link] move) 200
and [Link] [Link] = '7' 201
and ( [Link] [Link] = [Link] 202
or [Link] [Link] = [Link]) 203
and ~void([Link] [Link]) and [Link] [Link] 204
then 205
if mode = for check test then 206
yield [Link] [Link] 207
else 208
to := #POS 209
[Link] := [Link] [Link] 210
yield to 211
end 212
end 213
-- no more moves 214
quit 215
else -- i.e. if isblack 216
if mode = ordinary then 217
-- vertical steps 218
loop to:=[Link]!(POS::south two) 219
if [Link] piece(to) then -- position and continued way blocked 220
break! 221
end 222
yield to 223
end 224
end 225
-- diag up 226
if [Link] > 'a' then 227
to:=#POS 228
[Link] := [Link] 229
if mode = for check test then 230
yield to 231
else 232
if [Link] white piece(to) then 233
yield to 234
end 235
end 236
end 237
-- diag dn 238
if [Link] 'h' then
< 239
53
to:=#POS 240
[Link] := [Link] 241
if mode = for check test then 242
yield to 243
else 244
if [Link] white piece(to) then 245
yield to 246
end 247
end 248
end 249
-- en passant 250
if [Link] = '4' 251
and ~void([Link] move) 252
and [Link] [Link] = '2' 253
and ( [Link] [Link] = [Link] 254
or [Link] [Link] = [Link]) 255
and ~void([Link] [Link]) and [Link] [Link] 256
then 257
if mode = for check test then 258
yield [Link] [Link] 259
else 260
to := #POS 261
[Link] := [Link] [Link] 262
yield to 263
end 264
end 265
quit 266
end 267
end -- of move! 268
end -- of class PAWN 269
54
if [Link] piece(to) and same color(b,to) and mode = ordinary then 281
-- skip this move 282
else 283
if mode = for check test or ~[Link] in check(to) then 284
yield to 285
end 286
end 287
end 288
-- castle moves 289
spot1, spot2, spot3, rook : $PIECE 290
if [Link] to play and ~[Link] K moved and position = "e1" then 291
-- q-castle 292
spot1:= [Link]("d1") spot2:= [Link]("c1") spot3:= [Link]("b1") 293
rook := [Link]("a1") 294
if ~void(rook) and [Link] and [Link] 295
and void(spot1) and void(spot2) and void(spot3) 296
then 297
to := #POS 298
[Link] := "d1" 299
if ~[Link] in check(to) then 300
[Link] := "c1" 301
if ~[Link] in check(to) then 302
yield to 303
end 304
end 305
end 306
-- k-castle 307
spot1:= [Link]("f1") spot2:= [Link]("g1") rook := [Link]("h1") 308
if ~void(rook) and [Link] and [Link] 309
and void(spot1) and void(spot2) 310
then 311
to := #POS 312
[Link] := "f1" 313
if ~[Link] in check(to) then 314
[Link] := "g1" 315
if ~[Link] in check(to) then 316
yield to 317
end 318
end 319
end 320
end -- castle moves of white king 321
if ~[Link] to play and ~[Link] K moved and position = "e8" then 322
-- q-castle 323
spot1:= [Link]("d8") spot2:= [Link]("c8") spot3:= [Link]("b8") 324
rook := [Link]("a8") 325
if ~void(rook) and [Link] and [Link] 326
and void(spot1) and void(spot2) and void(spot3) 327
then 328
to := #POS 329
[Link] := "d8" 330
if ~[Link] in check(to) then 331
55
[Link] := "c8" 332
if ~[Link] in check(to) then 333
yield to 334
end 335
end 336
end 337
-- k-castle 338
spot1:= [Link]("f8") spot2:= [Link]("g8") rook := [Link]("h8") 339
if ~void(rook) and [Link] and [Link] 340
and void(spot1) and void(spot2) 341
then 342
to := #POS 343
[Link] := "f8" 344
if ~[Link] in check(to) then 345
[Link] := "g8" 346
if ~[Link] in check(to) then 347
yield to 348
end 349
end 350
end 351
end -- castle move of black king 352
end -- of move! 353
end -- of class KING 354
56
10 Suggested Execises
Extend Sather Tutorial Chess to print out all moves of the game in standard chess notation
after the game is over.
If the user decides to have a computer player, the random number generator always is initialized
with the same seed. Extend Sather Tutorial Chess to ask the user for his name. Then from
this name compute a seed to initialize the random number generator.
Introduce a new subtype of $PLAYER that inherits the implementation of MINMAX. Call this
class ALPHABETA and implement an Alpha-Beta-Search to improve the expertise of the auto-
matic player. You might want to change the routine setup of MAIN to create an ALPHABETA
player instead of a MINMAX player.
Change POS to be a value class. Instead of having the internal addressing scheme that
numbers the positions of the board from 0 to 63 in variable absolute, the positions should be
represented with two integers, one for the row number and the other for the number of the
column. Obviously, nearly all routines in POS have to be changed to reect that choice. Other
than that the code is relatively independent of the implementation of POS. There might be
some problems when POS objects are tested to be void. Furthermore, the routine is the only
place outside of [Link] that knows about the internal addressing used in POS. Note, that
the new internal addressing eases the complexity of the computation of neighboring elements
slightly. Instead of divisions and modulo operations, a routine is o! board could be used to
deal with all the necessary plausibility testing.
See section 1.4 for further suggestions.
57
References
1] Robert Henderson and Benjamin Zorn. A comparison of object-oriented programming in four
modern languages. Technical Report CU-CS-641-93, University of Colerado, Boulder, July
1993.
2] Chu-Cheow Lim and A. Stolcke. Sather language design and performance evaluation. Technical
Report TR-91-034, International Computer Science Institute, Berkeley, May 1991.
3] Scott Milton and Heinz W. Schmidt. Dynamic dispatch in object-oriented languages. Techni-
cal Report TR-CS-94-02, CSIRO { Division of Information Technology, Canberra, Australia,
January 1992.
4] Stephan Murer, Stephen Omohundro, and Clemens Szyperski. Sather Iters: Object-oriented
iteration abstraction. Technical Report TR-93-045, International Computer Science Institute,
Berkeley, August 1993.
5] Object-Orientation FAQ. [Link] scg/OOinfo/FAQ.
6] Stephen M. Omohundro. The dierences between Sather and Eiel. Eiel Outlook, 1(1):12{14,
April 1991.
7] Stephen M. Omohundro. Sather's design. Eiel Outlook, 1(3):20{21, August 1991.
8] Stephen M. Omohundro. Sather provides nonproprietary access to object-oriented program-
ming. Computer in Physics, 6(5):444{449, September 1992.
9] Stephen M. Omohundro. The Sather programming language. Dr. Dobb's Journal, 18(11):42{48,
October 1993.
10] Stephen M. Omohundro. The Sather 1.0 specication. Technical Report TR-in preparation,
International Computer Science Institute, Berkeley, 1994.
11] Stephen M. Omohundro and Chu-Cheow Lim. The Sather language and libraries. Technical
Report TR-92-017, International Computer Science Institute, Berkeley, March 1992.
12] Heinz W. Schmidt and Stephen M. Omohundro. CLOS, Eiel, and Sather: A comparison. In
Andreas Paepcke, editor, Object-Oriented Programming: The CLOS Perspective, pages 181{213.
MIT Press Cambridge, Massachusetts, London, England, 1993. Available as ICSI TR-91-047.
13] Clemens Szyperski, Stephen Omohundro, and Stephan Murer. Engineering a programming
language: The type and class system of Sather. In Jurg Gutknecht, editor, Programming Lan-
guages and System Architectures, pages 208{227. Springer Verlag, Lecture Notes in Computer
Science 782, November 1993. Available as technical report ICSI TR-93-064.
58
The DEFAULT class encapsulates decision-making for display mode selection, adhering to the design principles of separation of concerns and dependency injection. This approach abstracts the logic away from direct instantiation, mitigating dependencies on fixed display formats. It exemplifies flexibility and maintainability, allowing the system to easily toggle between graphical and ASCII representations without altering fundamental logic .
In the PAWN class, 'en passant' is a special capture move allowing a pawn to capture an opponent's pawn that has moved two squares forward from its starting position. This move is contingent on specific board conditions like the captured pawn's last move direction and position. Unlike regular pawn movements, which can only advance or capture diagonally, 'en passant' allows capturing on adjacent squares under unique conditions .
The BISHOP class implements movement by considering diagonal directions (up-right, up-left, down-right, and down-left), allowing moves until blocked by another piece. Conversely, the ROOK class examines horizontal and vertical directions. Both classes account for valid moves, respecting the chess rules of movement, reflecting each piece's capability to control specific board areas respectively .
The BISHOP class uses logical checks to validate moves, ensuring moves only progress diagonally unless obstructed by another piece. The validation logic respects opposing pieces' colors and termination upon encountering same-color pieces. Similar validation logic applies to other pieces, like combining direction checks and obstruction handling, ensuring all moves comply with the game's rules .
The DEFAULT class determines which display object to create based on the character ‘d’. If 'd' equals 'X', an X DISPLAY object is created, otherwise, an ASCII DISPLAY object is instantiated. This enables the system to handle both graphical and text-based interfaces dynamically. The dual implementation strategy in DEFAULT allows flexibility and system adaptability concerning environmental constraints and display requirements .
The PAWN class uses conditional logic to distinguish between white and black pawn moves. It applies separate move sequences depending on the pawn's color, calculating legal forward and diagonal moves while addressing specific rules like the first move double-step and 'en passant'. This color-aware mechanism ensures the implementation of chess-specific constraints tailored distinctly for each side, highlighting the nuanced treatment of pawn rules .
The chess board is represented by an array of characters, where each character specifies the piece on a particular board position. Capital characters represent white pieces, while small characters indicate black pieces. This representation is consistent across different routines and classes like ASCII DISPLAY and is fundamental for displaying the board state .
ASCII DISPLAY inherits several routines from CHESS DISPLAY like create, redraw, and update without modification. The inherited routines provide basic board display and update functionalities that apply to both text and potential graphical interfaces. This inheritance enables ASCII DISPLAY to leverage existing code for fundamental operations, reducing redundancy and ensuring consistency in their implementation .
The CHESS DISPLAY class is primarily used as an abstract class to declare common attributes and routines shared by its concrete subclasses. This approach facilitates code reuse and inheritance, allowing classes like ASCII DISPLAY to include these routines without duplicating code. By using CHESS DISPLAY, the design ensures that all subclasses adhere to a specific interface which supports both code maintainability and flexibility .
The include statement in ASCII DISPLAY imports code from CHESS DISPLAY, allowing ASCII DISPLAY to inherit functionality without code duplication. This mechanism ensures ASCII DISPLAY benefits from all abstract attributes and routines of CHESS DISPLAY, fostering modularity and reducing errors associated with maintaining separate implementations for repeating functionalities .