[net.sources.games] Another Othello game

richb@yarra.OZ (Rich Burridge) (01/12/87)

This program is a combined hack of the work of two other people.

It is a combination of the front-end graphics program for
Othello by Ed Falk of Sun Microsystems (Mountain View) plus
the computer strategy of Chris Miller posted a couple of years
ago.

I have included Larry Wall's PATCHLEVEL define to make patching
easier. NOTE that patch 1 has already been applied, (You haven't
got this yet Ed).

This plays a fairly reasonable game of Othello.

I am just about to change jobs, moving to Sun Microsystems here
in Australia, who currently haven't got a machine here on the net.
My account here on yarra will stay, but I probabily won't be able
to read it so readily, therefore please expect a delay in mail
turnaround.

All suggestions, bugs, comments and flames to me please.

Regards Rich.

Rich Burridge,       ARPA: richb%yarra.oz@seismo.css.gov
Sun Microsystems     UUCP: seismo!munnari!yarra.oz!richb
AUSTRALIA.           ACS:  richb@yarra.oz

-------CUT HERE-------CUT HERE---------------CUT HERE--------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	README
#	Makefile
#	black.icon
#	othello.icon
#	white.icon
#	makemove.c
#	otool.c
#	remark.c
#	othello.h
#	othello.remarks
#	patch.1
# This archive created: Mon Jan 12 16:57:20 1987
# By:	Rich Burridge (Sun Microsystems Australia)
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'README'
then
	echo shar: "will not over-write existing file 'README'"
else
cat << \SHAR_EOF > 'README'
                       OTHELLO FOR THE SUN.

This is the board game Othello for the Sun workstation. It is played
on an 8 x 8 board, with pieces which are black on one side and white
on the other. A legal move consists of placing a piece of one's own
color on the board so as to "sandwich" a row (orthogonal or diagonal)
of pieces of the opposite color between the piece just placed and
another piece of the same color. All pieces so sandwiched are flipped
over to reveal the color of the other side.

The object of the game, is to have more pieces than the opponent at
the end of the game (ie. when the board is full or neither side has
a legal move). If you have no legal move, you simply miss a turn.
White goes first.

This version is a combination of the Sun graphics front-end from
Edward Falk of Sun Microsystems, plus the best move computer strategy
and remarks from Chris Miller. I have fixed a couple of bugs and
consolidated the whole lot. By my reckoning, it plays a very
reasonable game.

Regards Rich.

Rich Burridge                  ARPA: richb%yarra.oz@seismo.css.gov
Sun Microsystems               UUCP: seismo!munnari!yarra.oz!richb
AUSTRALIA                      ACS : richb@yarra.oz
SHAR_EOF
fi
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'

LIB       = /usr/richb/done/games/othello
CFLAGS    = -g
DESTDIR   =
BIN       = /usr/games
MODE      = u=rwx,g=rx,o=
DEFS      = -DLIB=\"$(DESTDIR)$(LIB)
CFLAGS    = -g $(DEFS)

LIBS      = -lsuntool -lsunwindow -lpixrect
OBJS      = makemove.o remark.o otool.o
SRCS      = makemove.c remark.c otool.c

otool:  $(OBJS)
	cc $(CFLAGS) -o otool $(OBJS) $(LIBS)

install: otool
	cp otool $(DESTDIR)$(BIN)
	strip $(DESTDIR)$(BIN)/otool
	chmod $(MODE) $(DESTDIR)$(BIN)/otool

backup:
	cp makemove.c makemove.c~
	cp remark.c remark.c~
	cp otool.c otool.c~
	cp othello.h othello.h~

lint:
	lint $(DEFS) $(SRCS) -lsuntool -lsunwindow -lpixrect

makemove.o: makemove.c othello.h
remark.o:   remark.c othello.h
otool.o:    otool.c othello.h
SHAR_EOF
fi
if test -f 'black.icon'
then
	echo shar: "will not over-write existing file 'black.icon'"
else
cat << \SHAR_EOF > 'black.icon'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x000F,0xE000,0x0000,0x0000,0x007F,0xFC00,0x0000,0x0000,
	0x01FF,0xFF00,0x0000,0x0000,0x03FF,0xFF80,0x0000,0x0000,
	0x07FF,0xFFC0,0x0000,0x0000,0x0FFF,0xFFE0,0x0000,0x0000,
	0x1FFF,0xFFF0,0x0000,0x0000,0x3FFF,0xFFF8,0x0000,0x0000,
	0x3FFF,0xFFF8,0x0000,0x0000,0x7FFF,0xFFFC,0x0000,0x0000,
	0x7FFF,0xFFFC,0x0000,0x0000,0x7FFF,0xFFFC,0x0000,0x0000,
	0xFFFF,0xFFFE,0x0000,0x0000,0xFFFF,0xFFFE,0x0000,0x0000,
	0xFFFF,0xFFFE,0x0000,0x0000,0xFFFF,0xFFFE,0x0000,0x0000,
	0xFFFF,0xFFFE,0x0000,0x0000,0xFFFF,0xFFFE,0x0000,0x0000,
	0xFFFF,0xFFFE,0x0000,0x0000,0x7FFF,0xFFFC,0x0000,0x0000,
	0x7FFF,0xFFFC,0x0000,0x0000,0x7FFF,0xFFFC,0x0000,0x0000,
	0x3FFF,0xFFF8,0x0000,0x0000,0x3FFF,0xFFF8,0x0000,0x0000,
	0x1FFF,0xFFF0,0x0000,0x0000,0x0FFF,0xFFE0,0x0000,0x0000,
	0x07FF,0xFFC0,0x0000,0x0000,0x03FF,0xFF80,0x0000,0x0000,
	0x01FF,0xFF00,0x0000,0x0000,0x007F,0xFC00,0x0000,0x0000,
	0x000F,0xE000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
SHAR_EOF
fi
if test -f 'othello.icon'
then
	echo shar: "will not over-write existing file 'othello.icon'"
else
cat << \SHAR_EOF > 'othello.icon'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x0001,0x0001,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x87C1,0x07C1,0x07C1,0x07C1,0x8821,0x0821,0x0FE1,0x0821,
	0x9011,0x1011,0x1FF1,0x1011,0xA009,0x2009,0x3FF9,0x2009,
	0xA009,0x2009,0x3FF9,0x2009,0xA009,0x2009,0x3FF9,0x2009,
	0xA009,0x2009,0x3FF9,0x2009,0xA009,0x2009,0x3FF9,0x2009,
	0x9011,0x1011,0x1FF1,0x1011,0x8821,0x0821,0x0FE1,0x0821,
	0x87C1,0x07C1,0x07C1,0x07C1,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x0001,0x0001,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x07C1,0x07C1,0x0001,0x8001,0x0FE1,0x0821,0x0001,
	0x8001,0x1FF1,0x1011,0x0001,0x8001,0x3FF9,0x2009,0x0001,
	0x8001,0x3FF9,0x2009,0x0001,0x8001,0x3FF9,0x2009,0x0001,
	0x8001,0x3FF9,0x2009,0x0001,0x8001,0x3FF9,0x2009,0x0001,
	0x8001,0x1FF1,0x1011,0x0001,0x8001,0x0FE1,0x0821,0x0001,
	0x8001,0x07C1,0x07C1,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x0001,0x0001,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
	0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x87C1,0x0001,0x0001,0x0001,0x8821,0x0001,0x0001,0x0001,
	0x9011,0x0001,0x0001,0x0001,0xA009,0x0001,0x0001,0x0001,
	0xA009,0x0001,0x0001,0x0001,0xA009,0x0001,0x0001,0x0001,
	0xA009,0x0001,0x0001,0x0001,0xA009,0x0001,0x0001,0x0001,
	0x9011,0x0001,0x0001,0x0001,0x8821,0x0001,0x0001,0x0001,
	0x87C1,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001,
	0x8001,0x0001,0x0001,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF
