[net.sources.games] Othello Game in C with Lookahead - sun graphics version

falk@sun.uucp (Ed Falk) (11/25/86)

> Here is a complete, working Othello game in C (also known as Reversi) with
> N-move lookahead.  It is about 1300 lines and about 38K long.  I'm not a unix
> wizard and I don't know how to use makefiles and stuff so I am
> just putting the whole thing in one file.  I hope this doesn't
> upset anyone with a small buffer.  But even my Apple II has a 26K
> buffer, so anyone with a computer big enough to compile this
> should have no trouble.  Copy it, change it, whatever; just don't
> sell it or remove my name from it.
> 


Well, in the interest of community hacking, I now present a graphics interface
for a Sun workstation running 3.0 or better.

Notes:

The original program was quite well written and very easy to hack.
Congratulations.  This graphics program has a few quirks (I could never get
the shape of the circles quite right), but perhaps I'll send an improved
version later.  Whaddya want for one day's hacking.

By the way, that was the first program I EVER got off the net that compiled
and ran the first time.

The shell archive I'm posting has a few files in it:

A diffs file.
A make file
A modified version of othello.c
An include file: othello.h
The graphics program: otool.c
An icon: othello.icon

The diffs file describes the differences between the original program
and this version.  Basically, I commented out the main program so that
the file is now a subroutine package.  I commented out all the global
declarations and moved them into the include file.  There are also two
bug fixes in the subroutines "init_gamelog" and "erase_board", neither
of which were serious unless you change the game to play new games without
being restarted.

To build: unpack the archive and type "make"  This generates "otool" which
you run under suntools.

Putting it into "auto" mode is a good time.

One more suggestion: Someone should spend some time optimizing the search
algorithm.  At any level of difficulty past 2, it's too slow.  Not that it
matters; it can usually beat me at level 0.


#! /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 to create the files:
#	diffs
#	Makefile
#	othello.h
#	othello.c
#	otool.c
#	othello.icon
# This archive created: Mon Nov 24 18:53:01 PST 1986
#
#
export PATH; PATH=/bin:$PATH
#
if [ -f diffs ]
then
  echo shar: will not over-write existing file diffs
else
  echo shar: extracting 'diffs',     2073 characters
  cat > diffs <<'Funky_Stuff'
*** othello.c	Mon Nov 24 14:12:35 1986
--- othello.new	Mon Nov 24 14:12:24 1986
***************
*** 13,18 ****
--- 13,20 ----
  
  /* Othello - by Jonathan Dubman - Hard Hat Area - work in progress */
  
+ /* converted to for graphics by falk */
+ 
  /* Version 1.0   - 9/29/86  - grabs as many pieces as it can, plays by rules */
  /* Version 1.1   - 10/2/86  - includes log */
  /* Version 1.1.1 - 10/2/86  - includes suggest */
***************
*** 149,154 ****
--- 151,163 ----
  #include <stdio.h>
  #include <strings.h>
  
+ #include "othello.h"
+ 
+ #ifdef COMMENT
+ 
+ 
+ /* all this stuff commented out now that there's an include file */
+ 
  #define VERSION "1.1.8"
  
  /* Define the best strategy for playing the game : an evaluation function */
***************
*** 251,256 ****
--- 260,266 ----
  struct COORDINATES scanboard();
  
  /*  Miscellaneous global varibles */
+ #endif COMMENT
  
  char p[3] = { '-', 'X', 'O' };	/* board pieces: p[0], p[X], p[O] */
  int listcount;			/* most recently allocated list */
***************
*** 263,268 ****
--- 273,281 ----
  int autolook;				/* lookahead for player when computer
  					   takes over player's position */
  
+ 
+ 
+ #ifndef OTHELLOTOOL
  main()
  {
      struct COORDINATES playermove, computermove;
***************
*** 388,394 ****
--- 401,410 ----
  	printf("A tie.\n");
      }
  }
+ #endif
  
+ 
+ 
  /*  LIST HANDLER ROUTINES */
  
***************
*** 547,557 ****
  	}
      }
  
!     for (x = 0; x <= BOARDSIZE+1, x++;) {
  	board->loc[0][x] = BORDER;
  	board->loc[x][0] = BORDER;
! 	board->loc[BOARDSIZE][x] = BORDER;
! 	board->loc[x][BOARDSIZE] = BORDER;
      }
  }
  
--- 563,573 ----
  	}
      }
  
!     for (x = 0; x <= BOARDSIZE+1; x++) {
  	board->loc[0][x] = BORDER;
  	board->loc[x][0] = BORDER;
! 	board->loc[BOARDSIZE+1][x] = BORDER;
! 	board->loc[x][BOARDSIZE+1] = BORDER;
      }
  }
  
***************
*** 1211,1216 ****
--- 1227,1233 ----
  /*  Initialize the game log */
  init_gamelog()
  {
+     turns = 0;
      gamelog[0].X_count = 2;
      gamelog[0].O_count = 2;
  }
Funky_Stuff
  len=`wc -c < diffs`
  if [ $len !=     2073 ] ; then
    echo error: diffs was $len bytes long, should have been     2073
  fi
fi # end of overwriting check
if [ -f Makefile ]
then
  echo shar: will not over-write existing file Makefile
else
  echo shar: extracting 'Makefile',      249 characters
  cat > Makefile <<'Funky_Stuff'
DFLAGS = -O

CFLAGS = $(DFLAGS)

otool:	otool.o othello.o
	cc $(CFLAGS) -o otool otool.o othello.o -lsuntool -lsunwindow -lpixrect

othello: othello.o
	cc $(CFLAGS) -o othello othello.o

othello.o: othello.c
	cc $(CFLAGS) -DOTHELLOTOOL -c othello.c
Funky_Stuff
  len=`wc -c < Makefile`
  if [ $len !=      249 ] ; then
    echo error: Makefile was $len bytes long, should have been      249
  fi
fi # end of overwriting check
if [ -f othello.h ]
then
  echo shar: will not over-write existing file othello.h
else
  echo shar: extracting 'othello.h',     3702 characters
  cat > othello.h <<'Funky_Stuff'


#define VERSION "1.1.8"

/* Define the best strategy for playing the game : an evaluation function */

#define beststrategy	look

#define FALSE	0
#define TRUE	1

#define debug	FALSE		/* whether or not to print diagnostics */

#define EMPTY	0		/* an empty space on the board */
#define X	1		/* X piece in board array */
#define O	2		/* O piece, not zero */
#define BORDER	3		/* Put all along the edge of the board */

#define NOWHERE -1		/* Coordinate of an invalid move */
#define INVALID	10000		/* Evaluation of an invalid move - arbitary */
				/* number, but no confuse with REAL eval # */
#define PASS -1			/* Parameter for gamelog when player passes */

#define MAXLIST		100	/* maximum number of lists in memory */
#define STACKSIZE	1000	/* size of coordinate stack */

/*  The board must be square and should have an even dimension.
 *  printboard() and getmove() will have to be modified if BOARDSIZE >=10.
 */

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

#define NUMSQUARES	BOARDSIZE * BOARDSIZE

/*  Structure for storing a playing board.  Access as board.loc[x][y] where
 *  x is across, y is down, origin is upper left.
 *  Each element in the array is a piece - either EMPTY, X, or O.
 *  Note: Subscript of 9 means 0..8 are valid indexes!  Not 1..9 or 0..9!
 *  Note: On the edge of the playing board, i.e. the 0th column and 9th column
 *  and 0th row and 9th row, we put BORDER pieces instead of EMPTY, to simplify
 *  range checking in some functions.
 */

struct BOARD {
    int loc[BOARDSIZE+2][BOARDSIZE+2];
} gameboard;

/*  Structure for storing a pair of coordinates */

struct COORDINATES {
    int xc;
    int yc;
    int eval;		/* NOTE: Some routines use this, other's don't. */
};

/*  Structure for storing an entire list of coordinates, such as the list of
 *  coordinates the computer would take if it placed a piece at (x1, y1).
 *  Note that start indexes, and not points to, a coordinate structure.
 *  There are no actual addresses being stored, just indexes to the stack
 *  array.  There may be many lists in memory.
 */

struct LIST {
    int start;				/* index to first pair in list */
    int size;				/* number of coordinates in list */
} list[MAXLIST];

/*  The stack is where all the actual coordinates are stored.  list[]
 *  refers to addresses on the stack.  Each stack element is of type
 *  COORDINATES.  It is a stack because allocations and deallocations of
 *  lists must be made in LIFO order, even though one can access any of the
 *  lists at any time.
 */

struct COORDINATES stack[STACKSIZE];

/*  The gamelog keeps track of each turn: location of the players' move, and
 *  number of pieces they had at that time.  Associated functions have the name
 *  'log' imbedded in them.  Note the number of turns must be no more than the
 *  number of squares.
 */

struct GAMELOG {
    char xloc;
    char yloc;
    char X_count;
    char O_count;
    char take;
    char pass;
} gamelog[NUMSQUARES];

/*  Evaluation functions */

