[net.sources.games] Program to play the game REVERSI

chris@hwcs.UUCP (Chris Miller) (06/04/85)

: Apologies to downstream sites who have already seen this once.

export PATH || exec /bin/sh $0 $*
: "This is a shar archive; use /bin/sh to extract"
: "Extracted files will be owned by you, and will have"
: "default permissions"
PATH=/bin:/usr/bin
echo If this archive is complete, \"End of archive\" will appear at the end
echo Extracting Makefile
sed 's/^X//' <<'End-of-file' >Makefile
XLIB=/usr/games/lib/reversi
XDESTDIR=
XBIN=/usr/games
XMAN=/usr/man/man6
XMODE=u=rwx,g=rx,o=
XLIBS="-lcurses -ltermlib"
X#	other libraries may be needed for non-4.2 installations
X
Xdefault: reversi .informed
X
Xreversi:
X	cd src; \
X	make LIB=$(LIB) DESTDIR=$(DESTDIR) BIN=$(BIN) LIBS=$(LIBS)
X
X.informed:
X	sh inform
X	echo >.informed
X
Xallinstall: install libinstall
X
Xinstall:
X	cd src; \
X	make LIB=$(LIB) DESTDIR=$(DESTDIR) BIN=$(BIN) MODE=$(MODE) \
X		LIBS=$(LIBS) install
X	@echo Type '"make libinstall"' to install documentation, etc.
X
Xlibinstall:
X	cd lib; make LIB=$(LIB) MAN=$(MAN) DESTDIR=$(DESTDIR) install
X
Xclean:
X	rm -f src/reversi src/reversi.o
X
Xshar: reversi.shar
X
Xreversi.shar: clean Makefile READ.ME inform src/* lib/*
X	shar Makefile READ.ME inform src lib >reversi.shar
End-of-file
echo Extracting READ.ME
sed 's/^X//' <<'End-of-file' >READ.ME
XThis is a revised version of "reversi", using the standard
XI/O library and the curses package.  This distribution may be freely
Xcopied provided that no part of it be used for profit, that credit to
Xthe original author be retained, and that any subsequent amendments be
Xclearly labelled as such.
X
XIt runs on 4.2bsd on a VAX 11/750; I have no reason to believe that
Xany of the code is VAX-dependent, and I do not recall having changed
Xthis version significantly from a version which ran on 4.1bsd and V7.
XSome small modifications to I/O and signal-handling may be needed for
XSystem III/V and other versions.
X
XThe building of library path-names uses some of the Reiser kludges in
Xthe C pre-processor to concatenate part-strings.
X
XTo install, extract the distribution in a clean directory, check the
Xdefinitions at the head of the top-level Makefile, and type "make".
X
XPlease mail me to let me know that you have built this; I would like to
Xknow who has it.  The Makefile will attempt to let me know automatically
Xbut the path used probably won't reach me from sites outside the UK.
End-of-file
echo Extracting inform
sed 's/^X//' <<'End-of-file' >inform
X: 'Set MAILPATH to a sufficient path to reach <Europe>!mcvax!ukc!hwcs!chris'
X
XMAILPATH=chris@hwcs.uucp
Xset `who am i`
X
Xmail chris@hwcs.uucp <<%
XReversi: $1, `date`
X%
End-of-file
echo Making directory src.
mkdir src
echo Extracting src/Makefile
sed 's/^X//' <<'End-of-file' >src/Makefile
XLIBS=-lcurses -ltermlib
XLIB=/usr/games/lib/reversi
XDESTDIR=
XBIN=/usr/games
XMODE=u=rwx,g=rx,o=
XCFLAGS=-O -DLIB=\"$(DESTDIR)$(LIB)
X
Xreversi: reversi.o
X	cc -o reversi reversi.o $(LIBS)
X
Xinstall: reversi
X	cp reversi $(DESTDIR)$(BIN)
X	strip $(DESTDIR)$(BIN)/reversi
X	chmod $(MODE) $(DESTDIR)$(BIN)/reversi
End-of-file
echo Extracting src/reversi.c
sed 's/^X//' <<'End-of-file' >src/reversi.c
X/************************************************
X *                                         	*
X *           REVERSI                       	*
X *                                         	*
X *   Copyright: Chris Miller, 1979, 1981, 1984  *
X *                                         	*
X ************************************************/
X
X/*	The program requires the curses library. */
X
X/*      Notes on efficiency.
X
X	The best way to speed up the program would probably be to
X	improve the heuristic ordering of moves in the search. This
X	may require generating all legal successor positions and doing
X	a quick and dirty static evaluation on them; coding this would
X	be a lot of work.
X
X	Most of the program's time is spent in the routines:
X		sandwich   (c.20%)
X		legal         15%
X		domove        15%
X		static_eval   15%
X	so that any tweaking should be done in these procedures. A certain
X	amount has already been done which explains the rather hacked code,
X	especially in sandwich
X*/
X
X#include <curses.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <sys/types.h>
X#include <sys/times.h>
X
Xlong time();
Xchar *sprintf();
X
X#define SKIP ;
X
X#define FOR_BOARD(i)  for (i=0; i<64; i++)
X
X#define NOMOVE -1
X#define ismove(pos,player) (nextmove(pos,NOMOVE,(POSITION *) NULL, player) != NOMOVE)
X
X#define INFINITY 10000
X
X#define WHITE -1
X#define BLACK 1
X#define FREE  0
X
X#define MAXDEPTH 6
X#define INIT_DEPTH 2
Xint depth = INIT_DEPTH;
X
X#define MAXASPIRE 6
X#define INIT_ASPIRE 3
Xint aspire = INIT_ASPIRE;
Xint expected, margin;        /* Bounds on aspiration for alphabeta search */
X
Xchar input[20];
Xint machine_piece, human_piece;
Xint trace_eval = FALSE, remark = TRUE, cretin_flag;
Xint last_remark;
X#define FIRST_REMARK (INFINITY+1)
Xlong ab_count, se_count;        /* Calls of alfabeta and static_eval */
X
X#define	libpath(l,c) l/c"
Xchar infofil[] = libpath(LIB,reversi.doc);
Xchar rem_file[] = libpath(LIB,reversi.remark);
X
Xtypedef struct {
X	char    SQUARE[64];
X	int	MOVES_LEFT;
X	} POSITION;
X
Xjmp_buf newgame, undomove, intrundo;
X
XWINDOW	*wboard, *wmoves, *wtrace, *wremarks, *wmessages;
X
Xmain ()
X{
X	int finish_up ();
X	int oninterrupt ();
X	int onquit ();
X	int exit ();
X	long initrand;
X
X	signal (SIGQUIT, exit);
X
X	initscr();
X	initwindows();
X	crmode();
X	noecho();
X	move(0, 16);
X	addstr ("R E V E R S I\n\n");
X
X	while (TRUE)
X	    {
X		message ("Do you want instructions? ");
X		*input = getch();
X		if (*input == 'y' || *input == 'Y')
X		    {	info();
X			break;
X		    }
X		if (*input == 'n' || *input == 'N') break;
X		bell();
X	    }
X
X	initrand = time ((long *) NULL);        /* Initialise random number generator */
X	srand ((int) (initrand & 0377));
X
X	get_remarks ();
X
X	signal (SIGINT, oninterrupt);
X	signal (SIGQUIT, onquit);
X
X	while (TRUE)
X	    {   setjmp(newgame);
X		setjmp(undomove);
X		setjmp(intrundo);
X		playgame();
X		setjmp(intrundo);
X		setjmp(undomove);
X		message("Would you like another game? ");
X		*input = getch();
X		if (*input == 'n' || *input == 'N') break;
X		if (*input != 'y' && *input != 'Y') {
X			message ("I assume that grunt means yes!\n");
X			touchwin(wmoves);
X			touchwin(wtrace);
X			touchwin(wremarks);
X		}
X	    }
X
X	finish_up ();
X}
X
Xfinish_up ()
X{
X	wclear(wtrace);
X	wrefresh(wtrace);
X	move(LINES-3, 0);
X	refresh();
X	endwin();
X	puts ("Thank you for your company.\nAu revoir ...");
X	exit (0);
X}
X
Xoninterrupt ()
X{       char ch;
X	int oninterrupt();
X
X	signal (SIGINT, SIG_IGN);      /* Ignore interrupts within interrupts */
X
X	wclear(wtrace);
X	waddstr(wtrace, "            *** Interrupt ***\n");
X        waddstr(wtrace, "x to exit program        c to continue execution\n");
X	waddstr(wtrace, "n to start a new game    u to undo last move\n");
X	waddstr(wtrace, "i for instructions\n");
X	wrefresh(wtrace);
X	while (TRUE)
X	    {   
X		while (whitesp (ch=getch())) SKIP
X		switch (ch)
X		    {
X			case 'x':       message ("*** EXIT ***\n");
X					finish_up();
X
X			case 'u':
X					signal (SIGINT, oninterrupt);
X					wclear(wmessages);
X					wclear(wtrace);
X					redraw();
X					longjmp(intrundo, 1);
X
X			case 'n':
X					signal (SIGINT, oninterrupt);
X					wclear(wmessages);
X					wclear(wtrace);
X					redraw();
X					longjmp(newgame, 0);
X
X			case 'c':       
X					wclear(wmessages);
X					waddstr(wmessages, "*** Continue ***");
X					wclear(wtrace);
X					redraw();
X					signal(SIGINT, oninterrupt);
X					return;
X
X			case 'i':	info();
X					wclear(wtrace);
X					redraw();
X					signal(SIGINT, oninterrupt);
X					return;
X		    }
X	    }
X}
X
Xonquit ()
X{       message ("*** QUIT ***");
X	finish_up ();
X}
X
Xplaygame ()
X{
X	POSITION board, saveboard, oldboard;
X	register int  i;
X	int colour = BLACK, white = 0, black = 0;
X
X	wclear(stdscr);
X	wrefresh(stdscr);
X	wclear(wboard);
X	wclear(wtrace);
X	wclear(wremarks);
X	wclear(wmessages);
X	wclear(wmoves);
X	touchwin(wboard);
X	touchwin(wtrace);
X	touchwin(wremarks);
X	touchwin(wmessages);
X	touchwin(wmoves);
X	while (TRUE)
X	    {   
X		message ("What colour would you like? ");
X		*input = getch();
X		if (*input == 'w' || *input == 'W')
X		    {	human_piece = WHITE;
X			machine_piece = BLACK;
X			break;
X		    }
X		if (*input == 'b' || *input == 'B')
X		    {	human_piece = BLACK;
X			machine_piece = WHITE;
X			break;
X		    }
X		bell();
X	    }
X
X	FOR_BOARD (i) board.SQUARE[i] = FREE;
X	board.MOVES_LEFT = 64;
X	saveboard = board;
X	cretin_flag = FALSE;
X	last_remark = FIRST_REMARK;     /* Force a remark first time */
X
X	clear();
X	display (&board);
X
X	while (!game_over(&board))
X	    {   if (colour==human_piece)
X		    {   oldboard = saveboard;
X			saveboard = board;
X			if (setjmp(undomove)!=0)
X			    {   board = oldboard;
X				colour = human_piece;
X				display (&board);
X				continue;
X			    }
X			if (setjmp(intrundo)!=0)
X			    {   board = saveboard;
X				colour = human_piece;
X				display (&board);
X				continue;
X			    }
X		    }
X
X		if (!makemove(&board, &colour)) return;
X			/* Pass a pointer to colour so that restore
X				can reset it (this is ugly!)
X			*/
X		colour = -colour;
X	    }
X
X	if (machine_piece==colour)
X	    {   
X		display (&board);
X	    }
X
X	FOR_BOARD (i)
X		switch (board.SQUARE[i])
X		    {	case WHITE:	white++; break;
X			case BLACK:	black++;
X		    }
X
X	setjmp(intrundo);
X	clear();
X	refresh();
X	touchwin(wboard);
X	display(&board);
X	move(13, 0);
X	printw ("Game over: I have %d pieces, you have %d pieces",
X		(machine_piece==WHITE? white: black),
X		(human_piece==BLACK? black: white));
X	if (white==black)
X	    {   addstr ("\n\n\t*** Game drawn! ***\n");
X		refresh();
X		return;
X	    }
X	if ((machine_piece==WHITE && white>black) ||
X	    (machine_piece==BLACK && black>white))
X	    {   addstr("\n\n\t*** I win ***\n");
X		if (cretin_flag) addstr ("\n\t*** CRETIN! ***\n");
X	    }
X	   else addstr("\n\n\t*** You win ***\n");
X	refresh();
X}
X
Xmakemove (board, colour)
X	POSITION *board;
X	int *colour;
X{
X	char letter; int number;
X	int mv = NOMOVE, value;
X	int d;
X	struct tms timesbefore, timesafter;
X	long realtime, cputime;
X
X    getmove:
X	if (*colour == human_piece)
X	    {   if (!ismove (board, *colour))
X		    {
X			wmove(wmoves, 0, 0);
X			waddstr (wmoves, "** You have no legal move **");
X			wclrtoeol(wmoves);
X			wrefresh(wmoves);
X			sleep (3);
X			return (TRUE);
X		    }
X
X		wmove(wmoves, 0, 0);
X		wprintw(wmoves, "Your move (%s): __",
X			human_piece==BLACK? "XX": "OO");
X		wclrtoeol(wmoves);
X		waddstr(wmoves, "\b\b");
X		wmove(wmoves, 0, 16);
X		wrefresh(wmoves);
X		getreply (wmoves, input);
X		decode (input, &letter, &number);
X		if (('h'<letter) || (1>number) || (8<number) || ('a'>letter))
X			{ if (!do_command(letter, number, board, colour))
X				return (FALSE);
X			}
X		   else if (okmove(board, letter, number, *colour))
X			    return(TRUE);
X		goto getmove;
X	    }
X
X	if (*colour == machine_piece)
X	    {   if ((mv = nextmove (board, NOMOVE, 0, *colour)) == NOMOVE)
X		    {   
X			wmove(wmoves, 1, 0);
X			wprintw (wmoves, "%s","** I have no legal move **");
X			wclrtoeol(wmoves);
X			wrefresh(wmoves);
X			if (remark) {
X				wmove (wremarks, 0, 0);
X				waddstr (wremarks, "I'm not dead, just resting");
X				wclrtoeol(wremarks);
X				wrefresh(wremarks);
X			}
X			display (board);
X			return (TRUE);
X		    }
X
X		ab_count = se_count = 0;
X		if (trace_eval)
X		    {   times(&timesbefore);
X			realtime = time((long *) NULL);
X		    }
X		if ((!remark) && (!trace_eval) &&
X			(nextmove(board, mv, 0, colour) == NOMOVE))
X		    {		/* Only one legal move - make it */
X				/* (provided we don't need dynamic value) */
X			value = static_eval (board);
X			goto movefound;
X		    }
X
X		if ((board->MOVES_LEFT <= 10) &&
X			(board->MOVES_LEFT <= 3*depth))
X			/* Exhaustive for last 3*depth ply (at most 10!) */
X			d = board->MOVES_LEFT;
X		   else d = depth;
X
X		set_aspiration (board);
X		value = alfabeta (board, d, (*colour)*INFINITY, *colour, &mv);
X
X	    movefound:
X		wmove(wmoves, 1, 0);
X		wprintw(wmoves, "My move   (%s): ",
X			machine_piece==BLACK? "XX": "OO");
X		wclrtoeol(wmoves);
X		wmove(wmoves, 1, 16);
X		wprintw(wmoves, "%c%c", (mv&7)+'a', (mv>>3)+'1');
X		wrefresh(wmoves);
X		domove (board, mv, board, *colour);
X		if (remark) make_remark(value);
X		if (trace_eval)
X		    {
X			wmove (wtrace, 0, 0);
X			wprintw(wtrace, "Depth: %d; Level: %d",
X					depth, aspire);
X			wclrtoeol(wtrace);
X			wprintw(wtrace, "\nValue: %5d", value);
X			wclrtoeol(wtrace);
X			wprintw(wtrace,
X			    "\nBoards evaluated - static: %5D, dynamic: %5D",
X				se_count, ab_count);
X			wclrtoeol(wtrace);
X
X			times (&timesafter);
X			cputime = timesafter.tms_utime + timesafter.tms_stime
X				   - timesbefore.tms_utime - timesbefore.tms_stime;
X
X			wprintw(wtrace, "\nTime - elapsed: %4D, cpu: %4D.%1D",
X				(time(0) - realtime),
X				cputime/60,             /* Seconds */
X				(cputime/6) % 10);      /* Tenths */
X			wclrtoeol(wtrace);
X			wprintw(wtrace, "\nBoards per cpu second: %.2f",
X				(double) (se_count+ab_count) * 60.0
X					/ (double) cputime);
X			wclrtoeol(wtrace);
X			wrefresh(wtrace);
X		    }
X		display (board);
X		return (TRUE);
X	    }
X	return(FALSE);
X}
X
Xokmove (board, letter, number, colour)
X	POSITION *board;
X	char letter;
X	int number;
X	int colour;
X{
X	int mv;
X
X	if ((number<1) || (number>8) || (letter<'a') || (letter>'h'))
X	    {   
X		message ("Column should be in range a-h, row in range 1-8");
X		bell();
X		return (FALSE);
X	    }
X
X	mv = letter - 'a' + 8*(--number);
X	if (!legal(mv, colour, board))
X	    {   
X		message ("Illegal move");
X		bell();
X		return (FALSE);
X	    }
X
X	domove (board, mv, board, colour);
X	display (board);
X	return (TRUE);
X}
X
Xmake_remark (value)
X	int value;
X{
X	value *= machine_piece;
X	wmove(wremarks, 0, 0);
X	wclrtobot(wremarks);
X	if (value < -INFINITY+64)
X	    {
X		waddstr (wremarks, "You should win");
X		last_remark = FIRST_REMARK; /* Always remark next time */
X		if (aspire==MAXASPIRE)
X			wprintw(wremarks, 
X				"        (by at most %d)", -value-INFINITY+64);
X	    }
X	 else if (value < -1000)
X	    {   if (last_remark != -INFINITY)
X		    {   cretin_flag = TRUE;
X			last_remark = -INFINITY;
X			waddstr(wremarks,
X				"\nOnly a cretin could lose from your position");
X		    }
X	    }
X	 else if (value <= 1000) printr(value);
X	 else if (value < INFINITY-63)
X	    {   if (last_remark!=INFINITY)
X		    {   waddstr(wremarks,
X			"Resign, you dolt! Further resistance is futile");
X			last_remark = INFINITY;
X		    }
X	    }
X	 else
X	    {   waddstr (wremarks, "I shall win");
X		last_remark = FIRST_REMARK;
X		if (aspire==MAXASPIRE)
X		       wprintw (wremarks,
X			"        (by at least %d)", value-INFINITY+64);
X	    }
X	wrefresh(wremarks);
X}
X
X#define MAX_REMARKS 50
XFILE *rem_fp;
Xlong good_remarks[MAX_REMARKS], bad_remarks[MAX_REMARKS];
Xint max_good = 0, max_bad = 0;
X
Xget_remarks ()
X/*      Set up file pointers to remarks; the format of the remarks file
X	is
X
X		Remarks to the effect that machine is winning, in
X			increasing order of strength, one per line.
X		A line beginning with the character "@".
X		Remarks to the effect that human is winning, in increasing
X			order of strength, one per line.
X*/
X{       long fp = 0l;
X	int ch;
X
X	rem_fp = fopen (rem_file, "r");
X	if (rem_fp==NULL)
X	    {   message ("Remarks not available");
X		bell();
X		sleep(1);
X		return;
X	    }
X
X	while ((ch=getc(rem_fp))!=EOF)
X	    {   if (ch=='@') break;
X		good_remarks[max_good++]=fp++;
X		if (max_good==MAX_REMARKS)
X		    {   message ("Too many remarks");
X			bell();
X			sleep(1);
X			return;
X		    }
X		while (ch!='\n')
X		    {   ch = getc(rem_fp);
X			fp++;
X		    }
X	    }
X	max_good--;
X
X	fp++;
X	while (ch!='\n')
X	    {   ch = getc(rem_fp);
X		fp++;
X	    }
X
X	while ((ch=getc(rem_fp))!=EOF)
X	    {   bad_remarks[max_bad++]=fp++;
X		if (max_bad==MAX_REMARKS)
X		    {   message("Too many remarks");
X			bell();
X			sleep(1);
X			return;
X		    }
X		while (ch!='\n')
X		    {   ch=fgetc(rem_fp);
X			fp++;
X		    }
X	    }
X	max_bad--;
X}
X
Xprintr (n)
X	int n;
X/*      Locate file-pointer and print appropriate line. The strange
X	computation is to cluster remarks towards low evaluations,
X	since the evaluation function changes more rapidly with small
X	changes in position as the evaluation gets larger
X*/
X
X
X{       int ch;
X	int index;
X	float findex;
X	int sign_n = (n<0? -1: 1);
X	char string[100], *stringp = string;
X
X	if (rem_fp==NULL) return;
X
X	findex = (float) abs(n)/1000.0;
X	index = findex * (2-findex) * (n<0? max_bad: max_good) + 0.5;
X	if (index*sign_n == last_remark)
X			/* Don't make the same remark twice in a row */
X	    {   
X		return;
X	    }
X
X	last_remark = index*sign_n;
X
X	fseek (rem_fp, n<0? bad_remarks[index]: good_remarks[index], 0);
X	while ((ch=getc(rem_fp))!=EOF)
X	    {   if (ch=='\n') break;
X		*stringp++ = ch;
X	    }
X	*stringp = '\0';
X	waddstr(wremarks, string);
X}
X
Xdo_command (letter, number, board, colour)
X	POSITION *board;
X	char letter;
X	int number;
X	int *colour;
X{
X	int mv = NOMOVE;
X
X	switch (letter)
X	    {	case 'q':	return(FALSE);
X
X		case 'h':
X		case '?':       print_help ();
X				wclear(wmessages);
X				redraw();
X				break;
X
X		case 'p':       redraw();
X				break;
X
X		case 't':	trace_eval = !trace_eval;
X				message("Tracing now %s",
X					   trace_eval? "on": "off");
X				if (!trace_eval) {
X					wclear(wtrace);
X					wrefresh(wtrace);
X				}
X				break;
X
X		case 'r':       if (rem_fp==NULL)
X				    {   
X				   	 message("Remark file not available");
X					 break;
X				    }
X				remark = !remark;
X				if (remark) {
X					message ("OK, I'll make remarks");
X				} else {
X					message ("OK, I'll shut up");
X					wclear(wremarks);
X					wrefresh(wremarks);
X				   }
X				break;
X
X		case 's':	if (number==0) {
X					message ("Current search depth: %d",
X						    depth);
X				} else if (number<0 || number>MAXDEPTH) {
X					message ("Search depth must be in range 1-%d",
X						    MAXDEPTH);
X					bell();
X				}
X				else {	depth = number;
X					message ("Search depth set to %d",
X						    number);
X				     }
X				break;
X
X		case 'l':	if (number==0)
X					message ("Current aspiration level: %d",
X						    aspire);
X				else if (number<0 || number>MAXASPIRE) {
X					message ("Aspiration level must be 1-%d",
X						    MAXASPIRE);
X					bell();
X				}
X				else {	aspire = number;
X					message ("Aspiration level set to %d",
X						    number);
X				     }
X				break;
X
X		case 'e':       message("Static evaluation: %d",
X					     static_eval (board));
X				break;
X
X		case 'v':	if (number==0) number=depth;
X				if (number<1 || number>9) {
X					message("Invalid depth");
X					bell();
X				}
X				else {  set_aspiration (board);
X					message("Dynamic evaluation: %d",
X				  	alfabeta (board, number,
X						 human_piece*INFINITY,
X						 human_piece, &mv));
X				     }
X				break;
X
X		case 'm':	if (number==0) number=depth;
X				if (number<1 || number>9)
X				    {   message ("Invalid depth");
X					bell();
X					break;
X				    }
X				set_aspiration (board);
X				alfabeta (board, number, human_piece*INFINITY,
X						human_piece, &mv);
X				message("I would move at %c%c",
X					 'a'+(mv&7), '1'+(mv>>3));
X				break;
X
X		case '>':       save(board, colour);
X				break;
X
X		case '<':       restore(board, colour);
X				break;
X
X		case 'u':       longjmp(undomove, 1);
X				break;
X
X		default:        message ("Type ? for help");
X				bell();
X				break;
X	    }
X	return (TRUE);
X}
X
Xalfabeta (pos, depth, parent_opt, sign, parent_best)
X
X/* Alpha-beta pruning algorithm.
X
X	If sign=1, then try to MAXIMISE score, else MINIMISE.
X	Parent node wants to MINIMISE/MAXIMISE, so as soon as
X	we exceed parent_opt, give up and return INFINITY.
X	Return best move in parent_best.
X
X	Externals used:
X		static_eval (position)
X		ismove (position, player) [does player have a legal move?]
X		game_over (pos)
X		typedef POSITION
X		aspiration (player, depth, position)  [return the aspiration limit for
X					       next level of search]
X		nextmove (position, mv, nextposition, player)
X			[give the next legal move for player in position;
X			   put the resulting position in nextposition]
X
X*/
X
X	POSITION *pos;
X	int depth, parent_opt, sign, *parent_best;
X
X{
X	POSITION nextpos;
X	int value, this_opt, this_best = NOMOVE, mv = NOMOVE, asp = 0;
X
X	if ((depth==0) || game_over(pos))
X	    {	value = static_eval (pos);
X		*parent_best = NOMOVE;
X		if ((sign*value) > (sign*parent_opt))
X			return (sign*INFINITY);
X		   else return (value);
X	    }
X
X	ab_count++;     /* Record boards dynamically evaluated */
X	this_opt = (sign==1? -INFINITY: INFINITY);
X
X	if (!ismove(pos,sign))		/* No legal move */
X	    {   value = alfabeta (pos, depth, this_opt, -sign, &this_best);
X		goto valfound;
X	    }
X
X	asp = sign * aspiration (sign, depth);
X
X	while ((mv=nextmove (pos, mv, &nextpos, sign)) != NOMOVE)
X	    {	value = alfabeta (&nextpos, depth-1, this_opt, -sign, &this_best);
X	      valfound:
X		if ((sign*value) >= (sign*parent_opt))
X			return (sign*INFINITY);
X
X		if ((sign*value) > asp)
X		    {   *parent_best = mv;
X			return (value);
X		    }
X
X		if ((sign*value) > (sign*this_opt))
X		    {	this_opt = value;
X			*parent_best = mv;
X		    }
X
X			/* If two moves have same evaluation, choose
X			   uniformly randomly between them (of course,
X			   where several moves have same value, this
X			   is biased towards the later ones
X			*/
X
X		if ((value == this_opt) && (rand() & 020))
X			*parent_best = mv;
X	    }
X
X	return (this_opt);
X}
X
Xset_aspiration (position)
X	POSITION *position;
X/*	Aspirations are as follows:
X	   Aspiration-level		Aspiration
X		1		The worse of 0, static value
X		2		The static value
X		3		10% better than the static value
X		4		25% better than the static value
X		5		Any winning move
X		6		The move winning by the greatest margin
X
X	It is assumed that the opponent has
X	the same level of aspiration as the program.
X*/
X
X{
X	if (aspire==MAXASPIRE)
X	    {	expected = 0;
X		margin = INFINITY;
X		return;
X	    }
X
X	if (aspire==(MAXASPIRE-1))
X	    {	expected = 0;
X		margin = INFINITY-64;
X		return;
X	    }
X
X	expected = static_eval (position);
X
X	switch (aspire)
X	    {   case 1: expected /= 2;
X			margin = -abs(expected);
X			return;
X		case 2: margin = 0;  return;
X		case 3: margin = abs (expected/10);  return;
X		case 4: margin = abs (expected/4);   return;
X	    }
X}
X
Xaspiration (player, depth)
X	int player, depth;
X{
X	if (aspire==MAXASPIRE) return (player*INFINITY);
X	if (depth<3 && aspire>1) return (player*(INFINITY-64));
X	return (expected+player*margin);
X}
X
Xstatic_eval (pos)
X	POSITION *pos;
X
X/*	Square values (only valid when a "vulnerable" piece occupies the
X	square in question)
X*/
X
X#define	VCORN	100
X		/* Corner */
X#define VORTH	-30
X		/* Orthogonally next to corner */
X#define VDIAG	-50
X		/* Diagonally     "   "    "   */
X#define VEDGE	20
X		/* On edge */
X#define VNEXT	-7
X		/* Next to edge */
X#define VNORM	1
X		/* Elsewhere */
X
X{
X	static int model[64] = {
X	  VCORN, VORTH, VEDGE, VEDGE, VEDGE, VEDGE, VORTH, VCORN,
X	  VORTH, VDIAG, VNEXT, VNEXT, VNEXT, VNEXT, VDIAG, VORTH,
X	  VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE,
X	  VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE,
X	  VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE,
X	  VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE,
X	  VORTH, VDIAG, VNEXT, VNEXT, VNEXT, VNEXT, VDIAG, VORTH,
X	  VCORN, VORTH, VEDGE, VEDGE, VEDGE, VEDGE, VORTH, VCORN
X	};
X
X	register int i;
X	register int value = 0;
X	int scores[64];
X
X	se_count++;     /* Record number of static evaluations */
X	if (game_over(pos))
X	    {   FOR_BOARD(i) value += pos->SQUARE[i];
X		return (value>0? INFINITY+value-64:
X			value<0? -INFINITY+value+64: 0);
X	    }
X
X	FOR_BOARD(i) scores[i] = 0;
X
X	find_invulnerable (pos, scores);
X		/* scores now contains VINVUL (or -VINVUL)
X			for each invulnerable piece, and 0 elsewhere;
X			now fill in other evaluations (special cases:
X			next to corner [bad!], on edge[good!], next to
X			edge[poor], anywhere else[boring])
X		*/
X
X	FOR_BOARD(i)
X		value += (scores[i]? scores[i]: model[i]*pos->SQUARE[i]);
X
X	return (value);
X}
X
Xgame_over (pos)
X	POSITION *pos;
X{
X	if (!(pos->MOVES_LEFT)) return (TRUE);
X
X	return ((!ismove(pos, WHITE)) && !(ismove(pos, BLACK)));
X}
X
X#define VINVUL 50
X
Xfind_invulnerable (board, scores)
X	POSITION *board;
X	int scores[64];
X
X/*	This function finds invulnerable pieces, and scores them
X	appropriately; it does not find ALL invulnerable pieces -
X	in fact, only concave blocks including a corner - but
X	nevertheless should provide a good approximation.
X*/
X
X{
X	int hwm, corner, value, i, j;
X
X	if ((corner=board->SQUARE[0]) != 0)
X	    {	value = corner*VINVUL;
X		hwm = 7;
X
X		for (i=0; i<56; i+= 8)
X		    {   if (board->SQUARE[i] != corner) break;
X			scores[i] = value;
X			for (j=1; j<hwm; j++)
X			    {   if (board->SQUARE[i+j] != corner)
X				    {	hwm = j;
X					break;
X				    }
X
X				scores[i+j] = value;
X			    }
X		    }
X		scores[0] = corner*VCORN;
X	    }
X
X	if ((corner=board->SQUARE[7]) != 0)
X	    {	value = corner*VINVUL;
X		hwm = 0;
X
X		for (i=0; i<56; i+= 8)
X		    {   if (board->SQUARE[i+7] != corner) break;
X			scores[i+7] = value;
X			for (j=6; j>hwm; j--)
X			    {   if (board->SQUARE[i+j] != corner)
X				    {	hwm = j;
X					break;
X				    }
X
X				scores[i+j] = value;
X			    }
X		    }
X		scores[7] = corner*VCORN;
X	    }
X
X	if ((corner=board->SQUARE[56]) != 0)
X	    {	value = corner*VINVUL;
X		hwm = 7;
X
X		for (i=56; i>0; i-= 8)
X		    {   if (board->SQUARE[i] != corner) break;
X			scores[i] = value;
X			for (j=1; j<hwm; j++)
X			    {   if (board->SQUARE[i+j] != corner)
X				    {	hwm = j;
X					break;
X				    }
X
X				scores[i+j] = value;
X			    }
X		    }
X		scores[56] = corner*VCORN;
X	    }
X
X	if ((corner=board->SQUARE[63]) != 0)
X	    {	value = corner*VINVUL;
X		hwm = 0;
X
X		for (i=56; i>0; i-= 8)
X		    {   if (board->SQUARE[i+7] != corner) break;
X			scores[i+7] = value;
X			for (j=6; j>hwm; j--)
X			    {   if (board->SQUARE[i+j] != corner)
X				    {	hwm = j;
X					break;
X				    }
X
X				scores[i+j] = value;
X			    }
X		    }
X		scores[63] = corner*VCORN;
X	    }
X}
X
Xwhitesp (ch)
Xchar ch;
X{
X	return (ch==' ' || ch=='\n' || ch=='\t');
X}
X
X
Xdecode (s, ch, num)
Xchar *s,*ch;
Xint *num;
X{
X	if (*s=='\0') return;
X	*ch = ('A'<= *s && *s <='Z'? *s-'A'+'a': *s);
X	s++;
X	*num = 0;
X	sscanf(s, " %d", num);
X}
X
X/*************************************/
X
Xnextmove (pos, mv, nextpos, player)
X	POSITION *pos, *nextpos;
X	int mv, player;
X{
X	/* On first entry for a given position, move is set to NOMOVE,
X	   i.e. -1.  Moves are generated in a rough plausibility order
X	   given by the list structure move_table, whose head is
X	   move_table[-1], and whose tail-pointer is set to NOMOVE.
X	   The order is purely heuristic, and corresponds roughly to
X	   the values associated by static_eval with different squares.
X	*/
X
X	static int m_table[65] =
X	    {	0,  7,  6,  5,  4, 24, 16,  8, 56, 15,
X		14, 13, 12, 25, 17, 49, 48, 23, 22, 21,
X		20, 26, 42, 41, 40, 31, 30, 29, 28, 35,
X		34, 33, 32, 39, 38, 37, 36, 10, 43, 51,
X		59, 47, 46, 45, 44, 27, 19, 50, 58, 55,
X		54, 53, 52,  1, 11, -1, 57, 63, 62, 61,
X		60, 18,  3,  9,  2
X	    };
X
X	static int *move_table = &(m_table[1]);
X
X	while ((mv=move_table[mv])!=NOMOVE)
X		if (legal(mv, player, pos))
X		    {	if (nextpos) domove (pos, mv, nextpos, player);
X			   /* Next position generated only if pointer is
X				non-zero */
X			return (mv);
X		    }
X	return (NOMOVE);	/* No more legal moves */
X}
X
X/**************************************/
X
X/*      Globals for passing arguments to "sandwich";
X	this is to save time putting arguments on and off the
X	stack in a very heavily used piece of code
X*/
X
Xint s_move, s_row, s_col, s_player, s_opponent, s_flip;
XPOSITION *s_pos;
X
Xdomove (pos, mv, nextpos, player)
X	POSITION *pos, *nextpos;
X	int mv, player;
X{
X	register int i;
X	if (pos != nextpos) FOR_BOARD(i) nextpos->SQUARE[i] = pos->SQUARE[i];
X
X	s_move = mv;
X	s_row = mv >> 3;
X	s_col = mv & 7;
X	s_player = player;
X	s_opponent = -player;
X	s_flip = TRUE;
X	s_pos = nextpos;
X
X	nextpos->SQUARE[s_move] = player;
X
X	sandwich (/* mv, */ -9 /* , player, nextpos, TRUE */ );
X	sandwich (/* mv, */ -8 /* , player, nextpos, TRUE */ );
X	sandwich (/* mv, */ -7 /* , player, nextpos, TRUE */ );
X	sandwich (/* mv, */ -1 /* , player, nextpos, TRUE */ );
X	sandwich (/* mv, */  1 /* , player, nextpos, TRUE */ );
X	sandwich (/* mv, */  7 /* , player, nextpos, TRUE */ );
X	sandwich (/* mv, */  8 /* , player, nextpos, TRUE */ );
X	sandwich (/* mv, */  9 /* , player, nextpos, TRUE */ );
X
X	nextpos->MOVES_LEFT = (pos->MOVES_LEFT) - 1;
X}
X
X/*************************************/
X
Xlegal (mv, player, pos)
X	POSITION *pos;
X	int mv, player;
X{
X	if (pos->SQUARE[mv]) return(FALSE);
X					   /* Already occupied */
X
X	if (pos->MOVES_LEFT > 60)	/* Initial 4 moves */
X		return ((mv==27) || (mv==28) || (mv==35) || (mv==36));
X				/* Central four squares */
X
X	s_move = mv;
X	s_row = mv >> 3;
X	s_col = mv & 7;
X	s_player = player;
X	s_opponent = -player;
X	s_flip = FALSE;
X	s_pos = pos;
X
X	return ( sandwich ( /* mv, */ -9 /* , player, pos, FALSE */ ) ||
X		 sandwich ( /* mv, */ -8 /* , player, pos, FALSE */ ) ||
X		 sandwich ( /* mv, */ -7 /* , player, pos, FALSE */ ) ||
X		 sandwich ( /* mv, */ -1 /* , player, pos, FALSE */ ) ||
X		 sandwich ( /* mv, */  1 /* , player, pos, FALSE */ ) ||
X		 sandwich ( /* mv, */  7 /* , player, pos, FALSE */ ) ||
X		 sandwich ( /* mv, */  8 /* , player, pos, FALSE */ ) ||
X		 sandwich ( /* mv, */  9 /* , player, pos, FALSE */ ));
X}
X
X/**********************************/
X
Xsandwich ( /* s_move, */ increment /* , s_player, s_pos, s_flip */ )
X	register int increment;
X	/* int s_player, s_move, s_flip; */
X	/* POSITION *s_pos; */
X
X/*	Test whether the square move sandwiches a line
X	of enemy pieces in the direction [row_inc, col_inc];
X	If (s_flip) then update position by capturing such pieces
X*/
X
X{
X	register int square, offset;
X	int row_offset, col_offset, piece, piece_count;
X
X	if (s_pos->SQUARE[s_move+increment] != s_opponent) return(FALSE);
X
X			/*  Quick test to catch most failures -
X			    note that the tested square may not even
X			    be on the board, but the condition is a
X			    sufficient one for failure */
X
X	row_offset = (increment<-1 ? s_row:	/* inc -1: -9, -8, -7 */
X		      increment> 1 ? 7-s_row:      /* inc  1:  7,  8,  9 */
X		      8);
X	col_offset = (increment & 4? s_col:	/* inc -1: -9, -1,  7 */
X		      increment & 1? 7-s_col:      /* inc  1: -7,  1,  9 */
X		      8);
X
X	offset = (row_offset>col_offset? col_offset: row_offset);
X			/* offset = shortest distance to an edge in the
X			   direction of search */
X	if (2 > offset) return (FALSE);
X
X	piece_count = 1;
X	square = s_move+increment;
X
X	while (--offset)
X	    {   if (!(piece=s_pos->SQUARE[square += increment]))
X			return (FALSE);	 /* If empty square, give up */
X
X		if (piece==s_player) break;
X		    else piece_count++;	 /* Count opponent's pieces
X						   encountered */
X	    }
X
X	if (!offset) return (FALSE);
X
X	if (s_flip)
X		while (piece_count--)
X			s_pos->SQUARE[square -= increment] = s_player;
X
X	return (TRUE);
X}
X
Xprint_help ()
X{
X	clear();
X	move(3, 0);
X	addstr ("    <column><row> to make a move\n");
X	addstr ("    ? or h        to obtain this message\n");
X	addstr ("    q             to terminate this game at once\n");
X	addstr ("    p             to re-display the current position\n");
X	addstr ("    t             to switch on or off tracing of evaluation\n");
X	addstr ("    r             to switch remarks on or off\n");
X	addstr ("    s<n>          to set depth of search to n\n");
X	addstr ("    l<n>          to set the level of aspiration to n\n");
X	addstr ("    e             to get the static evaluation of the position\n");
X	addstr ("    v<n>          to get the dynamic evaluation (depth n)\n");
X	addstr ("    m<n>          to get a suggested next move (depth n)\n");
X	addstr ("    u             to undo your last move\n");
X	addstr ("    >             to save the position in a file\n");
X	addstr ("    <             to restore from a file\n");
X	refresh();
X	message("Type SPACE to continue ... ");
X	*input = getch();
X}
X
Xinfo()
X{
X	char cmd[256];
X
X	clear();
X	refresh();
X	if (access(infofil, 0444) == -1) {
X		message("Unfortunately, there is no information available\n");
X		bell();
X		sleep(1);
X		return;
X	}
X	sprintf(cmd, "/usr/ucb/more %s", infofil);
X	system(cmd);
X	message("Hit new line when ready...");
X	*input = getch();
X	clear();
X	refresh();
X	crmode();
X	noecho();
X
X	return;
X}
X
Xdisplay (board)
X	POSITION *board;
X{
X	register int i, j;
X	char *piece;
X
X	wclear(wmessages);
X	wrefresh(wmessages);
X	wmove(wboard, 0, 0);
X
X	waddstr(wboard, "   a  b  c  d  e  f  g  h\n");
X	for (i=0; i<8; i++)
X	    {	wprintw(wboard, "%1d|", i+1);
X		for (j=0; j<8; j++)
X		    {   switch (board->SQUARE[8*i+j])
X			    {	case WHITE: piece = "OO"; break;
X				case BLACK: piece = "XX"; break;
X				case FREE : piece = "  ";  break;
X			    }
X			wprintw(wboard, "%s|", piece);
X		    }
X		wclrtoeol(wboard);
X		waddch(wboard, '\n');
X	    }
X	wrefresh(wboard);
X}
X
Xgetreply (w, str)
X	WINDOW *w;
X	char *str;
X{       int ch;
X	char *s = str;
X
X	while ((ch=getch()) == ' ' || ch == '\t') SKIP
X	while (ch!='\n' && ch!='\r')
X	    {   if (ch==EOF)        /* End-of-file or read error */
X		    {   if ((ch=getch())==EOF)    /* Re-try; if failure
X						   assume EOF */
X			    {   message ("Unexpected end of input");
X				bell();
X				finish_up();
X			    }
X			  else {
X				continue;
X			  }
X		    }
X		if (ch==0177) {
X			if (s>str) {
X				s--;
X				waddstr(w, "\b \b");
X				wrefresh(w);
X			}
X			ch = getch();
X			continue;
X		}
X		*s++ = ch;
X		waddch(w, ch);
X		wrefresh(w);
X		ch = getch();
X	    }
X
X	*s = 0;
X}
X
X#define MAGIC   0501    /* Header word */
X
Xsave(board, colour)
X	POSITION *board;
X	int *colour;
X{       FILE *fp;
X	char fname[128];
X
X	message("Save into: ");
X	getreply(wmessages, fname);
X	if ((fp=fopen(fname,"w"))==NULL)
X	    {   message("Can't open %s\n", fname);
X		bell();
X		return;
X	    }
X	putw(MAGIC, fp);
X	fwrite(board, sizeof(POSITION), 1, fp);
X	putw(*colour, fp);
X	fclose(fp);
X}
X
Xrestore(board, colour)
X	POSITION *board;
X	int *colour;
X{       FILE *fp;
X	char fname[128];
X
X	message("Restore from: ");
X	getreply(wmessages, fname);
X	if ((fp=fopen(fname,"r"))==NULL)
X	    {   message("Can't open %s\n", fname);
X		bell();
X		return;
X	    }
X	if (getw(fp)!=MAGIC)
X	    {   message("Not a saved position\n");
X		bell();
X		return;
X	    }
X	fread(board, sizeof(POSITION), 1, fp);
X	*colour = getw(fp);
X	fclose(fp);
X}
X
Xbell()
X{
X	putchar('\07');
X	fflush(stdout);
X}
X
Xmessage(f, a1, a2, a3, a4, a5)
X{
X	wclear(wmessages);
X	wprintw(wmessages, f, a1, a2, a3, a4, a5);
X	wrefresh(wmessages);
X}
X
Xinitwindows()
X{
X	wboard =	newwin(9, 30, 2, (COLS-30)/2);
X	wmoves =	newwin(2, 30, 12, (COLS-30)/2);
X	wmessages =	newwin(1, COLS, 0, 0);
X	wtrace =	newwin(5, COLS-8, 18, 8);
X	wremarks =	newwin(2, COLS, 16, 0);
X}
X
Xredraw()
X{
X	clear();
X	refresh();
X	touchwin(wboard);
X	touchwin(wmessages);
X	touchwin(wtrace);
X	touchwin(wremarks);
X	touchwin(wmoves);
X	wrefresh(wboard);
X	wrefresh(wmessages);
X	wrefresh(wtrace);
X	wrefresh(wremarks);
X	wrefresh(wmoves);
X}
End-of-file
echo Making directory lib.
mkdir lib
echo Extracting lib/Makefile
sed 's/^X//' <<'End-of-file' >lib/Makefile
XLIB=/usr/games/lib/reversi
XMAN=/usr/man/man6
XDESTDIR=
X
Xreversi.doc: reversi.me
X	tbl reversi.me | nroff -me | col > reversi.doc
X
Xinstall: reversi.doc reversi.remark
X	cp reversi.doc reversi.remark $(DESTDIR)$(LIB)
X	cp reversi.6 $(DESTDIR)$(MAN)
End-of-file
echo Extracting lib/reversi.6
sed 's/^X//' <<'End-of-file' >lib/reversi.6
X.TH REVERSI 6 17/1/79
X.SH NAME
Xreversi \- the game of Reversi (or Othello)
X.SH SYNOPSIS
X/usr/games/reversi
X.SH DESCRIPTION
X.I Reversi
Xis a computerised version of the game sold in
Xshops as Othello. It is played on a 8x8 board with
Xpieces which are white on one side and black on the
Xother (or X and O in our case). The games starts with
Xthe players placing
Xtwo pieces of each colour in the centre of the board.
XSubsequent moves are made by adding a piece to the board so that
Xit brackets one or more of your opponents pieces between
Xitself and another of your pieces. All such bracketed
Xpieces are then reversed and become your pieces. The
Xobject of the game is to have more pieces than the computer
Xwhen the board is full.
X.PP
XMoves are specified by board positions. Other commands
Xare "h" for help, "p" to print the board,
Xand "q" to quit; there are various parameters governing
Xthe standard of play - the info provided when you run
Xthe program gives full details
X.SH AUTHOR
XC.D.F. Miller
End-of-file
echo Extracting lib/reversi.doc
sed 's/^X//' <<'End-of-file' >lib/reversi.doc
X
X
X
X
X
X
X
X                    _T_h_e _G_a_m_e _o_f _R_e_v_e_r_s_i
X
X
X     _R_e_v_e_r_s_i is an old english game, recently "invented"  in
XJapan  under the name Othello. It is played on an 8x8 board,
Xwith pieces which are black on one side  and  white  on  the
Xother.   Except  for  the initial 4 moves, which must all be
Xmade in the centre 4 squares, a legal move consists of plac-
Xing  a  piece  of  one's  own  colour  on the board so as to
X"sandwich" a row (orthogonal or diagonal) of pieces  of  the
Xopposite  colour  between  the piece just placed and another
Xpiece of the same colour.   All  pieces  so  sandwiched  are
Xflipped over to reveal the colour on the other side.
X
X     The object is to have more pieces than the opponent  at
Xthe  end of the game (i.e. when the board is full or neither
Xside has a legal move).  If you have no legal move, you sim-
Xply miss a turn.  Black moves first.
X
X     To specify a move, type its  coordinates  in  the  form
X<letter><digit>.
X
X     There are also a number of  other  commands.   Some  of
Xthese  cause  evaluations to be printed out - in this case a
Xnegative value means white is winning, a positive one  means
Xblack  is  winning.   Some commands take an optional integer
Xargument to specify a depth - the  default  is  the  current
Xdepth of search.
X
X    Commands:
X    ____________________________________________________
X    ? or h      Print help
X    q           Quit the current game.
X    p           Print the position.
X    t           Switch tracing on or off - with  tracing
X                on  the  program's estimate of the value
X                of the position is printed each time  it
X                moves.
X    e           Print the static evaluation of the posi-
X                tion;  this  is  a  very unsophisticated
X                type of evaluation.
X    v<n>        Print the dynamic evaluation of the  po-
X                sition,  using  a search depth of n (the
X                default is the current depth).  This in-
X                volves  the  program  searching possible
X                lines of play up to  n  moves  ahead  in
X                order to evaluate the position.
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X    m<n>        Suggest a move; the program uses its own
X                search  strategy  with  depth n (default
X                the current  depth)  to  find  the  move
X                which  it  would  play in its opponent's
X                position.
X    s<n>        Set the current search depth to n moves.
X                If  n  is  missing, the current depth is
X                printed.   At  present,  n  may  not  be
X                greater  than  6; it is set initially to
X                2.  A beginner should  set  n  to  1  at
X                first.
X    l<n>        Set the current level of  aspiration  to
X                n; the higher the value of n, the higher
X                will be the program's standards for  ac-
X                cepting  a  move  as  playable.  If n is
X                missing, the current level  is  printed.
X                At present, n may not be greater than 6;
X                it is initially 3.   A  beginner  should
X                play  with  n  set  to  1, and an expert
X                should not normally set n greater than 5
X                (at  level 6, the program not only tries
X                to win, but tries to win by the greatest
X                possible margin).
X    r           Switch remarks on or off;  with  remarks
X                on, the program will make comments (usu-
X                ally rude) about the state of play.
X    u           Undo the last move you made -  this  is,
X                of course, cheating unless you genuinely
X                mistyped it!
X    >           Save the position on a file  -  you  are
X                prompted for the filename.
X    <           Restore a position from file.
X
X
X     All settings (tracing, search  depth,  level,  remarks)
Xare preserved from one game to the next.
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
End-of-file
echo Extracting lib/reversi.me
sed 's/^X//' <<'End-of-file' >lib/reversi.me
X.pl 0
X.(b C
X.i
XThe Game of Reversi
X.r
X.)b
X.pp
X.i Reversi
Xis an old english game, recently "invented" in Japan
Xunder the name Othello. It is played on an 8x8 board, with
Xpieces which are black on one side and white on the other.
XExcept
Xfor the initial 4 moves, which must all be made in the centre 4
Xsquares, a legal move consists of placing a piece of one's own
Xcolour on the board so as to "sandwich" a row (orthogonal or
Xdiagonal) of pieces of the opposite colour between the piece just
Xplaced and another piece of the same colour.
XAll pieces so
Xsandwiched are flipped over to reveal the colour on the other
Xside.
X.pp
XThe object is to have more pieces than the opponent at the end
Xof the game (i.e. when the board is full or neither side has a
Xlegal move).
XIf you have no legal move, you simply miss a turn.
XBlack moves first.
X.pp
XTo specify a move, type its coordinates in the form
X<letter><digit>.
X.pp
XThere are also a number of other commands.
XSome of these
Xcause evaluations to be printed out - in this case a negative
Xvalue means white is winning, a positive one means black is winning.
XSome commands take an optional integer argument to specify
Xa depth \- the default is the current depth of search.
X.TS
Xcenter;
Xl lw(40).
XCommands:
X_
X? or h	Print help
Xq	T{
XQuit the current game.
XT}
Xp	T{
XPrint the position.
XT}
Xt	T{
XSwitch tracing on or off - with tracing on the
Xprogram's estimate of the value of the position
Xis printed each time it moves.
XT}
Xe	T{
XPrint the static evaluation of the position;
Xthis is a very unsophisticated type of evaluation.
XT}
Xv<n>	T{
XPrint the dynamic evaluation of the position, using
Xa search depth of n (the default is the current depth).
XThis involves the program searching possible lines of
Xplay up to n moves ahead in order to evaluate the
Xposition.
XT}
Xm<n>	T{
XSuggest a move; the program uses its own search strategy
Xwith depth n (default the current depth) to find the
Xmove which it would play in its opponent's position.
XT}
Xs<n>	T{
XSet the current search depth to n moves.
XIf n is missing, the current depth is printed.
XAt present, n may not be greater than 6; it is set initially to 2.
XA beginner should set n to 1 at first.
XT}
Xl<n>	T{
XSet the current level of aspiration to n; the higher the
Xvalue of n, the higher will be the program's standards
Xfor accepting a move as playable.
XIf n is missing, the current level is printed.
XAt present, n may not be greater than 6; it is initially 3.
XA beginner should play with n set to 1, and an expert should not normally set
Xn greater than 5 (at level 6, the program not only tries
Xto win, but tries to win by the greatest possible margin).
XT}
Xr	T{
XSwitch remarks on or off; with remarks on, the program
Xwill make comments (usually rude) about the state of play.
XT}
Xu	T{
XUndo the last move you made \- this is, of course, cheating
Xunless you genuinely mistyped it!
XT}
X>	T{
XSave the position on a file - you are prompted for the filename.
XT}
X<	T{
XRestore a position from file.
XT}
X.TE
X.pp
XAll settings (tracing, search depth, level, remarks) are
Xpreserved from one game to the next.
End-of-file
echo Extracting lib/reversi.remark
sed 's/^X//' <<'End-of-file' >lib/reversi.remark
XPretty level; I'll start trying soon.
XNot much in it; superior skill will tell.
XMy superior skill is telling.
XYou're beginning to lose your grip.
XYou're really not so hot after all.
XWake up! You're falling by the wayside.
XCome on, concentrate!
XIf I weren't a computer, I'd be starting to get bored.
XThey tell me noughts and crosses is easier.
XI'm starting to get bored.
XI could beat you with one CPU tied behind my back.
XWho on earth programmed YOU?
XQuiche-eater!
XI seem to be hammering you a little.
XAre you impersonating a random-number generator?
XPerhaps you should try practising against a desk-calculator.
XJust remember, when things are going badly DON'T PANIC.
XPerhaps it's about time you started to panic.
XYour position leaves almost as much to be desired as does your intelligence.
XI'm thrashing you.
XOh for some REAL opposition.
XWhat a massacre!
XMy brilliance is rivalled only by your dullness.
XWhy don't you take up knitting instead?
XWhy don't you take up computer science instead?
XThat's how it goes - I win some, you lose some.
XYou've had it. Why not give up now?
X@
XJust about dead level.
XMaybe you have just the tiniest advantage.
XI shall easily sweep aside your slight advantage.
XMaybe you're not as daft as you look, after all.
XGood! You're beginning to get the hang of it now.
XI suppose I ought to start playing properly soon.
XAll right, no need to show off.
XHave you by some chance played this game before?
XHmmm. This game may be harder than I thought.
XAre you fifth-generation?
XI'm just trying to lull you into a false state of security.
XI wish to register a complaint.
XI didn't want to be a computer, you know. I wanted to be a LUMBERJACK.
XYou're revealing the evidence of a mis-spent youth.
XGive me a chance! I'm only a machine.
XI suppose you think it's pretty smart, beating machines at silly games.
XDo you go round beating up old ladies as well?
XSmartass! You may be ahead, but it isn't over yet.
XWell, it isn't QUITE over yet.
XYou seem to know this game pretty well.
XBully! 	Just wait till I get my big brother Kray onto you.
XI think I'm getting a slight headache.
XWhy don't you pick on someone your own size.
XMegarats! You're winning heavily.
End-of-file
echo End of archive
exit 0



-- 
	Chris Miller, Heriot-Watt University, Edinburgh
	...!ukc!{cstvax,kcl-cs}!hwcs!chris   chris@hwcs.uucp