SHAR_EOF
fi
if test -f 'white.icon'
then
	echo shar: "will not over-write existing file 'white.icon'"
else
cat << \SHAR_EOF > 'white.icon'
/* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16
 */
	0x000F,0xE000,0x0000,0x0000,0x0070,0x1C00,0x0000,0x0000,
	0x0180,0x0300,0x0000,0x0000,0x0200,0x0080,0x0000,0x0000,
	0x0400,0x0040,0x0000,0x0000,0x0800,0x0020,0x0000,0x0000,
	0x1000,0x0010,0x0000,0x0000,0x2000,0x0008,0x0000,0x0000,
	0x2000,0x0008,0x0000,0x0000,0x4000,0x0004,0x0000,0x0000,
	0x4000,0x0004,0x0000,0x0000,0x4000,0x0004,0x0000,0x0000,
	0x8000,0x0002,0x0000,0x0000,0x8000,0x0002,0x0000,0x0000,
	0x8000,0x0002,0x0000,0x0000,0x8000,0x0002,0x0000,0x0000,
	0x8000,0x0002,0x0000,0x0000,0x8000,0x0002,0x0000,0x0000,
	0x8000,0x0002,0x0000,0x0000,0x4000,0x0004,0x0000,0x0000,
	0x4000,0x0004,0x0000,0x0000,0x4000,0x0004,0x0000,0x0000,
	0x2000,0x0008,0x0000,0x0000,0x2000,0x0008,0x0000,0x0000,
	0x1000,0x0010,0x0000,0x0000,0x0800,0x0020,0x0000,0x0000,
	0x0400,0x0040,0x0000,0x0000,0x0200,0x0080,0x0000,0x0000,
	0x0180,0x0300,0x0000,0x0000,0x0070,0x1C00,0x0000,0x0000,
	0x000F,0xE000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,
	0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
SHAR_EOF
fi
if test -f 'makemove.c'
then
	echo shar: "will not over-write existing file 'makemove.c'"
else
cat << \SHAR_EOF > 'makemove.c'

/*  makemove.c
 *
 *  Best computer strategy routines.
 *
 *  Copyright: Chris Miller, 1979, 1981, 1984.
 *
 *  Included with this othello game by
 *  Rich Burridge, Sun Microsystems, Australia - December 1986.
 */

#include "othello.h"

/*  Globals for passing arguments to "sandwich";
 *  this is to save time putting arguments on and off the
 *  stack in a very heavily used piece of code
 */

int s_move,s_row,s_col,s_player,s_opponent,s_flip ;
BOARD *s_pos ;

extern int aspire ;	      /* Computers level of aspiration. */
extern int cretin_flag ;
extern int remark ;	      /* Indicates if remarks to be displayed. */
extern Panel_item remark_mes ;

int expected,margin ;
long ab_count,se_count ;	 /* Calls of alfabeta and static_eval. */


makemove(board,player,depth)
BOARD board ;
int depth,player ;

{
  int d,value ;
  int mv = NOMOVE ;
 
  if (!depth)
    {
      if ((mv = nextmove(&board,NOMOVE,(BOARD *) 0,player)) == NOMOVE)
	return(-1) ;
      else return(mv) ;
    }

  if ((mv = nextmove(&board,NOMOVE,(BOARD *) 0,player)) == NOMOVE)
    {
      if (remark) remark_msg("I'm not dead, just resting") ;
      return(-1) ;
    }
  ab_count = se_count = 0 ;
  if (nextmove(&board,mv,(BOARD *) 0,player) == NOMOVE)
    {
      value = static_eval(&board) ;
      if (remark) make_remark(player,value) ;
      return(mv) ;
    }

/* Exhaustive for last 3*depth ply (at most 10!). */

  if ((board.MOVES_LEFT <= 10) && (board.MOVES_LEFT <= 3*depth))
    d = board.MOVES_LEFT ;
  else d = depth ;
 
  set_aspiration(&board) ;
  value = alfabeta(&board,d,player*INFINITY,player,&mv) ;
  if (remark) make_remark(player,value) ;
  return(mv) ;
}


nextmove(pos,mv,nextpos,player)
BOARD *pos,*nextpos ;
int mv,player ;

{

/*  On first entry for a given position, move is set to NOMOVE,
 *  i.e. -1.  Moves are generated in a rough plausibility order
 *  given by the list structure move_table, whose head is
 *  move_table[-1], and whose tail-pointer is set to NOMOVE.
 *  The order is purely heuristic, and corresponds roughly to
 *  the values associated by static_eval with different squares.
 */

  static int m_table[65] =
    {
       0,  7,  6,  5,  4, 24, 16,  8, 56, 15,
      14, 13, 12, 25, 17, 49, 48, 23, 22, 21,
      20, 26, 42, 41, 40, 31, 30, 29, 28, 35,
      34, 33, 32, 39, 38, 37, 36, 10, 43, 51,
      59, 47, 46, 45, 44, 27, 19, 50, 58, 55,
      54, 53, 52,  1, 11, -1, 57, 63, 62, 61,
      60, 18,  3,  9,  2
    } ;

  static int *move_table = &(m_table[1]) ;

  while ((mv = move_table[mv]) != NOMOVE)
    if (legal(mv,player,pos))
      {
	if (nextpos) domove(pos,mv,nextpos,player) ;

/* Next position generated only if pointer is non-zero */
	return(mv) ;
      }
    return(NOMOVE) ;	/* No more legal moves */
}


domove(pos,mv,nextpos,player)
BOARD *pos,*nextpos ;
int mv,player;

{
  register int i ;
  if (pos != nextpos) FOR_BOARD(i) nextpos->SQUARE[i] = pos->SQUARE[i] ;

  s_move = mv ;
  s_row = mv >> 3 ;
  s_col = mv & 7 ;
  s_player = player ;
  s_opponent = -player ;
  s_flip = TRUE ;
  s_pos = nextpos ;

  nextpos->SQUARE[s_move] = player ;

  (void) sandwich(-9) ;
  (void) sandwich(-8) ;
  (void) sandwich(-7) ;
  (void) sandwich(-1) ;
  (void) sandwich(1) ;
  (void) sandwich(7) ;
  (void) sandwich(8) ;
  (void) sandwich(9) ;

  nextpos->MOVES_LEFT = (pos->MOVES_LEFT) - 1 ;
}


legal(mv,player,pos)
BOARD *pos ;
int mv,player ;

{
  if (pos->SQUARE[mv]) return(FALSE) ;     /* Already occupied */

  s_move = mv ;
  s_row = mv >> 3 ;
  s_col = mv & 7 ;
  s_player = player ;
  s_opponent = -player ;
  s_flip = FALSE ;
  s_pos = pos ;

  return(sandwich(-9) || sandwich(-8) || sandwich(-7) || sandwich(-1) ||
	 sandwich(1)  || sandwich(7)  || sandwich(8)  || sandwich(9)) ;
}


sandwich(increment)
register int increment ;

/*  Test whether the square move sandwiches a line
 *  of enemy pieces in the direction [row_inc, col_inc];
 *  If (s_flip) then update position by capturing such pieces
 */