int mostpieces();
int mostcorners();
int lookone();
int look();

/*  Other functions */

struct COORDINATES getmove();
struct COORDINATES scanboard();

/*  Miscellaneous global varibles */

char p[3] ;				/* board pieces: p[0], p[X], p[O] */
int listcount;				/* most recently allocated list */
int mistakes ;				/* number of mistakes player's made */
int updown, leftright ;			/* used in scanboard only - see that */
int computer ;				/* flag for computer against itself */
int turns ;				/* number of turns */
int firstmove;				/* TRUE if player first, else FALSE */
int lookahead;				/* depth of search for comp's move */
int autolook;				/* lookahead for player when computer
					   takes over player's position */

Funky_Stuff
  len=`wc -c < othello.h`
  if [ $len !=     3702 ] ; then
    echo error: othello.h was $len bytes long, should have been     3702
  fi
fi # end of overwriting check
if [ -f othello.c ]
then
  echo shar: will not over-write existing file othello.c
else
  echo shar: extracting 'othello.c',    39445 characters
  cat > othello.c <<'Funky_Stuff'
/* othello game 1.2 @(#)othello.c	1.2

Here is a complete, working Othello game in C (also known as Reversi) with
N-move lookahead.  It is about 1300 lines and about 38K long.  I'm not a unix
wizard and I don't know how to use makefiles and stuff so I am just putting the
whole thing in one file.  I hope this doesn't upset anyone with a small buffer.
But even my Apple II has a 26K buffer, so anyone with a computer big enough to
compile this should have no trouble.  Copy it, change it, whatever; just don't
sell it or remove my name from it.

<=== CUT HERE ===>
*/

/* Othello - by Jonathan Dubman - Hard Hat Area - work in progress */

/* converted to for graphics by falk */

/* Version 1.0   - 9/29/86  - grabs as many pieces as it can, plays by rules */
/* Version 1.1   - 10/2/86  - includes log */
/* Version 1.1.1 - 10/2/86  - includes suggest */
/* Version 1.1.2 - 10/6/86  - looks ahead one move and goes for corners -bugs*/
/* Version 1.1.3 - 10/13/86 - cleaner version that actually looks ahead */
/* Version 1.1.4 - 10/17/86 - attempt at looking ahead N moves */
/* Version 1.1.5 - 10/31/86 - sort of works when looking ahead N moves */
/* Version 1.1.6 - 11/7/86  - debugged version - there were lots of 'em! */
/* Version 1.1.7 - 11/10/86 - forced passing implemented */
/* Version 1.1.8 - 11/22/86 - significant amount of documentation added 

/* This program plays the game of Othello: player against computer, or
 * computer against itself.  It uses a recursive search to determine the best
 * possible move.  The rules of Othello are summarized in the instructions
 * at the end of this file.  The basic strategy is to take as many pieces as
 * possible, subject to the exceptions that corners are very good, edges are
 * somewhat good, and any squares adjacent to corners are bad.  The program is
 * intended to be portable and flexible, though I haven't tried to compile it
 * on a non-unix machine, and changing the board size might create problems.
 *
 * The main parts of the program are: List Handler, Board Handler, Scanner,
 * Evaluation Functions, and User Interface.  I will describe all of these
 * in brief.  I am not going to bother including the actual parameters of
 * each function.  Only the most important functions are described.  Detailed
 * descriptions are given in the function definitions.
 *
 *
 * LIST HANDLER
 *
 * Othello is a game that deals with groups of pieces, all of which have
 * coordinates.  A group of coordinates is called a list.  Many routines must
 * deal with groups of coordinates; for example, the routines that flip pieces
 * after a move, form a list of pieces taken if a player makes a certain move,
 * etc.  The lists are stored on a large stack of coordinates.  There is also
 * another array, list, that keeps track of the length and starting position
 * of each list.
 *
 * initlists()		Erase all lists and start fresh.  listcount keeps
 *			track of how many lists are in memory.
 * list_x(), list_y()	Get coordinates out out of a certain list.
 * add_list()		Add an entirely new list to memory.
 * zap_list()		Delete an entire list from memory.
 * consolidate_lists()	Combine several lists into one list.
 * add_element()	Add a pair of coordinates to a given list.
 *
 *
 * BOARD HANDLER
 *
 * There is a type BOARD which is a grid of pieces - either empty, X, O, or
 * border.  Lots of functions pass around boards.  The evaluation functions
 * create plenty of new, temporary boards with which they perform experiments.
 *
 * erase_board()	Clear board to all empty, with border beyond the edges.
 * initboard()		Eraseboard and place first four pieces.
 * copyboard()		Copy the contents of one board to another.
 * printboard()		Show a text display of the contents of a board,along
 *			with who's winning, the coordinate grid, and an
 *			optional message string, i.e. "O just took 4 pieces."
 * count()		Count how many pieces a certain player has.
 *
 *
 * SCANNER
 *
 * The most important part of the game is the intelligence part.  How does the
 * computer decide how to make a move?  All the routines are equally valid for
 * both players.  The computer can make a suggestion to the player without much
 * extra work.  The key function is scanboard, and the key idea is that it can
 * be made to work with any evaluation function by C's pointer-to-function
 * ability.
 *
 * validmove()		Determine whether a given move is valid for a given
 *			player.
 * formfliplist()	Form a coordinate list of pieces that would be taken
 *			by a given player moving at a given place.  Pieces
 *			can be taken in any combination of all 8 directions.
 * scanboard()		Given a player, board, pointer to evaluation function,
 *			and depth of search, maximize that function over all
 *			X,Y combinations on the board.
 *
 *
 * EVALUATION FUNCTIONS
 *
 * Each evaluation function takes a board, a specific position, a player, and
 * a lookahead value, and must return an integer corresponding to how good the
 * move is.
 *
 * mostpieces()		Just grab as many pieces as you can.
 * mostcorners()	Take into account the fact that corners are good, etc.
 * lookone()		Look ahead one move.  It is kept as a demonstration.
 * look()		The recursive lookahead function, that gets very
 *			slow with lookahead greater than three or four.
 *
 *
 * USER INTERFACE
 *
 * The user interface is all that need be changed to change the program to
 * graphics, etc.  Main is a little messy, but more flexible that way.
 *
 * main()		Main is just a front end to the whole works.
 *			Set skill level, first player, etc., set up the
 *			game, then alternate players' moves, keeping track of
 *			passing if necessary and stopping at the end of the
 *			game.
 * getmove()		Get a pair of coordinates from the user.  Getmove
 *			also supports numerous niceties like help, suggestion,
 *			etc.  It keeps trying until it gets a valid move.
 * printboard()		Obviously needs to be changed for a graphics version.
 *
 *
 * MISCELLANEOUS
 *
 * There is a game log and a lot of other functions, but they are not too
 * important to the understanding of the overall working of the game.
 * 
 *
 * USER MODIFICATIONS
 *
 * Aside from fixing bugs, converting the whole thing to graphics, etc., the
 * most obvious extension to the program would be to improve the evaluation
 * functions.  The evaluation functions must return an integer corresponding
 * to how good the move is.  A high number is a good move.  Negative values
 * ARE permitted.  The function should return INVALID if that move is not
 * possible.
 *
 * ---------------------------------------------------------------------------
 * This program is freely redistributable subject to the restriction that it
 * shall not be sold for profit and my name shall not be removed from it.
 *
 * I would greatly appreciate comments, bugs, etc.
 * Until December 17th, send mail to ucbvax!buddy!c9c-bi
 * After then, I don't know what my account will be.  Try ucbvax!cory!dubman
 */

#include <stdio.h>
#include <strings.h>

#include "othello.h"

#ifdef COMMENT


/* all this stuff commented out now that there's an include file */

#define VERSION "1.1.8"

/* Define the best strategy for playing the game : an evaluation function */

#define beststrategy	look

#define FALSE	0
#define TRUE	1

#define debug	FALSE		/* whether or not to print diagnostics */

#define EMPTY	0		/* an empty space on the board */
#define X	1		/* X piece in board array */
#define O	2		/* O piece, not zero */
#define BORDER	3		/* Put all along the edge of the board */

#define NOWHERE -1		/* Coordinate of an invalid move */
#define INVALID	10000		/* Evaluation of an invalid move - arbitary */
				/* number, but no confuse with REAL eval # */
#define PASS -1			/* Parameter for gamelog when player passes */

#define MAXLIST		100	/* maximum number of lists in memory */
#define STACKSIZE	1000	/* size of coordinate stack */

/*  The board must be square and should have an even dimension.
 *  printboard() and getmove() will have to be modified if BOARDSIZE >=10.
 */

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

#define NUMSQUARES	BOARDSIZE * BOARDSIZE

/*  Structure for storing a playing board.  Access as board.loc[x][y] where
 *  x is across, y is down, origin is upper left.
 *  Each element in the array is a piece - either EMPTY, X, or O.
 *  Note: Subscript of 9 means 0..8 are valid indexes!  Not 1..9 or 0..9!
 *  Note: On the edge of the playing board, i.e. the 0th column and 9th column
 *  and 0th row and 9th row, we put BORDER pieces instead of EMPTY, to simplify
 *  range checking in some functions.
 */

struct BOARD {
    int loc[BOARDSIZE+2][BOARDSIZE+2];
} gameboard;

/*  Structure for storing a pair of coordinates */

struct COORDINATES {
    int xc;
    int yc;
    int eval;		/* NOTE: Some routines use this, other's don't. */
};

/*  Structure for storing an entire list of coordinates, such as the list of
 *  coordinates the computer would take if it placed a piece at (x1, y1).
 *  Note that start indexes, and not points to, a coordinate structure.
 *  There are no actual addresses being stored, just indexes to the stack
 *  array.  There may be many lists in memory.
 */

struct LIST {
    int start;				/* index to first pair in list */
    int size;				/* number of coordinates in list */
} list[MAXLIST];

/*  The stack is where all the actual coordinates are stored.  list[]
 *  refers to addresses on the stack.  Each stack element is of type
 *  COORDINATES.  It is a stack because allocations and deallocations of
 *  lists must be made in LIFO order, even though one can access any of the
 *  lists at any time.
 */

struct COORDINATES stack[STACKSIZE];

/*  The gamelog keeps track of each turn: location of the players' move, and
 *  number of pieces they had at that time.  Associated functions have the name
 *  'log' imbedded in them.  Note the number of turns must be no more than the
 *  number of squares.
 */

struct GAMELOG {
    char xloc;
    char yloc;
    char X_count;
    char O_count;
    char take;
    char pass;
} gamelog[NUMSQUARES];

/*  Evaluation functions */

int mostpieces();
int mostcorners();
int lookone();
int look();

/*  Other functions */

struct COORDINATES getmove();
struct COORDINATES scanboard();

/*  Miscellaneous global varibles */
#endif COMMENT

char p[3] = { '-', 'X', 'O' };		/* board pieces: p[0], p[X], p[O] */
int listcount;				/* most recently allocated list */
int mistakes = 0;			/* number of mistakes player's made */
int updown = 0, leftright = 0;		/* used in scanboard only - see that */
int computer = FALSE;			/* flag for computer against itself */
int turns = 0;				/* number of turns */
int firstmove;				/* TRUE if player first, else FALSE */
int lookahead;				/* depth of search for comp's move */
int autolook;				/* lookahead for player when computer
					   takes over player's position */



#ifndef OTHELLOTOOL
main()
{
    struct COORDINATES playermove, computermove;

    char choice, firstchoice, garbage;	/* character inputs */
    char string[80];			/* for passing msg to printboard */
    int cx, cy;				/* coordinates */
    int xpass, opass;			/* both true -> end of game */

    init_gamelog();

    printf("OTHELLO v%s\nby Jonathan Dubman\n", VERSION);
    printf("Do you want instructions? (Y/N) [No] ");
    choice = getchar();

    if (choice == 'y' || choice == 'Y') {
	instructions();
    }
    /* skip past all the junk until the newline */
    if (choice != '\n') while ((garbage = getchar()) != '\n');

    printf("Do you want to go first (Y/N) [Yes] ");
    firstchoice = getchar();
    if (firstchoice != '\n') while ((garbage = getchar()) != '\n');

    printf("Skill level 0 (easy,fast) to 9 (hard,slow) [0] ");
    choice = getchar();
    if (choice != '\n') while ((garbage = getchar()) != '\n');
    lookahead = 0;
    if (choice > '0' && choice <= '9') {
	lookahead = choice - '0';
    }

    printf("\n");
    initlists();
    initboard(&gameboard);
    printboard(&gameboard, "Beginning new game");

    if (firstchoice == 'N' || firstchoice == 'n') {
	firstmove = FALSE;
	goto compmove;
    } else {
	firstmove = TRUE;
    }

    /* Main turn loop */

    while (TRUE) {
	playerturn:
	playermove = scanboard(&gameboard, X, mostpieces, 0);
	cx = playermove.xc;
	cy = playermove.yc;
	if (validmove(&gameboard, cx, cy, X) == FALSE) {
	    printf("Player is forced to pass.\n");
	    update_gamelog(X, PASS);
	    sprintf(string, "X is forced to pass.");
	    xpass = TRUE;
	} else {
	    if (computer == FALSE) {
	        playermove = getmove(&gameboard);
	        if (computer == TRUE) goto playerturn;
	        cx = playermove.xc;
	        cy = playermove.yc;
	    } else {
		playermove = scanboard(&gameboard, X, beststrategy, autolook);
		cx = playermove.xc;
		cy = playermove.yc;
	        printf("\nNonplayer moves at ");
	        printcoordinates(cx, cy);
	        printf("\n");
	    }
	    formfliplist(&gameboard, cx, cy, X);
	    gameboard.loc[cx][cy] = X;
	    do_flip(&gameboard, listcount);
	    zap_list();
	    update_gamelog(X, cx, cy);
	    if (gamelog[turns].take == 1) {
	        sprintf(string, "X just took 1 piece");
	    } else {
	        sprintf(string, "X just took %d pieces", gamelog[turns].take);
	    }
	}
        printboard(&gameboard, string);
	
	compmove:
	computermove = scanboard(&gameboard, O, beststrategy, lookahead);
	cx = computermove.xc;
	cy = computermove.yc;
	if (validmove(&gameboard, cx, cy, O) == FALSE) {
	    printf("Computer is forced to pass.\n\n");
	    update_gamelog(O, PASS);
	    sprintf(string, "O was forced to pass");
	    opass = TRUE;
	} else {
	    printf("\nComputer moves at ");
	    printcoordinates(cx, cy);
	    printf("\n");
	    formfliplist(&gameboard, cx, cy, O);
	    gameboard.loc[cx][cy] = O;
            do_flip(&gameboard, listcount);
	    zap_list();
	    update_gamelog(O, cx, cy);
	    if (gamelog[turns].take == 1) {
	        sprintf(string, "O just took 1 piece");
	    } else {
	        sprintf(string, "O just took %d pieces", gamelog[turns].take);
	    }
	}
        printboard(&gameboard, string);
	if (xpass || opass) break;
    }
    printf("\nThe Game is Over.\n\n");
    printf("FINAL TALLY\n");
    printf("You:  %d\n", cx = count(&gameboard, X));
    printf("Me:   %d\n", cy = count(&gameboard, O));
    printf("\n");
    if (cx > cy) {
	printf("You win.\n");
    } else if (cy > cx) {
	printf("I win.\n");
	printf("One more in the bag for Artificial Intelligence.\n");
    } else {
	printf("A tie.\n");
    }
}
#endif



/*  LIST HANDLER ROUTINES */

/*  Erase all lists and start fresh.  The first list is a null list that merely
 *  specifies the starting index for the next list.  Obviously, the size
 *  of the null list is zero, and since it occupies list[1], the next list
 *  (the first real one), goes in list[1].
 */

initlists()
{
    list[0].start = 0;
    list[0].size = 0;
    listcount = 0;
}

/*  This is useful only as a debugging tool.  It prints data on every list
 *  in memory including the null list at the beginning, but does not print
 *  the coordinate lists themselves.
 */

printlists()
{
    int loop;

    printf("current is list number %d\n", listcount);
    printf("list_num   size     start\n");
    for (loop = 0; loop <= listcount; loop++) {
	printf("%5d     %5d     %5d\n",
		loop, list[loop].size, list[loop].start);
    }
}

/*  Another debugging tool.  This one prints out all the coordinates of a
 *  list, given the list number.
 */

list_number(listnum)
int listnum;
{
    int loop;

    printf("List number %d\n", listnum);
    if (list_length(listnum) == 0)
	printf("is empty.\n");
    else
	for (loop = 0; loop < list_length(listnum); loop++) {
	    printf("x = %d   y = %d\n",
		    list_x(listnum, loop), list_y(listnum, loop));
	}
}

/* Return the x value in the index'th element of list listnum */

list_x(listnum, index)
int listnum, index;
{
    return(stack[list[listnum].start + index].xc);
}

/* Return the y value in the index'th element of list listnum */

list_y(listnum, index)
int listnum, index;
{
    return(stack[list[listnum].start + index].yc);
}

/*  This list handling routine should be called whenever a new list is to be
 *  added.  Use zap_list() to delete it later.  Add info to the list with
 *  add_element().  add_list returns the list number that was just added.
 */

add_list()
{
    listcount++;
    list[listcount].start = list[listcount - 1].start +
			    list[listcount - 1].size;
    list[listcount].size = 0;
    if (listcount < 0 || listcount > 10) {
	printf("Something strange!  There are %d lists.\n", listcount);
    }
    return(listcount);
}

/*  This function consoliates the last N lists in memory as one large list.
 *  It returns the size of the new list.
 */

consolidate_lists(how_many)
int how_many;
{
    int size_so_far = 0;
    int loop;

    if (how_many > listcount || how_many < 1) return(0);

    for (loop = 1; loop < how_many; loop++) {
	size_so_far += list_length(listcount);
	zap_list();
    }
    list[listcount].size += size_so_far;
    return(list[listcount].size);
}