{
  register int square,offset ;
  int row_offset,col_offset,piece,piece_count ;

  if (s_pos->SQUARE[s_move+increment] != s_opponent) return(FALSE) ;
 
/*  Quick test to catch most failures -
 *  note that the tested square may not even
 *  be on the board, but the condition is a
 *  sufficient one for failure.
 */
 
  row_offset = (increment < -1 ? s_row  :      /* inc -1: -9, -8, -7 */
		increment >  1 ? 7-s_row:      /* inc  1:  7,  8,  9 */
		8) ;
  col_offset = (increment & 4 ? s_col   :      /* inc -1: -9, -1,  7 */
		increment & 1 ? 7-s_col :      /* inc  1: -7,  1,  9 */
		8) ;
 
  offset = (row_offset > col_offset ? col_offset : row_offset) ;

/* offset = shortest distance to an edge in the direction of search */

  if (2 > offset) return(FALSE) ;
 
  piece_count = 1 ;
  square = s_move+increment ;
 
  while (--offset)
    {
      if (!(piece = s_pos->SQUARE[square += increment]))
	return(FALSE) ;	/* If empty square, give up */
 
      if (piece == s_player) break ;
      else piece_count++ ;      /* Count opponent's pieces encountered */
    }

  if (!offset) return(FALSE) ;

  if (s_flip)
    while (piece_count--)
      s_pos->SQUARE[square -= increment] = s_player ;

  return (TRUE) ;
}


alfabeta(pos,depth,parent_opt,sign,parent_best)
BOARD *pos ;
int depth,parent_opt,sign,*parent_best ;

/*  Alpha-beta pruning algorithm.
 *
 *  If sign = 1, then try to MAXIMISE score, else MINIMISE.
 *  Parent node wants to MINIMISE/MAXIMISE, so as soon as
 *  we exceed parent_opt, give up and return INFINITY.
 *  Return best move in parent_best.
 *
 *  Externals used:
 *      static_eval(position)
 *      ISMOVE(position,player)   [does player have a legal move?]
 *      game_ended(pos)
 *      typedef BOARD
 *      aspiration(player,depth,position)
 *	[return the aspiration limit for next level of search]
 *      nextmove(position,mv,nextposition,player)
 *	[give the next legal move for player in position;
 *	 put the resulting position in nextposition]
 */
 
{
  BOARD nextpos ;
  int value,this_opt,this_best = NOMOVE,mv = NOMOVE,asp = 0 ;
 
  if ((depth==0) || game_ended(pos))
    {
      value = static_eval(pos) ;
      *parent_best = NOMOVE ;
      if ((sign*value) > (sign*parent_opt)) return(sign*INFINITY) ;
      else return (value) ;
    }

  ab_count++ ;     /* Record boards dynamically evaluated */
  this_opt = (sign == 1 ? -INFINITY : INFINITY) ;

  if (!ISMOVE(pos,sign))	  /* No legal move */
    {
      value = alfabeta(pos,depth,this_opt,-sign,&this_best) ;
      goto valfound ;
    }

  asp = sign * aspiration(sign,depth) ;

  while ((mv = nextmove(pos,mv,&nextpos,sign)) != NOMOVE)
    {
      value = alfabeta(&nextpos,depth-1,this_opt,-sign,&this_best) ;

valfound:

      if ((sign*value) >= (sign*parent_opt)) return(sign*INFINITY) ;

      if ((sign*value) > asp)
	{
	  *parent_best = mv ;
	  return(value) ;
	}

      if ((sign*value) > (sign*this_opt))
	{
	  this_opt = value ;
	  *parent_best = mv ;
	}

/*  If two moves have same evaluation, choose
 *  uniformly randomly between them (of course,
 *  where several moves have same value, this
 *  is biased towards the later ones
 */

      if ((value == this_opt) && (rand() & 020)) *parent_best = mv ;
    }
  return(this_opt) ;
}


set_aspiration(position)
BOARD *position ;

/*  Aspirations are as follows:
 *  Aspiration-level	     Aspiration
 *      1	       The worse of 0, static value
 *      2	       The static value
 *      3	       10% better than the static value
 *      4	       25% better than the static value
 *      5	       Any winning move
 *      6	       The move winning by the greatest margin
 *
 *  It is assumed that the opponent has
 *  the same level of aspiration as the program.
 */
 
{
  if (aspire == MAXASPIRE)
    {
      expected = 0 ;
      margin = INFINITY ;
      return ;
    }
 
  if (aspire == (MAXASPIRE-1))
    {
      expected = 0 ;
      margin = INFINITY-64 ;
      return ;
    }
 
  expected = static_eval(position) ;
 
  switch (aspire)
    {
      case 1 : expected /= 2 ;
	       margin = -abs(expected) ;
	       return ;
      case 2 : margin = 0 ;
	       return ;
      case 3 : margin = abs(expected/10) ;
	       return ;
      case 4 : margin = abs(expected/4) ;
	       return ;
    }
}


aspiration(player,depth)
int player,depth ;

{
  if (aspire == MAXASPIRE) return(player*INFINITY) ;
  if (depth < 3 && aspire > 1) return(player*(INFINITY-64)) ;
  return(expected+player*margin) ;
}


static_eval(pos)
BOARD *pos ;

/*  Square values (only valid when a "vulnerable" piece occupies the
 *  square in question)
 */

#define  VCORN   100	   /* Corner */
#define  VORTH   -30	   /* Orthogonally next to corner */
#define  VDIAG   -50	   /* Diagonally     "   "    "   */
#define  VEDGE   20	    /* On edge */
#define  VNEXT   -7	    /* Next to edge */
#define  VNORM   1	     /* Elsewhere */
		 
{
  static int model[64] = {
	   VCORN, VORTH, VEDGE, VEDGE, VEDGE, VEDGE, VORTH, VCORN,
	   VORTH, VDIAG, VNEXT, VNEXT, VNEXT, VNEXT, VDIAG, VORTH,
	   VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE,
	   VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE,
	   VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE,
	   VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE,
	   VORTH, VDIAG, VNEXT, VNEXT, VNEXT, VNEXT, VDIAG, VORTH,
	   VCORN, VORTH, VEDGE, VEDGE, VEDGE, VEDGE, VORTH, VCORN
	 } ;
 
  register int i ;
  register int value = 0 ;
  int scores[64] ;

  se_count++ ;	       /* Record number of static evaluations */
  if (game_ended(pos))
    {
      FOR_BOARD(i) value += pos->SQUARE[i] ;
      return (value > 0 ? INFINITY+value-64  :
	      value < 0 ? -INFINITY+value+64 : 0) ;
    }

  FOR_BOARD(i) scores[i] = 0 ;

  find_invulnerable(pos,scores) ;

/*  Scores now contains VINVUL (or -VINVUL)
 *  for each invulnerable piece, and 0 elsewhere;
 *  now fill in other evaluations (special cases:
 *  next to corner [bad!], on edge[good!], next to
 *  edge[poor], anywhere else[boring])
 */

  FOR_BOARD(i) value += (scores[i] ? scores[i] : model[i]*pos->SQUARE[i]) ;
  return (value) ;
}


game_ended(pos)
BOARD *pos ;

{
  if (!(pos->MOVES_LEFT)) return(TRUE) ;
  return((!ISMOVE(pos,WHITE)) && !(ISMOVE(pos,BLACK))) ;
}


find_invulnerable(board,scores)
BOARD *board ;
int scores[64] ;

/*  This function finds invulnerable pieces, and scores them
 *  appropriately; it does not find ALL invulnerable pieces -
 *  in fact, only concave blocks including a corner - but
 *  nevertheless should provide a good approximation.
 */