/*  Erase the most recently allocated list.  It is made its own function
 *  for readability and to make code independent of the implementation of
 *  the coordinate lists.
 */

zap_list()
{
    if (listcount == 0) {
	printf("Error in program - trying to zap a nonexistent flip list!\n");
    } else {
        listcount--;
    }
}

/*  Given a list number, return its length (number of elements) */

list_length(listnum)
int listnum;
{
    return(list[listnum].size);
}

/*  This list handling routine should be called whenever a new element is
 *  to be added to the most recently allocated list.
 */

add_element(x, y)
int x, y;
{
    struct COORDINATES *elmptr;		/* points to next element on stack */

    elmptr = &stack[list[listcount].start + list[listcount].size];
    elmptr->xc = x;
    elmptr->yc = y;
    list[listcount].size++;
}

/*  BOARD HANDLER ROUTINES */

/*  Clear the specified board to all EMPTY in the middle
 *  and BORDER on the edge
 */

erase_board(board)
struct BOARD *board;
{
    int x, y;

    for (y = 1; y <= BOARDSIZE; y++) {
	for (x = 1; x <= BOARDSIZE; x++) {
	    board->loc[x][y] = EMPTY;
	}
    }

    for (x = 0; x <= BOARDSIZE+1; x++) {
	board->loc[0][x] = BORDER;
	board->loc[x][0] = BORDER;
	board->loc[BOARDSIZE+1][x] = BORDER;
	board->loc[x][BOARDSIZE+1] = BORDER;
    }
}

/*  Clear the named board and place the first four pieces in the center.
 *  It is generalized for any board size, as long as it is even.
 */

initboard(board)
struct BOARD *board;
{
    int x1, x2;

    x1 = BOARDSIZE / 2;
    x2 = BOARDSIZE / 2 + 1;

    erase_board(&gameboard);
    board->loc[x1][x1] = O;
    board->loc[x2][x1] = X;
    board->loc[x1][x2] = X;
    board->loc[x2][x2] = O;
}

/*  Copy the entire board structure from one board array to another */

copyboard(source, destination)
struct BOARD *source, *destination;
{
    int x, y;

    for (y = 0; y <= BOARDSIZE + 1; y++) {
	for (x = 0; x <= BOARDSIZE + 1; x++) {
	    destination->loc[x][y] = source->loc[x][y];
	}
    }
}

/*  Called by printboard to print "  1 2 3 4 5 6 7 8\n" up to BOARDSIZE */

shownumbers()
{
    int loop;

    printf("  ");
    for (loop = 1; loop <= BOARDSIZE; loop++) {
	printf("%d ", loop);
    }
}

/*  Show all the pieces on the given board with the coordinate grid:
 *  numbers 1-8 going left to right and letters A-H going top to bottom.
 *  Also tell who is ahead and by how many pieces.
 *  Added bonus:  Print the given string on the second line.
 */

printboard(board, string)
struct BOARD *board;
char *string;
{
    int x, y;
    int xcount, ocount;

    shownumbers();
    xcount = count(board, X);
    ocount = count(board, O);
    printf("    You: %2d  Me: %2d    ", xcount, ocount);
    if (xcount > ocount) {
	if (xcount - ocount == 1) {
	    printf("You are ahead by 1 piece.\n");
	} else {
	    printf("You are ahead by %d pieces.\n", xcount - ocount);
	}
    } else if (ocount > xcount) {
	if (ocount - xcount == 1) {
	    printf("I am ahead by 1 piece.\n");
	} else {
	    printf("I am ahead by %d pieces.\n", ocount - xcount);
	}
    } else {
	printf("We are tied.\n");
    }

    for (y = 1; y <= BOARDSIZE; y++) {
	printf("%c ", 'A' + y - 1);
	for (x = 1; x <= BOARDSIZE; x++) {
	    printf("%c ", p[board->loc[x][y]]);
	}
	printf("%c", 'A' + y - 1);
	if (y == 2) {
	    printf("     %s", string);
	}
	printf("\n");
    }
    shownumbers();
    printf("\n");
}

/*  Given a certain board and player, return the number of that player's
 *  pieces on the board.
 */

count(board, pl)
struct BOARD *board;
int pl;
{
    int x, y, number = 0;

    for (y = 1; y <= 8; y++) {
        for (x = 1; x <= 8; x++) {
	    if (board->loc[x][y] == pl) number++;
	}
    }
    return(number);
}

/*  Given a certain player, return the value of the other player */

other(player)
int player;
{
    switch (player) {
	case X:    return(O);
	case O:    return(X);
	default:   return(EMPTY);
    }
}

/*  Print the pair of coordinates for use in "Computer Moves at ##" */

printcoordinates(x, y)
int x, y;
{
    putchar('A' + y - 1);
    putchar('0' + x);
}

/*  See if the given player can move at the given location:  Form a flip
 *  list and test its length, then delete the flip list.
 *  Return TRUE if valid move, FALSE if invalid.  Obviously, it's also invalid
 *  if it ain't on the board!
 */

validmove(board, x, y, player)
struct BOARD *board;
int x, y, player;
{
    int length;

    if (x < 1 || y < 1 || x > BOARDSIZE || y > BOARDSIZE) {
	return(FALSE);
    }

    formfliplist(board, x, y, player);
    length = list_length(listcount);
    zap_list();
    if (length == 0) {
	return(FALSE);
    } else {
	return(TRUE);
    }
}

/*  Get a line of input from the user, terminated by a RETURN */

getline(string)
char *string;
{
    int index = 0;
    char c;

    while ((c = getchar()) != '\n') {
	string[index++] = c;
    }
    string[index] = '\0';
}

/*  Get an X-Y coordinate from the user, return in the COORDINATE structure.
 *  It would have been implemented as a return value, but you can't return
 *  two variables at a time.
 */

struct COORDINATES getmove(board)
struct BOARD *board;
{
    struct COORDINATES coordinates;	/* return coordinates */
    struct COORDINATES suggestion;	/* for suggesting a move */
    char xch, ych;			/* characters for input */
    int xc = -1, yc = -1;		/* actual coordinates of input */
    char string[80];			/* input string */

    while (validmove(board, xc, yc, X) == FALSE) {
	/* Specify move as A1..H8 */
	printf("Move: ");
	getline(string);

	/* put checking for extra commands here, in an else-if structure */

	if (strcmp(string, "help") == 0 || strcmp(string, "?") == 0) {
	    help();
	} else if (strcmp(string, "instructions") == 0) {
	    instructions();
	} else if (strcmp(string, "auto") == 0) {
	    computer = TRUE;
	    coordinates.xc = NOWHERE;
	    coordinates.yc = NOWHERE;
	    do {
		printf("Enter skill level of computer's opponent: ");
		scanf("%d", &autolook);
		if (autolook < 0 || autolook > 9) {
		    printf("Valid values are between 0 and 9.\n");
		}
	    } while (autolook < 0 || autolook > 9);
	    return(coordinates);
	} else if (strcmp(string, "log") == 0) {
	    print_gamelog();
	} else if (strcmp(string, "suggest") == 0) {
	    printf("I suggest moving at ");
	    suggestion = scanboard(board, X, beststrategy, lookahead);
	    printcoordinates(suggestion.xc, suggestion.yc);
	    printf("\n");
	} else if (strcmp(string, "printboard") == 0) {
	    printboard(board, "Reprint of Board");
	} else if (strcmp(string, "quit") == 0 || strcmp(string, "stop") == 0) {
	    printf("Bye!\n");
	    exit(0);
	} else {
	    ych = string[0];
	    if ((ych >= 'A') && (ych <= 'Z')) {
	        yc = ych - 'A';
	    } else {
	        yc = ych - 'a';
	    }
            yc++;
	    xch = string[1];
	    xc = xch - '0';

            if (validmove(board, xc, yc, X) == FALSE) {

	        /* Every fifth mistake - give him help */
	        if (mistakes % 5 != 0) {
	            printf("Illegal move\n");
	        } else {
		    printf("Illegal move - type help for command list\n");
	        }
	        mistakes++;
            }
	}
    }
    coordinates.xc = xc;
    coordinates.yc = yc;
    return(coordinates);
}

/*  Given a certain board and a list of coordinates, flip all the pieces at
 *  each of those coordinates:  O changes to X, X changes to O.
 */

do_flip(board, listnum)
struct BOARD *board;
int listnum;
{
    int loop, x, y;

    for (loop = 0; loop < list_length(listnum); loop++) {
	x = list_x(listnum, loop);
	y = list_y(listnum, loop);
	switch (board->loc[x][y]) {
	    case X: 
		    board->loc[x][y] = O;
		    break;
	    case O: 
		    board->loc[x][y] = X;
		    break;
	    default:
		    break;
	}
    }
}