{
  int hwm,corner,value,i,j ;

  if ((corner = board->SQUARE[0]) != 0)
    {
      value = corner*VINVUL ;
      hwm = 7 ;

      for (i = 0; i < 56; i += 8)
	{
	  if (board->SQUARE[i] != corner) break ;
	  scores[i] = value ;
	  for (j = 1; j < hwm; j++)
	    {
	      if (board->SQUARE[i+j] != corner)
		{
		  hwm = j ;
		  break ;
		}
	      scores[i+j] = value ;
	    }
	}
      scores[0] = corner*VCORN ;
    }

  if ((corner = board->SQUARE[7]) != 0)
    {
      value = corner*VINVUL ;
      hwm = 0 ;

      for (i = 0; i < 56; i+= 8)
	{
	  if (board->SQUARE[i+7] != corner) break ;
	  scores[i+7] = value ;
	  for (j = 6; j > hwm; j--)
	    {
	      if (board->SQUARE[i+j] != corner)
		{
		  hwm = j ;
		  break ;
		}
	      scores[i+j] = value ;
	    }
	}
      scores[7] = corner*VCORN;
    }

  if ((corner = board->SQUARE[56]) != 0)
    {
      value = corner*VINVUL ;
      hwm = 7 ;

      for (i = 56; i > 0; i -= 8)
	{
	  if (board->SQUARE[i] != corner) break ;
	  scores[i] = value ;
	  for (j = 1; j < hwm; j++)
	    {
	      if (board->SQUARE[i+j] != corner)
		{
		  hwm = j ;
		  break ;
		}
	      scores[i+j] = value ;
	    }
	}
      scores[56] = corner*VCORN ;
    }

  if ((corner=board->SQUARE[63]) != 0)
    {
      value = corner*VINVUL ;
      hwm = 0 ;

      for (i = 56; i > 0; i -= 8)
	{
	  if (board->SQUARE[i+7] != corner) break ;
	  scores[i+7] = value ;
	  for (j = 6; j > hwm; j--)
	    {
	      if (board->SQUARE[i+j] != corner)
		{
		  hwm = j ;
		  break ;
		}
	      scores[i+j] = value ;
	    }
	}
      scores[63] = corner*VCORN ;
    }
}
SHAR_EOF
fi
if test -f 'otool.c'
then
	echo shar: "will not over-write existing file 'otool.c'"
else
cat << \SHAR_EOF > 'otool.c'
/* othello tool @(#)otool.c     1.3 12/10/86 */

/*
 *       @@@   @@@@@  @   @  @@@@@  @      @       @@@
 *      @   @    @    @   @  @      @      @      @   @
 *      @   @    @    @@@@@  @@@    @      @      @   @
 *      @   @    @    @   @  @      @      @      @   @
 *       @@@     @    @   @  @@@@@  @@@@@  @@@@@   @@@
 *
 *      OTHELLO - graphics front end to othello game
 *
 *      Edward A. Falk
 *      Sun Microsystems
 *
 *      Various bugfixes, changes and additions by
 *      Rich Burridge, Sun Microsystems, Australia
 *
 *      December, 1986
 */

#include "othello.h"

#define  PATCHLEVEL     1

/* Compilation parameters: */

#define  CELL_WIDTH     40
#define  PIECE_MARGIN   5
#define  PIECE_RAD      (CELL_WIDTH/2 - PIECE_MARGIN)
 
#ifndef SUN2
#define  MOVE_DELTA     1       /* For animation */
#else
#define  MOVE_DELTA     3
#endif
 
#define TOTAL_WIDTH     BOARDSIZE*CELL_WIDTH

/* Constants, typedefs, externals, globals, statics, macros. */

static short icon_image[] = {		       /* Main icon. */
#include "othello.icon"
} ;
DEFINE_ICON_FROM_IMAGE(othello_icon,icon_image) ;

static short hglass_image[] = {
#include <images/hglass.cursor>
} ;
mpr_static(hglass_pr,16,16,1,hglass_image) ;

static short white_image[]  = {
#include "white.icon"
} ;
mpr_static(white_icon_pr,64,64,1,white_image) ;

static short black_image[]  = {
#include "black.icon"
} ;
mpr_static(black_icon_pr,64,64,1,black_image) ;

char w_who[3][9] = { "You", "Computer", "White" } ;

char w_be[3][4] = { "are", "is", "is" } ;

char b_who[3][9] = { "Computer", "You", "Black" } ;

char b_be[3][4] = { "is", "are", "is" } ;

static Frame base_frame ;       /* Assorted globals. */
static Panel panel ;
static Canvas canvas ;

static Panel_item last_but, new_game_but, quit_but, suggest_but ;
static Panel_item panel_mes, score_mes ;
static Panel_item computer_plays_choice, remark_choice ;
static Panel_item aspiration_choice, difficulty_choice ;

static Cursor canvas_cursor,hglass_cursor ;

static Pixwin *canvas_pw ;

static enum cmode {white_start, white_moving,
		   black_start, black_moving, game_over } canvas_mode ;
static enum pmode {play_black, play_white, play_both} play_mode ;

static int piece_x,piece_y ;      /* Current position of moving piece */

/* Variables used externally. */
int aspire ;		      /* Computers level of aspiration. */
int cretin_flag ;
int last_remark ;
int remark ;		      /* Indicates if remarks are to be displayed. */
Panel_item remark_mes ;

static int last_move ;	    /* Last valid computer move. */
static int move ;		 /* Current move being evaluated. */
static int next_player ;	  /* Next player (BLACK or WHITE) to move. */
static int suggestion ;	   /* Positive if a suggested move. */
static int suggest_x,suggest_y ;  /* Position of suggested move.*/
static int depth ;		/* Depth of search for computers move. */

BOARD old_board ;		 /* The previous Othello board. */
BOARD board ;		     /* The Othello board. */

static char line[40] ;

static void canvas_proc() ;       /* Proc definitions. */

static void last_proc(), new_game_proc(), quit_proc(), suggest_proc() ;
static void computer_plays_proc(), remark_proc() ;
static void aspiration_proc(), difficulty_proc() ;

static void init_panel() ;
static void canvas_init() ;


main(argc,argv)
int argc ;
char *argv[] ;

{
  (void) sprintf(line," Othello.  V1.%1d",PATCHLEVEL) ;
  base_frame = window_create(NULL,	  FRAME,
			     FRAME_ICON,    &othello_icon,
			     FRAME_LABEL,   line,
			     FRAME_SUBWINDOWS_ADJUSTABLE, FALSE,
			     WIN_ERROR_MSG, "Can't create window.",
			     FRAME_ARGS,    argc,argv,
			     0) ;

  panel = window_create(base_frame,   PANEL,
			WIN_WIDTH,    TOTAL_WIDTH+1,
			PANEL_LAYOUT, PANEL_HORIZONTAL,
			0) ;

  init_panel() ;
  window_fit_height(panel) ;

  canvas = window_create(base_frame,	     CANVAS,
			 WIN_X,		  0,
			 WIN_BELOW,	      panel,
			 WIN_CONSUME_KBD_EVENT,  WIN_NO_EVENTS,
			 WIN_EVENT_PROC,	 canvas_proc,
			 WIN_HEIGHT,	     TOTAL_WIDTH+1,
			 WIN_WIDTH,	      TOTAL_WIDTH+1,
			 WIN_IGNORE_PICK_EVENTS, KBD_REQUEST, WIN_ASCII_EVENTS, 0,
			 CANVAS_FAST_MONO, TRUE,
			 0) ;

  window_fit(base_frame) ;
  canvas_pw = (Pixwin *) window_get(canvas,CANVAS_PIXWIN) ;

  canvas_cursor = window_get(canvas,WIN_CURSOR) ;
  hglass_cursor = cursor_create(CURSOR_IMAGE,&hglass_pr,0) ;

  remark = TRUE ;
  last_move = -1 ;
  play_mode = play_black ;
  canvas_mode = white_start ;
  initboard() ;
  get_remarks() ;
  aspire = INIT_ASPIRE ;
  depth = INIT_DEPTH ;
  cretin_flag = FALSE ;
  last_remark = FIRST_REMARK ;	/* Force a remark the first time. */

  canvas_init() ;
  window_main_loop(base_frame) ;
  exit(0) ;
}