/*  Given a certain board, a certain X-Y location, and a certain player,
 *  X or O, form a list of coordinates corresponding to the pieces that
 *  would be flipped if that player were to move at that place.
 *  It is guaranteed to add one and only one list to the stack.
 *
 *  Of course - don't even bother looking at a square that is not empty.
 *
 *  Return the number of pieces taken if player performed that move.
 *
 *  Algorithm:  Search in directions (-1,-1), (0,-1), (1,-1) ... (1,1)
 *  If there is an opponent's piece there, add it to the list and start
 *  "walking" in that direction.  If we get to the end of the board or to
 *  an EMPTY square, throw away the list and continue searching in another
 *  direction.  If we get to another of the player's pieces, keep the list
 *  and continue searching in another direction.
 */

formfliplist(board, x, y, player)
struct BOARD *board;
int x, y;
int player;
{
    int round_x, round_y;		/* scan in all directions */
    int walk_x, walk_y;			/* walk forward in each direction */
    int numlists;			/* number of the new list */
    int opponent;			/* the other player */
    int old;

    add_list();

    if (board->loc[x][y] != EMPTY) return(0);

    old = listcount;
    numlists = 1;
    opponent = other(player);
    for (round_y = -1; round_y <= 1; round_y++) {
	for (round_x = -1; round_x <= 1; round_x++) {

	    /* do not try to walk in the "nowhere" direction */
	    if (round_x == 0 && round_y == 0) continue;

	    /* take one step */
	    walk_x = x + round_x;
	    walk_y = y + round_y;

	    /* keep walking as long as we see opponents */
	    if (board->loc[walk_x][walk_y] == opponent) {
		add_list();
		numlists++;
	        while (board->loc[walk_x][walk_y] == opponent &&
		       board->loc[walk_x][walk_y] != BORDER)
		{
		    add_element(walk_x, walk_y);
		    walk_x += round_x;
		    walk_y += round_y;
		}
		/*  OK - We've come to end of opponents' string of pieces.
		 *  If there is one of your pieces at the end, keep it,
		 *  otherwise throw it away.
		 */
		if (board->loc[walk_x][walk_y] != player) {
		    zap_list();
		    numlists--;
		}
	    } /* end of walk in a certain direction */
	}
    } /* end of directional scan */
    /* put all the lists together as one */
    consolidate_lists(numlists);
    if (listcount != old) {
	printf("error! we've added a list!");
	printf("old=%d  new=%d\n", old, listcount);
    }
    return(list_length(listcount));
} /* end of formfliplist() */

/*  rerandomize - change the values leftright and updown - called by scanboard
 */

rerandomize(times)
int times;
{
    int loop;

    for (loop = 0; loop < times; loop++) {
        leftright++;
        if (leftright == 2) {
	    leftright = 0;
	    updown++;
	    if (updown == 2) {
	        updown = 0;
	    }
        }
    }
}

/*  EVALUTATION mostpieces - Conforms to the standard for evaluation functions.
 *  This function says the evaluation of a certain move x, y on a given board
 *  by a given player is equal to the number of pieces he takes.  Other
 *  evaluation functions should base their evaluations on this scale.
 *  0 is worthless, 5 is a pretty good move, 10 is excellent, neg = REAL bad
 *  Return INVALID if the move is invalid.
 */

mostpieces(board, x, y, player, countdown)
struct BOARD *board;
int x, y, player, countdown;
{
    int number;

    formfliplist(board, x, y, player);
    number = list_length(listcount);
    zap_list();
    if (number == 0) {
	return(INVALID);
    } else {
        return(number);
    }
}

/*  EVALUATION mostcorners - same as mostpieces but it tries to get corners
 *  and edges.  Corners are worth 10 points and edges are worth 5.
 */

mostcorners(board, x, y, player, countdown)
struct BOARD *board;
int x, y, player, countdown;
{
    int evaluation;

    evaluation = mostpieces(board, x, y, player, 0);
    if (evaluation != INVALID) {
	evaluation += bonusvalue(x, y);
    }
    return(evaluation);
}

/*  EVALUATION lookone - looks ahead one move.  Calculates the number of
 *  pieces that it is going to take.  If valid, internally form the board
 *  that results.  Call scanboard with mostcorners as the evaluation
 *  function and then subtract the other player's best move's evaluation from
 *  yours.  Return that number.
 */

lookone(board, x, y, player, countdown)
struct BOARD *board;
int x, y, player, countdown;
{
    int eval_you, eval_him;
    struct BOARD tempboard;
    struct COORDINATES coordinates;

    eval_you = mostcorners(board, x, y, player, 0);
    if (eval_you == INVALID) return(INVALID);

    /* Make a temporary board and move the player */
    copyboard(board, &tempboard);
    formfliplist(&tempboard, x, y, player);
    do_flip(&tempboard, listcount);
    zap_list();
    tempboard.loc[x][y] = player;

    /* Find value opponents' best move and make it */
    coordinates = scanboard(&tempboard, other(player), mostcorners, 1);
    formfliplist(&tempboard, coordinates.xc, coordinates.yc, other(player));
    eval_him = list_length(listcount) + 
	       bonusvalue(coordinates.xc, coordinates.yc);
    zap_list();
    return(eval_you - eval_him);
}

/*  Called only by look - prints a bunch of spaces to make the game tree
 *  readable. 
 */

tabover(countdown)
int countdown;
{
    int loop;
    for (loop = 0; loop < lookahead - countdown; loop++) {
	printf("        ");
    }
}

/*  EVALUATION look - looks ahead countdown moves.  Returns the best move.
 *  The idea is an extension of the above routine, lookone.  It uses a depth-
 *  first maximization of move value for the game-tree formed by all possible
 *  moves and responses for each player.  This "game-tree" is not an actual
 *  tree, as it never needs to be stored.  Look calls scanboard to maximize
 *  the move value for each possible move, which in turn calls look.  Look
 *  also has to handle the base-case, which is to evaluate using mostcorners.
 */

look(board, x, y, player, countdown)
struct BOARD *board;
int x, y, player, countdown;
{
    int eval_you, eval_him;
    int loop;
    struct BOARD tempboard;
    struct COORDINATES coordinates;

    /* base case: no lookahead */

    if (debug) {
        tabover(countdown);
        printf("d=%d pl=%c loc=", countdown, p[player]);
        printcoordinates(x,y);
        printf("\n");
    }

    if (countdown == 0) {
	int worth;
	worth = mostcorners(board, x, y, player, countdown);
	if (debug) {
	    tabover(countdown);
	    printf("worth = %d\n", worth);
	}	
	return(worth);
    }

    /* lookahead: pretend player goes at this x,y, form the resultant board,
     * and call scanboard with the other player and the temporary board, with
     * a depth of countdown-1.
     */

    copyboard(board, &tempboard);
    formfliplist(&tempboard, x, y, player);
    if (list_length(listcount) == 0) {
	zap_list();
	return(INVALID);
    }
    do_flip(&tempboard, listcount);
    eval_you = list_length(listcount) + bonusvalue(x, y);
    zap_list();
    tempboard.loc[x][y] = player;

    /* We've formed the new board.  The other player gets a 'chance' to play
     * now and make his supposed best move.  He's also going to look ahead,
     * but we'll have to make him look ahead one move less.
     */

    coordinates = scanboard(&tempboard, other(player), look, countdown-1);
    eval_him = coordinates.eval;
    if (debug) {
        tabover(countdown);
        printf("mval=%d bresp=%d\n\n", eval_you, eval_him);
    }
    return(eval_you - eval_him);
}

/*  corner - returns TRUE if (x,y) is a corner, FALSE otherwise
 */

corner(x, y)
int x, y;
{
    return((x==1 || x==8) && (y==1 || y==8));
}

/*  edge - returns TRUE if (x,y) is on an edge but NOT a corner, else FALSE
 */

edge(x, y)
int x, y;
{
    return((!corner(x, y)) && (x == 1 || x == 8) && (y == 1 || y == 8));
}

/*  nextcorner- returns TRUE if (x,y) is within one square of a corner, else
 *  FALSE
 */

nextcorner(x, y)
int x, y;
{
    int xsearch, ysearch;

    for (xsearch = -1; xsearch <= 1; xsearch++) {
	for (ysearch = -1; ysearch <= 1; ysearch++) {
	    if (corner(x + xsearch, y + ysearch)) return(TRUE);
	}
    }
    return(FALSE);
}

/*  bonusvalue - return 0 if the piece is in the middle, 5 if on edge, 10 if
 *  on corner or -8 if it is next to a corner.
 */

bonusvalue(x, y)
int x, y;
{
    if (corner(x, y)) return(10);
    if (nextcorner(x, y)) return(-8);
    if (edge(x, y)) return(5);
    return(0);
}