initboard()    /* Initialise the othello board. */

{
  int i ;

  FOR_BOARD(i) old_board.SQUARE[i] = board.SQUARE[i] = FREE ;
  board.SQUARE[27] = BLACK ;
  board.SQUARE[28] = WHITE ;
  board.SQUARE[35] = WHITE ;
  board.SQUARE[36] = BLACK ;
  board.MOVES_LEFT = 60 ;
}


static void init_panel()

{
  last_but = panel_create_item(panel,PANEL_BUTTON,
	       PANEL_NOTIFY_PROC,last_proc,
	       PANEL_LABEL_IMAGE,
		 panel_button_image(panel,"last",4,(struct pixfont *) 0),
	       0) ;

  new_game_but = panel_create_item(panel,PANEL_BUTTON,
		   PANEL_NOTIFY_PROC,new_game_proc,
		   PANEL_LABEL_IMAGE,
		     panel_button_image(panel,"new game",8,(struct pixfont *) 0),
		   0) ;

  quit_but = panel_create_item(panel,PANEL_BUTTON,
	       PANEL_NOTIFY_PROC,quit_proc,
	       PANEL_LABEL_IMAGE,
		 panel_button_image(panel,"quit",4,(struct pixfont *) 0),
	       0) ;

  suggest_but = panel_create_item(panel,PANEL_BUTTON,
		  PANEL_NOTIFY_PROC,suggest_proc,
		  PANEL_LABEL_IMAGE,
		    panel_button_image(panel,"suggest",7,(struct pixfont *) 0),
		  0) ;

  aspiration_choice = panel_create_item(panel,PANEL_CYCLE,
			PANEL_NOTIFY_PROC,aspiration_proc,
			PANEL_LABEL_BOLD,TRUE,
			PANEL_LABEL_STRING,"Aspiration: ",
			PANEL_VALUE,2,
			PANEL_SHOW_MENU,TRUE,
			PANEL_CHOICE_STRINGS,"1","2","3","4","5","6",0,
			PANEL_DISPLAY_LEVEL,PANEL_CURRENT,
			0) ;

  difficulty_choice = panel_create_item(panel,PANEL_CYCLE,
			PANEL_NOTIFY_PROC,difficulty_proc,
			PANEL_LABEL_BOLD,TRUE,
			PANEL_LABEL_STRING,"Difficulty: ",
			PANEL_VALUE,0,
			PANEL_SHOW_MENU,TRUE,
			PANEL_CHOICE_STRINGS,"1","2","3","4",0,
			PANEL_DISPLAY_LEVEL,PANEL_CURRENT,
			0) ;
 
  computer_plays_choice = panel_create_item(panel,PANEL_CYCLE,
			    PANEL_NOTIFY_PROC,computer_plays_proc,
			    PANEL_LABEL_BOLD,TRUE,
			    PANEL_LABEL_STRING,"Computer plays: ",
			    PANEL_VALUE,0,
			    PANEL_SHOW_MENU,TRUE,
			    PANEL_CHOICE_STRINGS,"black","white","both",0,
			    PANEL_DISPLAY_LEVEL,PANEL_CURRENT,
			    0) ;

  remark_choice = panel_create_item(panel,PANEL_CYCLE,
		    PANEL_NOTIFY_PROC,remark_proc,
		    PANEL_LABEL_BOLD,TRUE,
		    PANEL_LABEL_STRING,"Remarks: ",
		    PANEL_VALUE,1,
		    PANEL_SHOW_MENU,TRUE,
		    PANEL_CHOICE_STRINGS,"off","on",0,
		    PANEL_DISPLAY_LEVEL,PANEL_CURRENT,
		    0) ;
 
  remark_mes = panel_create_item(panel,PANEL_MESSAGE,
		 PANEL_LABEL_STRING,"                               ",
                 0) ;

  panel_mes = panel_create_item(panel,PANEL_MESSAGE,
		PANEL_LABEL_STRING,"To move, use your left mouse button",
		0) ;

  score_mes = panel_create_item(panel,PANEL_MESSAGE,
		PANEL_LABEL_STRING,"White:  2, Black:  2",
		0) ;
}


/*ARGSUSED*/
static void
quit_proc(item,event)
Panel_item item ;
Event *event ;

{
  window_done(base_frame) ;
}


static void
suggest_proc()

{
  if (canvas_mode == white_start)
    suggestion = makemove(board,WHITE,depth) ;
  else suggestion = makemove(board,BLACK,depth) ;
  suggest_x = ((suggestion & 7) + 1) * CELL_WIDTH - CELL_WIDTH / 2 ;
  suggest_y = ((suggestion >> 3) + 1) * CELL_WIDTH - CELL_WIDTH / 2 ;
  pw_vector(canvas_pw,suggest_x-5,suggest_y-5,
		      suggest_x+5,suggest_y+5,RSRC,0xff) ;
  pw_vector(canvas_pw,suggest_x-5,suggest_y+5,
		      suggest_x+5,suggest_y-5,RSRC,0xff) ;
}


static void
draw_piece(player,x,y,rop)
int player,x,y,rop ;

{
  switch (player)
    {
      case WHITE : pw_write(canvas_pw,x,y,32,32,rop,&white_icon_pr,0,0) ;
		   break ;
      case BLACK : pw_write(canvas_pw,x,y,32,32,rop,&black_icon_pr,0,0) ;
    }
}


static void
last_proc()

{
  int flips,i,player,rop,x,y ;

  player = (canvas_mode == white_start) ? BLACK : WHITE ;
  for (flips = 0; flips < 4; flips++)
    {
      x = (last_move & 7)*CELL_WIDTH + PIECE_MARGIN ;
      y = (last_move >> 3)*CELL_WIDTH + PIECE_MARGIN ;
      rop = flips % 2 ? RSRC : RCLR ;
      draw_piece(player,x,y,rop) ;
      PAUSE(i) ;
    }
}


/*ARGSUSED*/
static void
difficulty_proc(item,value,event)
Panel_item item ;
int value ;
Event *event ;

{
  depth = value + INIT_DEPTH ;
}


/*ARGSUSED*/
static void
remark_proc(item,value,event)
Panel_item item ;
int value ;
Event *event ;

{
  remark = value ;
  if (remark) remark_msg("OK, I'll make remarks") ;
  else remark_msg("OK, I'll shut up") ;
}


/*ARGSUSED*/
static void
aspiration_proc(item,value,event)
Panel_item item ;
int value ;
Event *event ;

{
  aspire = value ;
}


count(board,player)     /* Count the number of player pieces on the board. */
int player ;
BOARD board ;

{
  int i,n ;

  n = 0 ;
  FOR_BOARD(i)
    if (board.SQUARE[i] == player) n++ ;
  return(n) ;
}


static void
update_board_image()
 
{
  int flips,i,j,rop,x,y ;
 
  if (suggestion != -1)
    {
      pw_vector(canvas_pw,suggest_x-5,suggest_y-5,
			  suggest_x+5,suggest_y+5,RCLR,0xff) ;
      pw_vector(canvas_pw,suggest_x-5,suggest_y+5,
			  suggest_x+5,suggest_y-5,RCLR,0xff) ;
      suggestion = -1 ;
    } 
  for (flips = 0; flips < 4; flips++)
    {
      pw_batch_on(canvas_pw) ;
      rop = flips % 2 ? RSRC : RCLR ;
      FOR_BOARD(i)
	{
	  if (board.SQUARE[i] != old_board.SQUARE[i])
	    {
	      x = (i & 7) * CELL_WIDTH + PIECE_MARGIN ;
	      y = (i >> 3) * CELL_WIDTH + PIECE_MARGIN ;
	      draw_piece(board.SQUARE[i],x,y,rop) ;
	    }   
	}   
      pw_batch_off(canvas_pw) ;
      PAUSE(j) ;
    }
  score_msg(sprintf(line,"White: %2d, Black: %2d",
			  count(board,WHITE),count(board,BLACK))) ;
}


static void
canvas_init()

{
  int x0 ;

  pw_writebackground(canvas_pw,0,0,TOTAL_WIDTH+1,TOTAL_WIDTH+1,RSRC) ;

  pw_batch_on(canvas_pw) ;
  for (x0 = 0; x0 <= TOTAL_WIDTH; x0 += CELL_WIDTH)
    {
      pw_vector(canvas_pw,x0,0,x0,TOTAL_WIDTH,RSRC,0xff) ;
      pw_vector(canvas_pw,0,x0,TOTAL_WIDTH,x0,RSRC,0xff) ;
    }
  pw_batch_off(canvas_pw) ;
  update_board_image() ;
}


static void
animate_move(move)
int move ;

{
  int x0,y0,x1,y1,x,y,dx,dy,ctr ;
  static struct rect r = {0,0,TOTAL_WIDTH+1,TOTAL_WIDTH+1} ;

  pw_lock(canvas_pw,&r) ;
  dx = x1 = (move & 7) * CELL_WIDTH + PIECE_MARGIN ;
  dy = y1 = (move >> 3) * CELL_WIDTH + PIECE_MARGIN ;
  if (x1 > y1)
    {
      ctr = dx/2 ;
      x = 0 ;
      y = 0 ;
      draw_piece(WHITE,x,y,RINV) ;
      while (x < x1)
	{
	  x0 = x ;
	  y0 = y ;
	  x += MOVE_DELTA ;
	  if ((ctr -= dy) < 0)
	    {
	      ctr += dx ;
	      y += MOVE_DELTA ; 
	    }
	  draw_piece(WHITE,x,y,RINV) ;
	  draw_piece(WHITE,x0,y0,RINV) ;
	}
      draw_piece(WHITE,x,y,RINV) ;
    }
  else
    {
      ctr = dy/2 ;
      x = 0 ;
      y = 0 ;
      draw_piece(WHITE,x,y,RINV) ;
      while (y < y1)
	{
	  x0 = x ;
	  y0 = y ;
	  y += MOVE_DELTA ;
	  if ((ctr -= dx) < 0)
	    {
	      ctr += dy ;
	      x += MOVE_DELTA ; 
	    }
	  draw_piece(WHITE,x,y,RINV) ;
	  draw_piece(WHITE,x0,y0,RINV) ;
	}
      draw_piece(WHITE,x,y,RINV) ;
    }
  pw_unlock(canvas_pw) ;
}


static void
who_wins()

{
  int cs,ps ;

  ps = count(board,WHITE) ;
  cs = count(board,BLACK) ;
  if (ps > cs) score_msg(sprintf(line,"White wins %d-%d",ps,cs)) ;
  else if (ps == cs) score_msg(sprintf(line,"A tie %d-%d",ps,cs)) ;
  else score_msg(sprintf(line,"Black wins %d-%d",cs,ps)) ;
  if (cretin_flag) remark_msg("*** CRETIN! ***") ;
  else remark_msg("") ;
}


do_move(player,who)
int player ;
char who[3][9] ;

{
  int taken ;		/* Number of pieces flipped this go. */

  taken = formfliplist(move,player) ;
  update_board_image() ;
  if (taken == 1)
    panel_msg(sprintf(line,"%s took 1 piece",who[(int) play_mode])) ;
  else panel_msg(sprintf(line,"%s took %d pieces",who[(int) play_mode],taken)) ;
}


formfliplist(move,player)
int move,player ;

{
  int cnt,i,old_cnt ;

  old_cnt = count(board,player) ;
  FOR_BOARD(i) old_board.SQUARE[i] = board.SQUARE[i] ;
  old_board.MOVES_LEFT = board.MOVES_LEFT ;
  domove(&old_board,move,&board,player) ;
  cnt = count(board,player) ;
  return(cnt - old_cnt - 1) ;
}


check(player,who,be)
int player ;
char who[3][9],be[3][4] ;

/*  This routine checks to see if a move can be made for this player.
 *  If not, various checks are made to see if the game is finished.
 *  Return value indicates if a move can be made.
 */

{
  if ((!count(board,BLACK)) || (!count(board,WHITE)) ||
      ((count(board,BLACK) + count(board,WHITE)) == 64))
    {
      who_wins() ;
      canvas_mode = game_over ;
      panel_msg("Game over") ;
      return(FALSE) ;
    }
  if ((move = makemove(board,player,0)) == NOMOVE)
    {
      panel_msg(sprintf(line,"%s %s forced to pass",
			who[(int) play_mode],be[(int) play_mode])) ;
      if ((move = makemove(board,OPPONENT(player),0)) == NOMOVE)
	{
	  who_wins() ;
	  canvas_mode = game_over ;
	  panel_msg("Game over") ;
	}
      return(FALSE) ;
    }   
  return(TRUE) ;
}


think(player,who)
int player ;
char who[3][9] ;

{
  window_set(canvas,WIN_CURSOR,hglass_cursor,0) ;
  move = makemove(board,player,depth) ;
  window_set(canvas,WIN_CURSOR,canvas_cursor,0) ;
  animate_move(move) ;
  do_move(player,who) ;
  last_move = move ;
  canvas_mode = (enum cmode) (OPPONENT(player) + 1) ;
}


move_and_check(player,who,be,opp_who,opp_be)
int player ;
char who[3][9],be[3][4],opp_who[3][9],opp_be[3][4] ;

{
  do_move(player,who) ;
  for (;;)
    if (check(OPPONENT(player),opp_who,opp_be) == TRUE)
      {
	think(OPPONENT(player),opp_who) ;
	if (check(player,who,be) == TRUE) break ;
	if (canvas_mode == game_over) break ;
      }
    else
      { 
	canvas_mode = (enum cmode) ((int) canvas_mode - 1) ;
	return ;
      }
}


do_action(event)
Event *event ;