/*  scanboard - given a certain board and player, and a POINTER TO A FUNCTION
 *  that returns the evaluation of a given move at location (x,y)...
 *
 *  Find the best move possible for that player by returning coordinates.
 *  Return coordinates if found ANY move, invalid coordinates if found none.
 *  Also return, in the coordinate structure, the evaluation of that move,
 *  which is not necessarily the number of pieces taken.
 *
 *  This function is guaranteed to add no lists to the flip stack.
 *
 *  Algorithm: For each square, form flip list.  If not the best move so far,
 *  get rid of that flip list.  If best move so far, delete old best move, if
 *  there was one, and replace it with this one.
 *
 *  Great pains are taken to decrease predictability by varying the search
 *  order of locations.  All the loop_init and xupper stuff relates to that.
 */

struct COORDINATES scanboard(board, player, evaluate, depth)
struct BOARD *board;
int player;
int (*evaluate)();
int depth;
{
    struct COORDINATES coordinates;	/* return value */
    int scan_x, scan_y;			/* scan every x, y coordinate */
    int xc, yc;				/* best coordinates so far */
    int max = -9999;			/* best evaluation so far */
    int evaluation;			/* value of certain move */

    /* Extra stuff added to ensure randomness in left-right, up-down
     * searching.  updown and leftright determine which are to be used:
     * count from 1 to 8 or from 8 down to 1.
     */

    static int loop_init[] = {1, 8};
    static int loop_final[] = {8, 1};
    static int loop_inc[] = {1, -1};

    int xlower, xupper, xinc;
    int ylower, yupper, yinc;

    xlower = loop_init[leftright];
    xupper = loop_final[leftright];
    xinc = loop_inc[leftright];

    ylower = loop_init[updown];
    yupper = loop_final[updown];
    yinc = loop_inc[updown];

    xc = NOWHERE;			/* an invalid move to begin with */
    yc = NOWHERE;

    for (scan_x = xlower ; scan_x != xupper + xinc; scan_x += xinc) {
	for (scan_y = ylower; scan_y != yupper + yinc ; scan_y += yinc) {
	    if (board->loc[scan_x][scan_y] != EMPTY) continue;

	    /* Evaluate the move at scan_x, scan_y using the function pointed
	     * to by evaluate.  Get the evaluation number in the integer
	     * evaluation.  It is up to the evaluation function to return
	     * the constant INVALID if the move is invalid */

	    rerandomize(board->loc[scan_x][scan_y]);
	    if (validmove(board, scan_x, scan_y, player)) {
	        evaluation = (*evaluate)(board, scan_x, scan_y, player, depth);
                if (evaluation > max && evaluation != INVALID) {
		    xc = scan_x;
		    yc = scan_y;
		    max = evaluation;
		}
	    }
	}
    }
    coordinates.xc = xc;
    coordinates.yc = yc;
    coordinates.eval = max;
    return(coordinates);
}

/*  Initialize the game log */
init_gamelog()
{
    turns = 0;
    gamelog[0].X_count = 2;
    gamelog[0].O_count = 2;
}

/*  Update the game log, given the coordinates of the most recent move
 *  If pass, xc=PASS and yc is undefined. (Only first two parameters passed)
 */

update_gamelog(player, xc, yc)
int player, xc, yc;
{
    turns++;
    if (xc == PASS) {
	gamelog[turns].pass = TRUE;
	gamelog[turns].take = 0;
    } else {
	gamelog[turns].pass = FALSE;
        gamelog[turns].xloc = xc;
        gamelog[turns].yloc = yc;
        gamelog[turns].X_count = count(&gameboard, X);
        gamelog[turns].O_count = count(&gameboard, O);
        if (player == X) {
	gamelog[turns].take = gamelog[turns-1].O_count - count(&gameboard, O);
        } else {
	gamelog[turns].take = gamelog[turns-1].X_count - count(&gameboard, X);
        }
    }
} 

/*  Print out the gamelog */

print_gamelog()
{
    int loop, player;
    int xcount, ocount;
    int totaltaken[2];    		/* how many each has taken so far */

    if (firstmove == TRUE) {
	player = X;
    } else {
	player = O;
    }

    totaltaken[X] = 0;
    totaltaken[O] = 0;

    for (loop = 1; loop <= turns; loop++) {
	printf("%2d: ", loop);
	if (gamelog[loop].pass == TRUE) {
	    printf("%c forced to pass...", p[player]);
	} else {
	    printf("%c at ", p[player]);
	    printcoordinates(gamelog[loop].xloc, gamelog[loop].yloc);
	    xcount = gamelog[loop].X_count;
	    ocount = gamelog[loop].O_count;

	    /* print how many pieces player just took */
	    printf(" taking %2d  ", gamelog[loop].take);
	    totaltaken[player] += gamelog[loop].take;

	    /* print 'ahead by' message */
	    if (xcount > ocount) {
	        printf("X ahead by %2d", xcount - ocount);
	    } else if (ocount > xcount) {
	        printf("O ahead by %2d", ocount - xcount);
	    } else {
	        printf("Tie so far     ");
	    }
	    printf("  Total taken = %2d", totaltaken[player]);
	}

	printf("\n");
	player = other(player);
    }
}

/*  Print the instructions */

instructions()
{
printf("\
Welcome to Othello!  The object of the game is to finish with more pieces\n\
that your opponent.  You will play X and I will play O.  We take turns\n\
placing pieces on an 8x8 grid.  You MUST take opponents' pieces on every\n\
turn.  To take pieces, place an X such that is surrounds a line or lines of\n\
O's.  Then all the O's are flipped to X's, i.e. \"X O O O X\" becomes\n\
\"X X X X X\".  Note that by clever placement you can take more than one\n\
line of O's at a time!  There are many extra features in the game; type help\n\
for more information.  Also: If a player has no move, he is forced to pass.\
Example first moves: D3, C4, F5, E6.\n\
");
}

/*  Print help */

help()
{
printf("\
The program is expecting a pair of coordinates in the form\n\
A1, B6, F2, etc.  You are player X.  You may move only on empty\n\
squares such that you surround a line of O's in any direction.\n\
\n\
help, ?             list these commands\n\
quit, stop          quit the game\n\
instructions        see the instructions again\n\
printboard          see the board again\n\
suggest             let the computer make a suggestion\n\
auto                let the computer finish the game\n\
log                 print a log of all the moves so far, with other info\n\
stats               print numerous statistics on the game so far\n\
");
}

/* END OF OTHELLO BY JONATHAN DUBMAN - APPROXIMATELY 1300 LINES, 38K TEXT */
Funky_Stuff
  len=`wc -c < othello.c`
  if [ $len !=    39445 ] ; then
    echo error: othello.c was $len bytes long, should have been    39445
  fi
fi # end of overwriting check
if [ -f otool.c ]
then
  echo shar: will not over-write existing file otool.c
else
  echo shar: extracting 'otool.c',    15356 characters
  cat > otool.c <<'Funky_Stuff'
/**********
 *
 *
 *	 @@@   @@@@@  @   @  @@@@@  @      @       @@@
 *	@   @    @    @   @  @      @      @      @   @
 *	@   @    @    @@@@@  @@@    @      @      @   @
 *	@   @    @    @   @  @      @      @      @   @
 *	 @@@     @    @   @  @@@@@  @@@@@  @@@@@   @@@
 *
 *	OTHELLO - graphics front end to othello game
 *
 *
 *
 *	Edward A. Falk
 *	Sun Microsystems
 *
 *	November, 1986
 *
 *
 *
 **********/


/* compilation parameters: */


#define	CELL_WIDTH	40
#define	PIECE_MARGIN	5
#define	PIECE_RAD	(CELL_WIDTH/2 - PIECE_MARGIN)

#ifdef sun3
#define	MOVE_DELTA	5	/* for animation */
#else
#define	MOVE_DELTA	10
#endif



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

#define	TOTAL_WIDTH	BOARDSIZE*CELL_WIDTH

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




#ifndef min
#define	min(x,y)	((x)<(y)?(x):(y))
#define max(x,y)	((x)>(y)?(x):(y))
#endif

#define abs(x)		((x)<0?-(x):(x))

#define	eat_line	while(getchar()!='\n')


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



	/* main icon */

static short  icon_image[] = {
#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) ;



	/* assorted globals */



static	struct pixfont	*font ;

static	Frame base_frame, help_frame ;

static	Panel	panel ;

static	Canvas	canvas ;

static	Panel_item	quit_but ;
static	Panel_item	suggest_but ;
static	Panel_item	auto_but ;
static	Panel_item	new_game_but ;
static	Panel_item	computer_first_but ;
static	Panel_item	panel_mes ;
static	Panel_item	score_mes ;
static	Panel_item	difficulty_choice ;


static	Cursor	canvas_cursor, hglass_cursor ;


static	Pixwin	*canvas_pw ;



static	enum	{player_start, player_moving, computer_thinking,
		 computer_moving, game_over} canvas_mode ;

static	int	piece_x, piece_y ;	/* current position of moving piece */
static	int	b_dx, b_dy, bres_ctr ;	/* bresenham counters */

static	struct	COORDINATES playermove, computermove;
static	int	cx, cy ;

static	struct BOARD	oldboard ;

static	char	line[40] ;



	/* proc definitions */

static	void	canvas_proc() ;

static	void	quit_proc() ;
static	void	new_game_proc() ;
static	void	computer_first_proc() ;
static	void	suggest_proc() ;
static	void	auto_proc() ;
static	void	difficulty_proc() ;


static	void	init_panel() ;
static	void	canvas_init() ;
extern	void	init_gamelog() ;
extern	void	initlists() ;
extern	void	initboard() ;
extern	void	erase_board() ;



main(argc,argv)
int	argc ;
char	*argv[] ;
{
	font = pw_pfsysopen() ;


	base_frame = window_create(NULL, FRAME,
	    FRAME_ICON,		&othello_icon,
	    FRAME_LABEL,	"Othello - written by Jonathan Dubman",
	    WIN_ERROR_MSG,	"can't create window.",
	    FRAME_ARGS,		argc, argv,
	    0) ;

	panel = window_create(base_frame, PANEL,
	    WIN_MENU, window_get(base_frame, WIN_MENU),
	    WIN_WIDTH, TOTAL_WIDTH+1,
	    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,
	    WIN_MENU, window_get(base_frame, WIN_MENU),
	    CANVAS_AUTO_EXPAND, FALSE,
	    CANVAS_AUTO_SHRINK, FALSE,
	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 ) ;


	/* game initialization */

	init_gamelog() ;
	initlists() ;
	initboard(&gameboard) ;
	erase_board(&oldboard) ;

	mistakes = 0 ;
	updown = 0 ;  leftright = 0 ;
	computer = FALSE ;
	turns = 0 ;
	firstmove = TRUE ;
	lookahead = 0 ;

	canvas_mode = player_start ;

	canvas_init() ;
	panel_msg("to move, use your left mouse button") ;

	window_main_loop(base_frame) ;
	exit(0) ;
}



static void
init_panel()
{

	quit_but = panel_create_item( panel, PANEL_BUTTON,
	    PANEL_NOTIFY_PROC, quit_proc,
	    PANEL_LABEL_IMAGE, panel_button_image(panel, "quit", 4, 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, 0),
	  0 ) ;

	computer_first_but = panel_create_item( panel, PANEL_BUTTON,
	    PANEL_NOTIFY_PROC, computer_first_proc,
	    PANEL_LABEL_IMAGE,
		panel_button_image(panel, "Computer starts", 15, 0),
	  0 ) ;

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

	auto_but = panel_create_item( panel, PANEL_BUTTON,
	    PANEL_NOTIFY_PROC, auto_proc,
	    PANEL_LABEL_IMAGE, panel_button_image(panel, "auto", 4, 0),
	  0 ) ;

	difficulty_choice = panel_create_item( panel, PANEL_CHOICE,
	    PANEL_NOTIFY_PROC, difficulty_proc,
	    PANEL_LABEL_BOLD, TRUE,
	    PANEL_LAYOUT, PANEL_HORIZONTAL,
	    PANEL_LABEL_STRING, "Difficulty: ",
	    PANEL_VALUE, 0,
	    PANEL_SHOW_MENU, TRUE,
	    PANEL_CHOICE_STRINGS, "0","1","2","3","4","5","6","7","8","9",0,
	    PANEL_DISPLAY_LEVEL, PANEL_CURRENT,
	  0 ) ;

	panel_mes = panel_create_item( panel, PANEL_MESSAGE,
	    PANEL_LABEL_STRING, "this is a test                ",
	  0 ) ;

	score_mes = panel_create_item( panel, PANEL_MESSAGE,
	    PANEL_LABEL_STRING, "player:  2, computer:  2",
	  0 ) ;
}


static void
quit_proc(item,event)
Panel_item	item ;
Event		*event ;
{
	window_done(base_frame) ;
}




static void
suggest_proc()
{
	struct	COORDINATES suggestion;
	int	x,y ;

	suggestion = scanboard(&gameboard, X, beststrategy, lookahead);
	x = suggestion.xc*CELL_WIDTH - CELL_WIDTH/2 ;
	y = suggestion.yc*CELL_WIDTH - CELL_WIDTH/2 ;
	pw_vector(canvas_pw, x-5,y-5, x+5,y+5, PIX_SRC, 0xff) ;
	pw_vector(canvas_pw, x-5,y+5, x+5,y-5, PIX_SRC, 0xff) ;
}
	



static void
difficulty_proc(item, value, event)
Panel_item	item ;
int		value ;
Event		*event ;
{
	lookahead = value ;
}


#define CIRCLE_VECS	24

static	struct pr_pos	circle_vlist[CIRCLE_VECS] ;

static void
init_circle()
{
static	float	ctab[24][2] = {
	{ 0.966, 0.259}, { 0.866, 0.500}, { 0.707, 0.707}, { 0.500, 0.866}, 
	{ 0.259, 0.966}, { 0.000, 1.000}, {-0.259, 0.966}, {-0.500, 0.866}, 
	{-0.707, 0.707}, {-0.866, 0.500}, {-0.966, 0.259}, {-1.000, 0.000}, 
	{-0.966,-0.259}, {-0.866,-0.500}, {-0.707,-0.707}, {-0.500,-0.866}, 
	{-0.259,-0.966}, { 0.000,-1.000}, { 0.259,-0.966}, { 0.500,-0.866}, 
	{ 0.707,-0.707}, { 0.866,-0.500}, { 0.966,-0.259}, { 1.000, 0.000}, } ;

	int	i ;
	float	x1,y1 ;

	for(i=0; i<CIRCLE_VECS;++i)
	{
	  x1 = (PIECE_RAD+.5) * ctab[i][0] + PIECE_RAD;
	  y1 = (PIECE_RAD+.5) * ctab[i][1] + PIECE_RAD;
	  circle_vlist[i].x = x1 ;
	  circle_vlist[i].y = y1 ;
	}
}


static void
draw_cute_circle(x,y,rop)
	int	x,y ;
	int	rop ;
{
	int	i ;
	int	x0,y0, x1,y1 ;

	pw_batch_on(canvas_pw) ;
	x0 = x + circle_vlist[CIRCLE_VECS-1].x ;
	y0 = y + circle_vlist[CIRCLE_VECS-1].y ;
	for(i=0; i<CIRCLE_VECS;i++)
	{
	  x1 = x + circle_vlist[i].x ;
	  y1 = y + circle_vlist[i].y ;
	  pw_vector(canvas_pw, x0,y0, x1,y1, rop, 0xff) ;
	  x0 = x1; y0 = y1 ;
	}
	pw_batch_off(canvas_pw) ;
}




static void
fill_cute_circle(x,y,rop)
	int	x,y ;
	int	rop ;
{
static	int	npts[] = {CIRCLE_VECS} ;

	pw_batch_on(canvas_pw) ;
	pw_polygon_2(canvas_pw,x,y, 1, npts, circle_vlist,
	  PIX_COLOR(0xff)|rop, NULL,0,0) ;
	pw_batch_off(canvas_pw) ;

}



static void
update_board_image()
{
	int	x, y ;
	int	x0, y0 ;

	int	xcount,ocount ;

	for(y=1; y<=BOARDSIZE; y++)
	  for(x=1; x<=BOARDSIZE; x++)
	    if( gameboard.loc[x][y] != oldboard.loc[x][y] )
	    {
	      x0 = (x-1)*CELL_WIDTH + PIECE_MARGIN ;
	      y0 = (y-1)*CELL_WIDTH + PIECE_MARGIN ;
	      switch(oldboard.loc[x][y])
	      {
		case EMPTY:
		case X:
		  switch(gameboard.loc[x][y])
		  {
		    case O:
		      fill_cute_circle(x0, y0, PIX_SRC) ;
		      break ;
		    case X:
		      draw_cute_circle(x0, y0, PIX_SRC) ;
		      break ;
		  }
		  break ;
		case O:
		  switch(gameboard.loc[x][y])
		  {
		    case X:
		      fill_cute_circle(x0, y0, PIX_CLR) ;
		      draw_cute_circle(x0, y0, PIX_SRC) ;
		      break ;
		  }
		  break ;
	      }
	      oldboard.loc[x][y] = gameboard.loc[x][y] ;
	    }


	xcount = count(&gameboard, X);
	ocount = count(&gameboard, O);
	score_msg( sprintf(line,"player: %2d, computer: %2d",xcount,ocount)) ;

}



static void
canvas_init()
{
	int	i, j ;
	int	x0,y0  ;

	init_circle() ;

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

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

	pw_batch_off(canvas_pw) ;

	update_board_image() ;
}



static void
animate_move(cx,cy)
	int	cx,cy ;
{
	int	x0,y0, x1,y1 ;
	int	x,y, dx,dy, ctr ;
static	struct rect	r = {0,0,TOTAL_WIDTH+1, TOTAL_WIDTH+1};

	pw_lock(canvas_pw, &r) ;
	x1 = (cx-1)*CELL_WIDTH + PIECE_MARGIN ;
	y1 = (cy-1)*CELL_WIDTH + PIECE_MARGIN ;
	dx = x1 ;
	dy = y1 ;
	if(x1>y1)
	{
	  ctr = dx/2 ;
	  x=0 ;
	  y=0 ;
	  draw_cute_circle(x,y,PIX_NOT(PIX_DST)) ;
	  while(x<x1)
	  {
	    x0 = x ;
	    y0 = y ;
	    x += MOVE_DELTA ;
	    if((ctr -= dy)<0)
	    {
	      ctr += dx ;
	      y += MOVE_DELTA ; 
	    }
	    draw_cute_circle(x,y,PIX_NOT(PIX_DST)) ;
	    draw_cute_circle(x0,y0,PIX_NOT(PIX_DST)) ;
	  }
	  draw_cute_circle(x,y,PIX_NOT(PIX_DST)) ;
	}
	else
	{
	  ctr = dy/2 ;
	  x=0 ;
	  y=0 ;
	  draw_cute_circle(x,y,PIX_NOT(PIX_DST)) ;
	  while(y<y1)
	  {
	    x0 = x ;
	    y0 = y ;
	    y += MOVE_DELTA ;
	    if((ctr -= dx)<0)
	    {
	      ctr += dy ;
	      x += MOVE_DELTA ; 
	    }
	    draw_cute_circle(x,y,PIX_NOT(PIX_DST)) ;
	    draw_cute_circle(x0,y0,PIX_NOT(PIX_DST)) ;
	  }
	  draw_cute_circle(x,y,PIX_NOT(PIX_DST)) ;
	}
	pw_unlock(canvas_pw) ;
}




static void
who_wins()
{
	int	cs, ps ;

	ps = count(&gameboard, X);
	cs = count(&gameboard, O);
	if(ps>cs)
	  score_msg(sprintf(line,"You win %d-%d",ps,cs)) ;
	else if(ps == cs)
	  score_msg(sprintf(line,"A tie %d-%d",ps,cs)) ;
	else
	  score_msg(sprintf(line,"Computer wins %d-%d",cs,ps));
}


static void
player_moved()
{
	playermove.xc = cx ;
	playermove.yc = cy ;
	formfliplist(&gameboard, cx, cy, X);
	gameboard.loc[cx][cy] = X;
	do_flip(&gameboard, listcount);
	zap_list();
	update_gamelog(X, cx, cy);
	update_board_image() ;
	if (gamelog[turns].take == 1)
	  panel_msg("you took 1 piece") ;
	else
	  panel_msg(sprintf(line, "you took %d pieces",gamelog[turns].take));
}



static void
computer_think()
{
	window_set(canvas, WIN_CURSOR, hglass_cursor, 0) ;
	computermove = scanboard(&gameboard, O, beststrategy, lookahead);
	cx = computermove.xc;
	cy = computermove.yc;
	if (validmove(&gameboard, cx, cy, O) == FALSE)
	{
	  panel_msg("Computer is forced to pass.") ;
	  update_gamelog(O, PASS);
	  who_wins() ;
	  canvas_mode = game_over ;
	}
	else
	{
	  formfliplist(&gameboard, cx, cy, O);
	  gameboard.loc[cx][cy] = O;
	  do_flip(&gameboard, listcount);
	  zap_list();
	  update_gamelog(O, cx, cy);
	  animate_move(cx,cy) ;
	  update_board_image() ;
	  if (gamelog[turns].take == 1)
	    panel_msg("Computer took 1 piece") ;
	  else
	    panel_msg(sprintf
	      (line, "Computer took %d pieces",gamelog[turns].take));
	  canvas_mode = player_start ;
	}
	window_set(canvas, WIN_CURSOR, canvas_cursor, 0 ) ;
}



static void
player_think()
{
	playermove = scanboard(&gameboard, X, mostpieces, 0);
	cx = playermove.xc;
	cy = playermove.yc;
	if (validmove(&gameboard, cx, cy, X) == FALSE)
	{
	  panel_msg("you are forced to pass");
	  who_wins() ;
	  update_gamelog(X, PASS);
	  canvas_mode = game_over ;
	}
	else
	{
	  if( computer == FALSE )
	  {
	    canvas_mode = player_start ;
	  }
	  else
	  {
	    window_set(canvas, WIN_CURSOR, hglass_cursor, 0) ;
	    playermove = scanboard(&gameboard, X, beststrategy, lookahead);
	    cx = playermove.xc;
	    cy = playermove.yc;
	    animate_move(cx,cy) ;
	    player_moved() ;
	    canvas_mode = computer_thinking ;
	    window_set(canvas, WIN_CURSOR, canvas_cursor, 0 ) ;
	  }
	}
}



static void
canvas_proc(win,event,arg)
	Canvas	win ;
	Event	*event ;
	caddr_t	arg ;
{
	int	x0,y0 ;


	switch(canvas_mode)
	{
	  case player_start:
	    switch(event_id(event))
	    {
	      case MS_LEFT:
		if(event_is_down(event))
		{
		  piece_x = event_x(event) - PIECE_RAD ;
		  piece_y = event_y(event) - PIECE_RAD ;
		  draw_cute_circle(piece_x, piece_y, PIX_NOT(PIX_DST)) ;
		  canvas_mode = player_moving ;
		}
		break ;
	    }			/* ignore all others */
	    break ;

	  case player_moving:
	    switch(event_id(event))
	    {
	      case LOC_MOVE:
	      case LOC_DRAG:
	      case LOC_TRAJECTORY:
		draw_cute_circle(piece_x, piece_y,PIX_NOT(PIX_DST)) ;
		piece_x = event_x(event) - PIECE_RAD ;
		piece_y = event_y(event) - PIECE_RAD ;
		draw_cute_circle(piece_x, piece_y,PIX_NOT(PIX_DST)) ;
		break ;

	      case LOC_WINENTER:
	      case LOC_WINEXIT:
	      case LOC_RGNENTER:
	      case LOC_RGNEXIT:
	      case WIN_STOP:
		draw_cute_circle(piece_x, piece_y,PIX_NOT(PIX_DST)) ;
		canvas_mode = player_start ;
		break ;

	      case MS_LEFT:
		if(event_is_down(event))
		{
		  draw_cute_circle(piece_x, piece_y,PIX_NOT(PIX_DST)) ;
		  canvas_mode = player_start ;
		}
		else
		{
		  draw_cute_circle(piece_x, piece_y,PIX_NOT(PIX_DST)) ;

		  /* this replaces the getmove subroutine */
		  cx = (piece_x+PIECE_RAD)/CELL_WIDTH + 1 ;
		  cy = (piece_y+PIECE_RAD)/CELL_WIDTH + 1 ;
		  if(validmove(&gameboard, cx, cy, X) == FALSE)
		  {
		    panel_msg("invalid move");
		    canvas_mode = player_start ;
		  }
		  else
		  {
		    player_moved() ;
		    canvas_mode = computer_thinking ;
		    while(canvas_mode == computer_thinking)
		    {
		      computer_think() ;
		      if(canvas_mode == player_start)
			player_think() ;
		    }
		  }
		}
		break ;
	    }			/* ignore all others */
	    break ;



	  case game_over:
	    switch(event_id(event))
	    {
	      case MS_LEFT:
	      case MS_MIDDLE:
	      case MS_RIGHT:
		if(event_is_down(event))
		{
		  panel_msg("Game over");
		}
		break ;
	    }			/* ignore all others */
	    break ;
	}
}



static void
computer_first_proc()
{
	if(turns == 0)
	{
	  canvas_mode = computer_thinking ;
	  while(canvas_mode == computer_thinking)
	  {
	    computer_think() ;
	    if(canvas_mode == player_start)
	      player_think() ;
	  }
	}
}



static void
auto_proc()
{
	if(canvas_mode == game_over)
	  panel_msg("Game over");
	else
	{
	  computer=TRUE ;
	  canvas_mode = player_start ;
	  while(canvas_mode == player_start)
	  {
	    player_think() ;
	    if(canvas_mode == computer_thinking)
	      computer_think() ;
	  }
	}
}


static void
new_game_proc()
{
	/* game initialization */

	init_gamelog() ;
	initlists() ;
	initboard(&gameboard) ;
	erase_board(&oldboard) ;

	firstmove = TRUE ;
	computer = FALSE ;

	canvas_mode = player_start ;

	canvas_init() ;
	panel_msg("to move, use your left mouse button") ;
}
Funky_Stuff
  len=`wc -c < otool.c`
  if [ $len !=    15356 ] ; then
    echo error: otool.c was $len bytes long, should have been    15356
  fi
fi # end of overwriting check
if [ -f othello.icon ]
then
  echo shar: will not over-write existing file othello.icon
else
  echo shar: extracting 'othello.icon',     1933 characters
  cat > othello.icon <<'Funky_Stuff'
/* 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
Funky_Stuff
  len=`wc -c < othello.icon`
  if [ $len !=     1933 ] ; then
    echo error: othello.icon was $len bytes long, should have been     1933
  fi
fi # end of overwriting check

-- 
		-ed falk, sun microsystems
terrorist, cryptography, DES, drugs, cipher, secret, decode, NSA, CIA, NRO.
(The above is food for the NSA line eater.)