{
  int cx,cy ;

  switch (event_id(event))
    {
      case LOC_MOVE       :
      case LOC_DRAG       :
      case LOC_TRAJECTORY :
	draw_piece(next_player,piece_x,piece_y,RINV) ;
	piece_x = event_x(event) - PIECE_RAD ;
	piece_y = event_y(event) - PIECE_RAD ;
	draw_piece(next_player,piece_x,piece_y,RINV) ;
	break ;

      case LOC_WINENTER   :
      case LOC_WINEXIT    :
      case LOC_RGNENTER   :
      case LOC_RGNEXIT    :
      case WIN_STOP       : draw_piece(next_player,piece_x,piece_y,RINV) ;
	canvas_mode = (enum cmode) ((int) canvas_mode - 1) ;
	break ;

      case MS_LEFT	:
	cursor_set(canvas_cursor,CURSOR_SHOW_CURSOR,TRUE,0) ;
	window_set(canvas,WIN_CURSOR,canvas_cursor,0) ;
	if (event_is_down(event))
	  {
	    draw_piece(next_player,piece_x,piece_y,RINV) ;
	    canvas_mode = (enum cmode) ((int) canvas_mode - 1) ;
	  }
	else
	  { 
	    draw_piece(next_player,piece_x,piece_y,RINV) ;
	    cx = (piece_x + PIECE_RAD) / CELL_WIDTH ;
	    cy = (piece_y + PIECE_RAD) / CELL_WIDTH ;
	    move = cy * BOARDSIZE + cx ;
	    if (legal(move,next_player,&board) == FALSE)
	      {
		panel_msg("Invalid move") ;
		canvas_mode = (enum cmode) ((int) canvas_mode - 1) ;
	      }
	    else
	      { 
		if (canvas_mode == white_moving)
		  move_and_check(WHITE,w_who,w_be,b_who,b_be) ;
		else move_and_check(BLACK,b_who,b_be,w_who,w_be) ;
	      }    
	  }    
	break ;
    }
}


/*ARGSUSED*/
static void
canvas_proc(win,event,arg)
Canvas  win ;
Event *event ;
caddr_t arg ;

{
  switch (canvas_mode)
    {
      case white_start  :
      case black_start  : next_player = (int) canvas_mode - 1 ;
			  if (event_id(event) == MS_LEFT && event_is_down(event))
			    {
			      cursor_set(canvas_cursor,CURSOR_SHOW_CURSOR,FALSE,0) ;
			      window_set(canvas,WIN_CURSOR,canvas_cursor,0) ;
			      piece_x = event_x(event) - PIECE_RAD ;
			      piece_y = event_y(event) - PIECE_RAD ;
			      draw_piece(next_player,piece_x,piece_y,RINV) ;
			     canvas_mode = (enum cmode) ((int) canvas_mode + 1) ;
			    } 
			  break ;
      case white_moving :
      case black_moving : do_action(event) ;
			  break ;

      case game_over    : panel_msg("Game over") ;
			  break ;
    }
}


/*ARGSUSED*/
static void
computer_plays_proc(item,value,event)
Panel_item item ;
int value ;
Event *event ;

{
  int old_remark ;

  switch (value)
    {
      case 0 : play_mode = play_black ;
	       if (canvas_mode != game_over)
		 if (check(BLACK,b_who,b_be) == TRUE)
		   think(BLACK,b_who) ;
	       break ;

      case 1 : play_mode = play_white ;
	       if (canvas_mode != game_over)
		 if (check(WHITE,w_who,w_be) == TRUE)
		   think(WHITE,w_who) ;
	       break ;

      case 2 : if (play_mode != play_both)
		 {
		   play_mode = play_both ;
		   old_remark = remark ;
		   remark = 0 ;
		   while (play_mode == play_both && canvas_mode != game_over)
		     {
		       if (canvas_mode == white_start)
			 {
			   for (;;)
			     if (check(WHITE,w_who,w_be) == TRUE)
			       {
				 think(WHITE,w_who) ;
				 canvas_mode = black_start ;
				 if (check(BLACK,b_who,b_be) == TRUE) break ;
				 if (canvas_mode == game_over) break ;
				 canvas_mode = white_start ;
			       }
			 }
		       else
			 {
			   for (;;)
			     if (check(BLACK,b_who,b_be) == TRUE)
			       {
				 think(BLACK,b_who) ;
				 canvas_mode = white_start ;
				 if (check(WHITE,w_who,w_be) == TRUE) break ;
				 if (canvas_mode == game_over) break ;
				 canvas_mode = black_start ;
			       }
			 }
		       notify_dispatch() ;
		     }
		 }    
	       remark = old_remark ;
	       break ;
    }
}


static void
new_game_proc()

{
  initboard() ;
  play_mode = play_black ;
  panel_set_value(computer_plays_choice,0) ;
  canvas_mode = white_start ;
  last_move = -1 ;
  canvas_init() ;
  remark_msg("") ;
  panel_msg("To move, use your left mouse button") ;
}
SHAR_EOF
fi
if test -f 'remark.c'
then
	echo shar: "will not over-write existing file 'remark.c'"
else
cat << \SHAR_EOF > 'remark.c'

/*  remark.c
 *
 *  Remark making code.
 *
 *  Copyright: Chris Miller, 1979, 1981, 1984
 *
 *  Included with this othello game by
 *  Rich Burridge, Sun Microsystems, Australia - December 1986.
 */

#include "othello.h"

char rem_file[] = LIBPATH(LIB,othello.remarks) ;

extern int aspire ;
extern int cretin_flag ;
extern int last_remark ;
extern Panel_item remark_mes ;

FILE *rem_fp ;
long good_remarks[MAX_REMARKS],bad_remarks[MAX_REMARKS] ;
int max_good = 0 ;
int max_bad = 0 ;
 

make_remark(machine_piece,value)
int machine_piece,value ;

{
  char message[100] ;

  value *= machine_piece ;
  if (value < -INFINITY+64)
    {
      last_remark = FIRST_REMARK ;	   /* Always remark next time */
      if (aspire == MAXASPIRE)
	(void) sprintf(message,"You should win  (by at most %d)",-value-INFINITY+64) ;
      else (void) strcpy(message,"You should win.") ;
    }
  else if (value < -1000)
    {
      if (last_remark != -INFINITY)
	{
	  cretin_flag = TRUE ;
	  last_remark = -INFINITY ;
	  (void) strcpy(message,"Only a cretin could lose now.") ;
	}
    }
  else if (value <= 1000) printr(value,message) ;
  else if (value < INFINITY-63)
    {
      if (last_remark!=INFINITY)
	{
	  (void) strcpy(message,"Resign, you dolt! Resistance is futile.") ;
	  last_remark = INFINITY ;
	}
    }
  else
    {
      last_remark = FIRST_REMARK ;
      if (aspire == MAXASPIRE)
	(void) sprintf(message,"I shall win  (by at least %d).", value-INFINITY+64) ;
      else (void) strcpy(message,"I shall win") ;
    }
  remark_msg(message) ;
}


get_remarks ()

/*  Set up file pointers to remarks; the format of the remarks file is:
 *
 *  Remarks to the effect that machine is winning, in increasing order of
 *  strength, one per line.
 *
 *  A line beginning with the character "@".
 *
 *  Remarks to the effect that human is winning, in increasing order of
 *  strength, one per line.
 */

{
  long fp = 0l ;
  int ch ;

  rem_fp = fopen(rem_file,"r") ;
  if (rem_fp == NULL)
    {
      remark_msg("Remarks not available") ;
      return ;
    }

  while ((ch = getc(rem_fp)) != EOF)
    {
      if (ch == '@') break ;
      good_remarks[max_good++] = fp++ ;
      if (max_good == MAX_REMARKS)
	{
	  remark_msg("Too many remarks") ;
	  return ;
	}
      while (ch != '\n')
	{
	  ch = getc(rem_fp) ;
	  fp++ ;
	}
    }
  max_good-- ;

  fp++ ;
  while (ch != '\n')
    {
      ch = getc(rem_fp) ;
      fp++ ;
    }
 
  while ((ch = getc(rem_fp)) != EOF)
    {
      bad_remarks[max_bad++] = fp++ ;
      if (max_bad == MAX_REMARKS)
	{
	  remark_msg("Too many remarks") ;
	  return ;
	}
      while (ch != '\n')
	{
	  ch = fgetc(rem_fp) ;
	  fp++ ;
	}
    }
  max_bad-- ;
}
 

printr(n,string)
int n ;
char string[100] ;

/*  Locate file-pointer and print appropriate line. The strange
 *  computation is to cluster remarks towards low evaluations,
 *  since the evaluation function changes more rapidly with small
 *  changes in position as the evaluation gets larger
 */

{
  int ch ;
  int index ;
  float findex ;
  int sign_n = (n < 0 ? -1 : 1) ;
  char *stringp = string ;

  if (rem_fp == NULL) return ;

  findex = (float) abs(n) / 1000.0 ;
  index = findex * (2-findex) * (n < 0 ? max_bad : max_good) + 0.5 ;

/* Don't make the same remark twice in a row */

  if (index * sign_n == last_remark) return ;

  last_remark = index * sign_n ;
  fseek(rem_fp,n < 0 ? bad_remarks[index] : good_remarks[index],0) ;
  while ((ch = getc(rem_fp)) != EOF)
    {
      if (ch == '\n') break ;
      *stringp++ = ch ;
    }
  *stringp = '\0' ;
}

SHAR_EOF
fi
if test -f 'othello.h'
then
	echo shar: "will not over-write existing file 'othello.h'"
else
cat << \SHAR_EOF > 'othello.h'

/*  othello.h
 *
 *  Definitions used by the Sun Othello game.
 */

#include <stdio.h>
#include <strings.h>
#include <suntool/sunview.h>
#include <suntool/panel.h>
#include <suntool/canvas.h>

#define  panel_msg(s)   (void) panel_set(panel_mes,PANEL_LABEL_STRING,s,0)
#define  remark_msg(s)  (void) panel_set(remark_mes,PANEL_LABEL_STRING,s,0)
#define  score_msg(s)   (void) panel_set(score_mes,PANEL_LABEL_STRING,s,0)

#define  BOARDSIZE     8                   /* 8x8 playing board */

#define  RCLR          PIX_CLR             /* Rasterop operations. */
#define  RSRC          PIX_SRC
#define  RINV          PIX_SRC ^ PIX_DST

#define  BLACK         1                   /* Piece definitions. */
#define  FREE          0
#define  WHITE         -1

#define  FOR_BOARD(i)  for (i = 0; i < 64; i++)
#define  INFINITY      10000
#define  INIT_ASPIRE   3
#define  INIT_DEPTH    2
#define  ISMOVE(pos,player) \
         (nextmove(pos,NOMOVE,(BOARD *) NULL, player) != NOMOVE)
#define  MAXASPIRE     6
#define  MAXDEPTH      6
#define  NOMOVE        -1
#define  OPPONENT(p)   p * -1
#define  PAUSE(i)      for (i = 0; i < 150000; i++)
#define  VINVUL        50

#define  MAX_REMARKS   50
#define  FIRST_REMARK  (INFINITY+1)

#define  LIBPATH(l,c)  l/c"

typedef struct {
          char SQUARE[64] ;
          int MOVES_LEFT ;
} BOARD ;
SHAR_EOF
fi
if test -f 'othello.remarks'
then
	echo shar: "will not over-write existing file 'othello.remarks'"
else
cat << \SHAR_EOF > 'othello.remarks'
Pretty level; I'll start trying soon.
Little in it; superior skill will tell.
My superior skill is telling.
You're beginning to lose your grip.
You're really not so hot after all.
Wake up! You're falling by the wayside.
Come on, concentrate!
If I weren't a computer, I'd be bored.
They tell me checkers is easier.
I'm starting to get bored.
I'd win with one CPU behind my back.
Who on earth programmed YOU?
Quiche-eater!
I seem to be hammering you a little.
Are you a random-number generator?
You should practice with a calculator.
When things are going badly DON'T PANIC.
I think you should start to panic.
I'm thrashing you.
Oh for some REAL opposition.
What a massacre!
My brilliance equals your dullness.
Why don't you take up knitting instead?
You should take up computer science.
So it goes - I win some, you lose some.
You've had it. Why not give up now?
@
Just about dead level.
Maybe you just have a tiny advantage.
I'll sweep aside your slight advantage.
Maybe you're not as daft as you look.
Good! You're getting the hang of it now.
I ought to start playing properly soon.
All right, no need to show off.
Have you played this game before?
Hmmm. This may be harder than I thought.
Are you fifth-generation?
I wish to register a complaint.
I didn't want to play this, you know.
Give me a chance! I'm only a machine.
I suppose you think your pretty smart.
Do you beat up old ladies as well?
You may be ahead, but it isn't over yet.
Well, it isn't QUITE over yet.
You seem to know this game pretty well.
Wait till I get my brother Kray onto you.
I think I'm getting a slight headache.
You should pick on someone your own size.
Megarats! You're winning heavily.
SHAR_EOF
fi
if test -f 'patch.1'
then
	echo shar: "will not over-write existing file 'patch.1'"
else
cat << \SHAR_EOF > 'patch.1'

This patchfile does two things:

  (1) Adds in the CANVAS_FAST_MONO option to the window_create system
      call (V3.2 onwards).
  (2) Changes TABS back to spaces in the panel_create_item call and forces
      this item to be on the next line.


*** otool.c~	Mon Jan  5 12:40:42 1987
--- otool.c	Mon Jan  5 14:05:29 1987
***************
*** 20,26 ****
  
  #include "othello.h"
  
! #define  PATCHLEVEL     0
  
  /* Compilation parameters: */
  
--- 20,26 ----
  
  #include "othello.h"
  
! #define  PATCHLEVEL     1
  
  /* Compilation parameters: */
  
***************
*** 144,150 ****
  			 WIN_HEIGHT,	     TOTAL_WIDTH+1,
  			 WIN_WIDTH,	      TOTAL_WIDTH+1,
  			 WIN_IGNORE_PICK_EVENTS, KBD_REQUEST, WIN_ASCII_EVENTS, 0,
! /*		       CANVAS_FAST_MONO, TRUE, */
  			 0) ;
  
    window_fit(base_frame) ;
--- 144,150 ----
  			 WIN_HEIGHT,	     TOTAL_WIDTH+1,
  			 WIN_WIDTH,	      TOTAL_WIDTH+1,
  			 WIN_IGNORE_PICK_EVENTS, KBD_REQUEST, WIN_ASCII_EVENTS, 0,
! 			 CANVAS_FAST_MONO, TRUE,
  			 0) ;
  
    window_fit(base_frame) ;
***************
*** 252,259 ****
  		    0) ;
   
    remark_mes = panel_create_item(panel,PANEL_MESSAGE,
! 		 PANEL_LABEL_STRING,"				    ",
! 		 0) ;
  
    panel_mes = panel_create_item(panel,PANEL_MESSAGE,
  		PANEL_LABEL_STRING,"To move, use your left mouse button",
--- 252,259 ----
  		    0) ;
   
    remark_mes = panel_create_item(panel,PANEL_MESSAGE,
! 		 PANEL_LABEL_STRING,"                               ",
!                  0) ;
  
    panel_mes = panel_create_item(panel,PANEL_MESSAGE,
  		PANEL_LABEL_STRING,"To move, use your left mouse button",
SHAR_EOF
fi
exit 0
#	End of shell archive
D
D