richb@yarra.OZ (Rich Burridge) (01/12/87)
This program is a combined hack of the work of two other people. It is a combination of the front-end graphics program for Othello by Ed Falk of Sun Microsystems (Mountain View) plus the computer strategy of Chris Miller posted a couple of years ago. I have included Larry Wall's PATCHLEVEL define to make patching easier. NOTE that patch 1 has already been applied, (You haven't got this yet Ed). This plays a fairly reasonable game of Othello. I am just about to change jobs, moving to Sun Microsystems here in Australia, who currently haven't got a machine here on the net. My account here on yarra will stay, but I probabily won't be able to read it so readily, therefore please expect a delay in mail turnaround. All suggestions, bugs, comments and flames to me please. Regards Rich. Rich Burridge, ARPA: richb%yarra.oz@seismo.css.gov Sun Microsystems UUCP: seismo!munnari!yarra.oz!richb AUSTRALIA. ACS: richb@yarra.oz -------CUT HERE-------CUT HERE---------------CUT HERE-------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # README # Makefile # black.icon # othello.icon # white.icon # makemove.c # otool.c # remark.c # othello.h # othello.remarks # patch.1 # This archive created: Mon Jan 12 16:57:20 1987 # By: Rich Burridge (Sun Microsystems Australia) export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'README' then echo shar: "will not over-write existing file 'README'" else cat << \SHAR_EOF > 'README' OTHELLO FOR THE SUN. This is the board game Othello for the Sun workstation. It is played on an 8 x 8 board, with pieces which are black on one side and white on the other. A legal move consists of placing a piece of one's own color on the board so as to "sandwich" a row (orthogonal or diagonal) of pieces of the opposite color between the piece just placed and another piece of the same color. All pieces so sandwiched are flipped over to reveal the color of the other side. The object of the game, is to have more pieces than the opponent at the end of the game (ie. when the board is full or neither side has a legal move). If you have no legal move, you simply miss a turn. White goes first. This version is a combination of the Sun graphics front-end from Edward Falk of Sun Microsystems, plus the best move computer strategy and remarks from Chris Miller. I have fixed a couple of bugs and consolidated the whole lot. By my reckoning, it plays a very reasonable game. Regards Rich. Rich Burridge ARPA: richb%yarra.oz@seismo.css.gov Sun Microsystems UUCP: seismo!munnari!yarra.oz!richb AUSTRALIA ACS : richb@yarra.oz SHAR_EOF fi if test -f 'Makefile' then echo shar: "will not over-write existing file 'Makefile'" else cat << \SHAR_EOF > 'Makefile' LIB = /usr/richb/done/games/othello CFLAGS = -g DESTDIR = BIN = /usr/games MODE = u=rwx,g=rx,o= DEFS = -DLIB=\"$(DESTDIR)$(LIB) CFLAGS = -g $(DEFS) LIBS = -lsuntool -lsunwindow -lpixrect OBJS = makemove.o remark.o otool.o SRCS = makemove.c remark.c otool.c otool: $(OBJS) cc $(CFLAGS) -o otool $(OBJS) $(LIBS) install: otool cp otool $(DESTDIR)$(BIN) strip $(DESTDIR)$(BIN)/otool chmod $(MODE) $(DESTDIR)$(BIN)/otool backup: cp makemove.c makemove.c~ cp remark.c remark.c~ cp otool.c otool.c~ cp othello.h othello.h~ lint: lint $(DEFS) $(SRCS) -lsuntool -lsunwindow -lpixrect makemove.o: makemove.c othello.h remark.o: remark.c othello.h otool.o: otool.c othello.h SHAR_EOF fi if test -f 'black.icon' then echo shar: "will not over-write existing file 'black.icon'" else cat << \SHAR_EOF > 'black.icon' /* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16 */ 0x000F,0xE000,0x0000,0x0000,0x007F,0xFC00,0x0000,0x0000, 0x01FF,0xFF00,0x0000,0x0000,0x03FF,0xFF80,0x0000,0x0000, 0x07FF,0xFFC0,0x0000,0x0000,0x0FFF,0xFFE0,0x0000,0x0000, 0x1FFF,0xFFF0,0x0000,0x0000,0x3FFF,0xFFF8,0x0000,0x0000, 0x3FFF,0xFFF8,0x0000,0x0000,0x7FFF,0xFFFC,0x0000,0x0000, 0x7FFF,0xFFFC,0x0000,0x0000,0x7FFF,0xFFFC,0x0000,0x0000, 0xFFFF,0xFFFE,0x0000,0x0000,0xFFFF,0xFFFE,0x0000,0x0000, 0xFFFF,0xFFFE,0x0000,0x0000,0xFFFF,0xFFFE,0x0000,0x0000, 0xFFFF,0xFFFE,0x0000,0x0000,0xFFFF,0xFFFE,0x0000,0x0000, 0xFFFF,0xFFFE,0x0000,0x0000,0x7FFF,0xFFFC,0x0000,0x0000, 0x7FFF,0xFFFC,0x0000,0x0000,0x7FFF,0xFFFC,0x0000,0x0000, 0x3FFF,0xFFF8,0x0000,0x0000,0x3FFF,0xFFF8,0x0000,0x0000, 0x1FFF,0xFFF0,0x0000,0x0000,0x0FFF,0xFFE0,0x0000,0x0000, 0x07FF,0xFFC0,0x0000,0x0000,0x03FF,0xFF80,0x0000,0x0000, 0x01FF,0xFF00,0x0000,0x0000,0x007F,0xFC00,0x0000,0x0000, 0x000F,0xE000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 SHAR_EOF fi if test -f 'othello.icon' then echo shar: "will not over-write existing file 'othello.icon'" else cat << \SHAR_EOF > 'othello.icon' /* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16 */ 0xFFFF,0xFFFF,0xFFFF,0xFFFF,0x8001,0x0001,0x0001,0x0001, 0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001, 0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001, 0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001, 0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001, 0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001, 0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001, 0x8001,0x0001,0x0001,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF, 0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001, 0x87C1,0x07C1,0x07C1,0x07C1,0x8821,0x0821,0x0FE1,0x0821, 0x9011,0x1011,0x1FF1,0x1011,0xA009,0x2009,0x3FF9,0x2009, 0xA009,0x2009,0x3FF9,0x2009,0xA009,0x2009,0x3FF9,0x2009, 0xA009,0x2009,0x3FF9,0x2009,0xA009,0x2009,0x3FF9,0x2009, 0x9011,0x1011,0x1FF1,0x1011,0x8821,0x0821,0x0FE1,0x0821, 0x87C1,0x07C1,0x07C1,0x07C1,0x8001,0x0001,0x0001,0x0001, 0x8001,0x0001,0x0001,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF, 0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001, 0x8001,0x07C1,0x07C1,0x0001,0x8001,0x0FE1,0x0821,0x0001, 0x8001,0x1FF1,0x1011,0x0001,0x8001,0x3FF9,0x2009,0x0001, 0x8001,0x3FF9,0x2009,0x0001,0x8001,0x3FF9,0x2009,0x0001, 0x8001,0x3FF9,0x2009,0x0001,0x8001,0x3FF9,0x2009,0x0001, 0x8001,0x1FF1,0x1011,0x0001,0x8001,0x0FE1,0x0821,0x0001, 0x8001,0x07C1,0x07C1,0x0001,0x8001,0x0001,0x0001,0x0001, 0x8001,0x0001,0x0001,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF, 0x8001,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001, 0x87C1,0x0001,0x0001,0x0001,0x8821,0x0001,0x0001,0x0001, 0x9011,0x0001,0x0001,0x0001,0xA009,0x0001,0x0001,0x0001, 0xA009,0x0001,0x0001,0x0001,0xA009,0x0001,0x0001,0x0001, 0xA009,0x0001,0x0001,0x0001,0xA009,0x0001,0x0001,0x0001, 0x9011,0x0001,0x0001,0x0001,0x8821,0x0001,0x0001,0x0001, 0x87C1,0x0001,0x0001,0x0001,0x8001,0x0001,0x0001,0x0001, 0x8001,0x0001,0x0001,0x0001,0xFFFF,0xFFFF,0xFFFF,0xFFFF SHAR_EOF fi if test -f 'white.icon' then echo shar: "will not over-write existing file 'white.icon'" else cat << \SHAR_EOF > 'white.icon' /* Format_version=1, Width=64, Height=64, Depth=1, Valid_bits_per_item=16 */ 0x000F,0xE000,0x0000,0x0000,0x0070,0x1C00,0x0000,0x0000, 0x0180,0x0300,0x0000,0x0000,0x0200,0x0080,0x0000,0x0000, 0x0400,0x0040,0x0000,0x0000,0x0800,0x0020,0x0000,0x0000, 0x1000,0x0010,0x0000,0x0000,0x2000,0x0008,0x0000,0x0000, 0x2000,0x0008,0x0000,0x0000,0x4000,0x0004,0x0000,0x0000, 0x4000,0x0004,0x0000,0x0000,0x4000,0x0004,0x0000,0x0000, 0x8000,0x0002,0x0000,0x0000,0x8000,0x0002,0x0000,0x0000, 0x8000,0x0002,0x0000,0x0000,0x8000,0x0002,0x0000,0x0000, 0x8000,0x0002,0x0000,0x0000,0x8000,0x0002,0x0000,0x0000, 0x8000,0x0002,0x0000,0x0000,0x4000,0x0004,0x0000,0x0000, 0x4000,0x0004,0x0000,0x0000,0x4000,0x0004,0x0000,0x0000, 0x2000,0x0008,0x0000,0x0000,0x2000,0x0008,0x0000,0x0000, 0x1000,0x0010,0x0000,0x0000,0x0800,0x0020,0x0000,0x0000, 0x0400,0x0040,0x0000,0x0000,0x0200,0x0080,0x0000,0x0000, 0x0180,0x0300,0x0000,0x0000,0x0070,0x1C00,0x0000,0x0000, 0x000F,0xE000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, 0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000 SHAR_EOF fi if test -f 'makemove.c' then echo shar: "will not over-write existing file 'makemove.c'" else cat << \SHAR_EOF > 'makemove.c' /* makemove.c * * Best computer strategy routines. * * Copyright: Chris Miller, 1979, 1981, 1984. * * Included with this othello game by * Rich Burridge, Sun Microsystems, Australia - December 1986. */ #include "othello.h" /* Globals for passing arguments to "sandwich"; * this is to save time putting arguments on and off the * stack in a very heavily used piece of code */ int s_move,s_row,s_col,s_player,s_opponent,s_flip ; BOARD *s_pos ; extern int aspire ; /* Computers level of aspiration. */ extern int cretin_flag ; extern int remark ; /* Indicates if remarks to be displayed. */ extern Panel_item remark_mes ; int expected,margin ; long ab_count,se_count ; /* Calls of alfabeta and static_eval. */ makemove(board,player,depth) BOARD board ; int depth,player ; { int d,value ; int mv = NOMOVE ; if (!depth) { if ((mv = nextmove(&board,NOMOVE,(BOARD *) 0,player)) == NOMOVE) return(-1) ; else return(mv) ; } if ((mv = nextmove(&board,NOMOVE,(BOARD *) 0,player)) == NOMOVE) { if (remark) remark_msg("I'm not dead, just resting") ; return(-1) ; } ab_count = se_count = 0 ; if (nextmove(&board,mv,(BOARD *) 0,player) == NOMOVE) { value = static_eval(&board) ; if (remark) make_remark(player,value) ; return(mv) ; } /* Exhaustive for last 3*depth ply (at most 10!). */ if ((board.MOVES_LEFT <= 10) && (board.MOVES_LEFT <= 3*depth)) d = board.MOVES_LEFT ; else d = depth ; set_aspiration(&board) ; value = alfabeta(&board,d,player*INFINITY,player,&mv) ; if (remark) make_remark(player,value) ; return(mv) ; } nextmove(pos,mv,nextpos,player) BOARD *pos,*nextpos ; int mv,player ; { /* On first entry for a given position, move is set to NOMOVE, * i.e. -1. Moves are generated in a rough plausibility order * given by the list structure move_table, whose head is * move_table[-1], and whose tail-pointer is set to NOMOVE. * The order is purely heuristic, and corresponds roughly to * the values associated by static_eval with different squares. */ static int m_table[65] = { 0, 7, 6, 5, 4, 24, 16, 8, 56, 15, 14, 13, 12, 25, 17, 49, 48, 23, 22, 21, 20, 26, 42, 41, 40, 31, 30, 29, 28, 35, 34, 33, 32, 39, 38, 37, 36, 10, 43, 51, 59, 47, 46, 45, 44, 27, 19, 50, 58, 55, 54, 53, 52, 1, 11, -1, 57, 63, 62, 61, 60, 18, 3, 9, 2 } ; static int *move_table = &(m_table[1]) ; while ((mv = move_table[mv]) != NOMOVE) if (legal(mv,player,pos)) { if (nextpos) domove(pos,mv,nextpos,player) ; /* Next position generated only if pointer is non-zero */ return(mv) ; } return(NOMOVE) ; /* No more legal moves */ } domove(pos,mv,nextpos,player) BOARD *pos,*nextpos ; int mv,player; { register int i ; if (pos != nextpos) FOR_BOARD(i) nextpos->SQUARE[i] = pos->SQUARE[i] ; s_move = mv ; s_row = mv >> 3 ; s_col = mv & 7 ; s_player = player ; s_opponent = -player ; s_flip = TRUE ; s_pos = nextpos ; nextpos->SQUARE[s_move] = player ; (void) sandwich(-9) ; (void) sandwich(-8) ; (void) sandwich(-7) ; (void) sandwich(-1) ; (void) sandwich(1) ; (void) sandwich(7) ; (void) sandwich(8) ; (void) sandwich(9) ; nextpos->MOVES_LEFT = (pos->MOVES_LEFT) - 1 ; } legal(mv,player,pos) BOARD *pos ; int mv,player ; { if (pos->SQUARE[mv]) return(FALSE) ; /* Already occupied */ s_move = mv ; s_row = mv >> 3 ; s_col = mv & 7 ; s_player = player ; s_opponent = -player ; s_flip = FALSE ; s_pos = pos ; return(sandwich(-9) || sandwich(-8) || sandwich(-7) || sandwich(-1) || sandwich(1) || sandwich(7) || sandwich(8) || sandwich(9)) ; } sandwich(increment) register int increment ; /* Test whether the square move sandwiches a line * of enemy pieces in the direction [row_inc, col_inc]; * If (s_flip) then update position by capturing such pieces */ { register int square,offset ; int row_offset,col_offset,piece,piece_count ; if (s_pos->SQUARE[s_move+increment] != s_opponent) return(FALSE) ; /* Quick test to catch most failures - * note that the tested square may not even * be on the board, but the condition is a * sufficient one for failure. */ row_offset = (increment < -1 ? s_row : /* inc -1: -9, -8, -7 */ increment > 1 ? 7-s_row: /* inc 1: 7, 8, 9 */ 8) ; col_offset = (increment & 4 ? s_col : /* inc -1: -9, -1, 7 */ increment & 1 ? 7-s_col : /* inc 1: -7, 1, 9 */ 8) ; offset = (row_offset > col_offset ? col_offset : row_offset) ; /* offset = shortest distance to an edge in the direction of search */ if (2 > offset) return(FALSE) ; piece_count = 1 ; square = s_move+increment ; while (--offset) { if (!(piece = s_pos->SQUARE[square += increment])) return(FALSE) ; /* If empty square, give up */ if (piece == s_player) break ; else piece_count++ ; /* Count opponent's pieces encountered */ } if (!offset) return(FALSE) ; if (s_flip) while (piece_count--) s_pos->SQUARE[square -= increment] = s_player ; return (TRUE) ; } alfabeta(pos,depth,parent_opt,sign,parent_best) BOARD *pos ; int depth,parent_opt,sign,*parent_best ; /* Alpha-beta pruning algorithm. * * If sign = 1, then try to MAXIMISE score, else MINIMISE. * Parent node wants to MINIMISE/MAXIMISE, so as soon as * we exceed parent_opt, give up and return INFINITY. * Return best move in parent_best. * * Externals used: * static_eval(position) * ISMOVE(position,player) [does player have a legal move?] * game_ended(pos) * typedef BOARD * aspiration(player,depth,position) * [return the aspiration limit for next level of search] * nextmove(position,mv,nextposition,player) * [give the next legal move for player in position; * put the resulting position in nextposition] */ { BOARD nextpos ; int value,this_opt,this_best = NOMOVE,mv = NOMOVE,asp = 0 ; if ((depth==0) || game_ended(pos)) { value = static_eval(pos) ; *parent_best = NOMOVE ; if ((sign*value) > (sign*parent_opt)) return(sign*INFINITY) ; else return (value) ; } ab_count++ ; /* Record boards dynamically evaluated */ this_opt = (sign == 1 ? -INFINITY : INFINITY) ; if (!ISMOVE(pos,sign)) /* No legal move */ { value = alfabeta(pos,depth,this_opt,-sign,&this_best) ; goto valfound ; } asp = sign * aspiration(sign,depth) ; while ((mv = nextmove(pos,mv,&nextpos,sign)) != NOMOVE) { value = alfabeta(&nextpos,depth-1,this_opt,-sign,&this_best) ; valfound: if ((sign*value) >= (sign*parent_opt)) return(sign*INFINITY) ; if ((sign*value) > asp) { *parent_best = mv ; return(value) ; } if ((sign*value) > (sign*this_opt)) { this_opt = value ; *parent_best = mv ; } /* If two moves have same evaluation, choose * uniformly randomly between them (of course, * where several moves have same value, this * is biased towards the later ones */ if ((value == this_opt) && (rand() & 020)) *parent_best = mv ; } return(this_opt) ; } set_aspiration(position) BOARD *position ; /* Aspirations are as follows: * Aspiration-level Aspiration * 1 The worse of 0, static value * 2 The static value * 3 10% better than the static value * 4 25% better than the static value * 5 Any winning move * 6 The move winning by the greatest margin * * It is assumed that the opponent has * the same level of aspiration as the program. */ { if (aspire == MAXASPIRE) { expected = 0 ; margin = INFINITY ; return ; } if (aspire == (MAXASPIRE-1)) { expected = 0 ; margin = INFINITY-64 ; return ; } expected = static_eval(position) ; switch (aspire) { case 1 : expected /= 2 ; margin = -abs(expected) ; return ; case 2 : margin = 0 ; return ; case 3 : margin = abs(expected/10) ; return ; case 4 : margin = abs(expected/4) ; return ; } } aspiration(player,depth) int player,depth ; { if (aspire == MAXASPIRE) return(player*INFINITY) ; if (depth < 3 && aspire > 1) return(player*(INFINITY-64)) ; return(expected+player*margin) ; } static_eval(pos) BOARD *pos ; /* Square values (only valid when a "vulnerable" piece occupies the * square in question) */ #define VCORN 100 /* Corner */ #define VORTH -30 /* Orthogonally next to corner */ #define VDIAG -50 /* Diagonally " " " */ #define VEDGE 20 /* On edge */ #define VNEXT -7 /* Next to edge */ #define VNORM 1 /* Elsewhere */ { static int model[64] = { VCORN, VORTH, VEDGE, VEDGE, VEDGE, VEDGE, VORTH, VCORN, VORTH, VDIAG, VNEXT, VNEXT, VNEXT, VNEXT, VDIAG, VORTH, VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE, VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE, VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE, VEDGE, VNEXT, VNORM, VNORM, VNORM, VNORM, VNEXT, VEDGE, VORTH, VDIAG, VNEXT, VNEXT, VNEXT, VNEXT, VDIAG, VORTH, VCORN, VORTH, VEDGE, VEDGE, VEDGE, VEDGE, VORTH, VCORN } ; register int i ; register int value = 0 ; int scores[64] ; se_count++ ; /* Record number of static evaluations */ if (game_ended(pos)) { FOR_BOARD(i) value += pos->SQUARE[i] ; return (value > 0 ? INFINITY+value-64 : value < 0 ? -INFINITY+value+64 : 0) ; } FOR_BOARD(i) scores[i] = 0 ; find_invulnerable(pos,scores) ; /* Scores now contains VINVUL (or -VINVUL) * for each invulnerable piece, and 0 elsewhere; * now fill in other evaluations (special cases: * next to corner [bad!], on edge[good!], next to * edge[poor], anywhere else[boring]) */ FOR_BOARD(i) value += (scores[i] ? scores[i] : model[i]*pos->SQUARE[i]) ; return (value) ; } game_ended(pos) BOARD *pos ; { if (!(pos->MOVES_LEFT)) return(TRUE) ; return((!ISMOVE(pos,WHITE)) && !(ISMOVE(pos,BLACK))) ; } find_invulnerable(board,scores) BOARD *board ; int scores[64] ; /* This function finds invulnerable pieces, and scores them * appropriately; it does not find ALL invulnerable pieces - * in fact, only concave blocks including a corner - but * nevertheless should provide a good approximation. */ { int hwm,corner,value,i,j ; if ((corner = board->SQUARE[0]) != 0) { value = corner*VINVUL ; hwm = 7 ; for (i = 0; i < 56; i += 8) { if (board->SQUARE[i] != corner) break ; scores[i] = value ; for (j = 1; j < hwm; j++) { if (board->SQUARE[i+j] != corner) { hwm = j ; break ; } scores[i+j] = value ; } } scores[0] = corner*VCORN ; } if ((corner = board->SQUARE[7]) != 0) { value = corner*VINVUL ; hwm = 0 ; for (i = 0; i < 56; i+= 8) { if (board->SQUARE[i+7] != corner) break ; scores[i+7] = value ; for (j = 6; j > hwm; j--) { if (board->SQUARE[i+j] != corner) { hwm = j ; break ; } scores[i+j] = value ; } } scores[7] = corner*VCORN; } if ((corner = board->SQUARE[56]) != 0) { value = corner*VINVUL ; hwm = 7 ; for (i = 56; i > 0; i -= 8) { if (board->SQUARE[i] != corner) break ; scores[i] = value ; for (j = 1; j < hwm; j++) { if (board->SQUARE[i+j] != corner) { hwm = j ; break ; } scores[i+j] = value ; } } scores[56] = corner*VCORN ; } if ((corner=board->SQUARE[63]) != 0) { value = corner*VINVUL ; hwm = 0 ; for (i = 56; i > 0; i -= 8) { if (board->SQUARE[i+7] != corner) break ; scores[i+7] = value ; for (j = 6; j > hwm; j--) { if (board->SQUARE[i+j] != corner) { hwm = j ; break ; } scores[i+j] = value ; } } scores[63] = corner*VCORN ; } } SHAR_EOF fi if test -f 'otool.c' then echo shar: "will not over-write existing file 'otool.c'" else cat << \SHAR_EOF > 'otool.c' /* othello tool @(#)otool.c 1.3 12/10/86 */ /* * @@@ @@@@@ @ @ @@@@@ @ @ @@@ * @ @ @ @ @ @ @ @ @ @ * @ @ @ @@@@@ @@@ @ @ @ @ * @ @ @ @ @ @ @ @ @ @ * @@@ @ @ @ @@@@@ @@@@@ @@@@@ @@@ * * OTHELLO - graphics front end to othello game * * Edward A. Falk * Sun Microsystems * * Various bugfixes, changes and additions by * Rich Burridge, Sun Microsystems, Australia * * December, 1986 */ #include "othello.h" #define PATCHLEVEL 1 /* Compilation parameters: */ #define CELL_WIDTH 40 #define PIECE_MARGIN 5 #define PIECE_RAD (CELL_WIDTH/2 - PIECE_MARGIN) #ifndef SUN2 #define MOVE_DELTA 1 /* For animation */ #else #define MOVE_DELTA 3 #endif #define TOTAL_WIDTH BOARDSIZE*CELL_WIDTH /* Constants, typedefs, externals, globals, statics, macros. */ static short icon_image[] = { /* Main icon. */ #include "othello.icon" } ; DEFINE_ICON_FROM_IMAGE(othello_icon,icon_image) ; static short hglass_image[] = { #include <images/hglass.cursor> } ; mpr_static(hglass_pr,16,16,1,hglass_image) ; static short white_image[] = { #include "white.icon" } ; mpr_static(white_icon_pr,64,64,1,white_image) ; static short black_image[] = { #include "black.icon" } ; mpr_static(black_icon_pr,64,64,1,black_image) ; char w_who[3][9] = { "You", "Computer", "White" } ; char w_be[3][4] = { "are", "is", "is" } ; char b_who[3][9] = { "Computer", "You", "Black" } ; char b_be[3][4] = { "is", "are", "is" } ; static Frame base_frame ; /* Assorted globals. */ static Panel panel ; static Canvas canvas ; static Panel_item last_but, new_game_but, quit_but, suggest_but ; static Panel_item panel_mes, score_mes ; static Panel_item computer_plays_choice, remark_choice ; static Panel_item aspiration_choice, difficulty_choice ; static Cursor canvas_cursor,hglass_cursor ; static Pixwin *canvas_pw ; static enum cmode {white_start, white_moving, black_start, black_moving, game_over } canvas_mode ; static enum pmode {play_black, play_white, play_both} play_mode ; static int piece_x,piece_y ; /* Current position of moving piece */ /* Variables used externally. */ int aspire ; /* Computers level of aspiration. */ int cretin_flag ; int last_remark ; int remark ; /* Indicates if remarks are to be displayed. */ Panel_item remark_mes ; static int last_move ; /* Last valid computer move. */ static int move ; /* Current move being evaluated. */ static int next_player ; /* Next player (BLACK or WHITE) to move. */ static int suggestion ; /* Positive if a suggested move. */ static int suggest_x,suggest_y ; /* Position of suggested move.*/ static int depth ; /* Depth of search for computers move. */ BOARD old_board ; /* The previous Othello board. */ BOARD board ; /* The Othello board. */ static char line[40] ; static void canvas_proc() ; /* Proc definitions. */ static void last_proc(), new_game_proc(), quit_proc(), suggest_proc() ; static void computer_plays_proc(), remark_proc() ; static void aspiration_proc(), difficulty_proc() ; static void init_panel() ; static void canvas_init() ; main(argc,argv) int argc ; char *argv[] ; { (void) sprintf(line," Othello. V1.%1d",PATCHLEVEL) ; base_frame = window_create(NULL, FRAME, FRAME_ICON, &othello_icon, FRAME_LABEL, line, FRAME_SUBWINDOWS_ADJUSTABLE, FALSE, WIN_ERROR_MSG, "Can't create window.", FRAME_ARGS, argc,argv, 0) ; panel = window_create(base_frame, PANEL, WIN_WIDTH, TOTAL_WIDTH+1, PANEL_LAYOUT, PANEL_HORIZONTAL, 0) ; init_panel() ; window_fit_height(panel) ; canvas = window_create(base_frame, CANVAS, WIN_X, 0, WIN_BELOW, panel, WIN_CONSUME_KBD_EVENT, WIN_NO_EVENTS, WIN_EVENT_PROC, canvas_proc, WIN_HEIGHT, TOTAL_WIDTH+1, WIN_WIDTH, TOTAL_WIDTH+1, WIN_IGNORE_PICK_EVENTS, KBD_REQUEST, WIN_ASCII_EVENTS, 0, CANVAS_FAST_MONO, TRUE, 0) ; window_fit(base_frame) ; canvas_pw = (Pixwin *) window_get(canvas,CANVAS_PIXWIN) ; canvas_cursor = window_get(canvas,WIN_CURSOR) ; hglass_cursor = cursor_create(CURSOR_IMAGE,&hglass_pr,0) ; remark = TRUE ; last_move = -1 ; play_mode = play_black ; canvas_mode = white_start ; initboard() ; get_remarks() ; aspire = INIT_ASPIRE ; depth = INIT_DEPTH ; cretin_flag = FALSE ; last_remark = FIRST_REMARK ; /* Force a remark the first time. */ canvas_init() ; window_main_loop(base_frame) ; exit(0) ; } initboard() /* Initialise the othello board. */ { int i ; FOR_BOARD(i) old_board.SQUARE[i] = board.SQUARE[i] = FREE ; board.SQUARE[27] = BLACK ; board.SQUARE[28] = WHITE ; board.SQUARE[35] = WHITE ; board.SQUARE[36] = BLACK ; board.MOVES_LEFT = 60 ; } static void init_panel() { last_but = panel_create_item(panel,PANEL_BUTTON, PANEL_NOTIFY_PROC,last_proc, PANEL_LABEL_IMAGE, panel_button_image(panel,"last",4,(struct pixfont *) 0), 0) ; new_game_but = panel_create_item(panel,PANEL_BUTTON, PANEL_NOTIFY_PROC,new_game_proc, PANEL_LABEL_IMAGE, panel_button_image(panel,"new game",8,(struct pixfont *) 0), 0) ; quit_but = panel_create_item(panel,PANEL_BUTTON, PANEL_NOTIFY_PROC,quit_proc, PANEL_LABEL_IMAGE, panel_button_image(panel,"quit",4,(struct pixfont *) 0), 0) ; suggest_but = panel_create_item(panel,PANEL_BUTTON, PANEL_NOTIFY_PROC,suggest_proc, PANEL_LABEL_IMAGE, panel_button_image(panel,"suggest",7,(struct pixfont *) 0), 0) ; aspiration_choice = panel_create_item(panel,PANEL_CYCLE, PANEL_NOTIFY_PROC,aspiration_proc, PANEL_LABEL_BOLD,TRUE, PANEL_LABEL_STRING,"Aspiration: ", PANEL_VALUE,2, PANEL_SHOW_MENU,TRUE, PANEL_CHOICE_STRINGS,"1","2","3","4","5","6",0, PANEL_DISPLAY_LEVEL,PANEL_CURRENT, 0) ; difficulty_choice = panel_create_item(panel,PANEL_CYCLE, PANEL_NOTIFY_PROC,difficulty_proc, PANEL_LABEL_BOLD,TRUE, PANEL_LABEL_STRING,"Difficulty: ", PANEL_VALUE,0, PANEL_SHOW_MENU,TRUE, PANEL_CHOICE_STRINGS,"1","2","3","4",0, PANEL_DISPLAY_LEVEL,PANEL_CURRENT, 0) ; computer_plays_choice = panel_create_item(panel,PANEL_CYCLE, PANEL_NOTIFY_PROC,computer_plays_proc, PANEL_LABEL_BOLD,TRUE, PANEL_LABEL_STRING,"Computer plays: ", PANEL_VALUE,0, PANEL_SHOW_MENU,TRUE, PANEL_CHOICE_STRINGS,"black","white","both",0, PANEL_DISPLAY_LEVEL,PANEL_CURRENT, 0) ; remark_choice = panel_create_item(panel,PANEL_CYCLE, PANEL_NOTIFY_PROC,remark_proc, PANEL_LABEL_BOLD,TRUE, PANEL_LABEL_STRING,"Remarks: ", PANEL_VALUE,1, PANEL_SHOW_MENU,TRUE, PANEL_CHOICE_STRINGS,"off","on",0, PANEL_DISPLAY_LEVEL,PANEL_CURRENT, 0) ; remark_mes = panel_create_item(panel,PANEL_MESSAGE, PANEL_LABEL_STRING," ", 0) ; panel_mes = panel_create_item(panel,PANEL_MESSAGE, PANEL_LABEL_STRING,"To move, use your left mouse button", 0) ; score_mes = panel_create_item(panel,PANEL_MESSAGE, PANEL_LABEL_STRING,"White: 2, Black: 2", 0) ; } /*ARGSUSED*/ static void quit_proc(item,event) Panel_item item ; Event *event ; { window_done(base_frame) ; } static void suggest_proc() { if (canvas_mode == white_start) suggestion = makemove(board,WHITE,depth) ; else suggestion = makemove(board,BLACK,depth) ; suggest_x = ((suggestion & 7) + 1) * CELL_WIDTH - CELL_WIDTH / 2 ; suggest_y = ((suggestion >> 3) + 1) * CELL_WIDTH - CELL_WIDTH / 2 ; pw_vector(canvas_pw,suggest_x-5,suggest_y-5, suggest_x+5,suggest_y+5,RSRC,0xff) ; pw_vector(canvas_pw,suggest_x-5,suggest_y+5, suggest_x+5,suggest_y-5,RSRC,0xff) ; } static void draw_piece(player,x,y,rop) int player,x,y,rop ; { switch (player) { case WHITE : pw_write(canvas_pw,x,y,32,32,rop,&white_icon_pr,0,0) ; break ; case BLACK : pw_write(canvas_pw,x,y,32,32,rop,&black_icon_pr,0,0) ; } } static void last_proc() { int flips,i,player,rop,x,y ; player = (canvas_mode == white_start) ? BLACK : WHITE ; for (flips = 0; flips < 4; flips++) { x = (last_move & 7)*CELL_WIDTH + PIECE_MARGIN ; y = (last_move >> 3)*CELL_WIDTH + PIECE_MARGIN ; rop = flips % 2 ? RSRC : RCLR ; draw_piece(player,x,y,rop) ; PAUSE(i) ; } } /*ARGSUSED*/ static void difficulty_proc(item,value,event) Panel_item item ; int value ; Event *event ; { depth = value + INIT_DEPTH ; } /*ARGSUSED*/ static void remark_proc(item,value,event) Panel_item item ; int value ; Event *event ; { remark = value ; if (remark) remark_msg("OK, I'll make remarks") ; else remark_msg("OK, I'll shut up") ; } /*ARGSUSED*/ static void aspiration_proc(item,value,event) Panel_item item ; int value ; Event *event ; { aspire = value ; } count(board,player) /* Count the number of player pieces on the board. */ int player ; BOARD board ; { int i,n ; n = 0 ; FOR_BOARD(i) if (board.SQUARE[i] == player) n++ ; return(n) ; } static void update_board_image() { int flips,i,j,rop,x,y ; if (suggestion != -1) { pw_vector(canvas_pw,suggest_x-5,suggest_y-5, suggest_x+5,suggest_y+5,RCLR,0xff) ; pw_vector(canvas_pw,suggest_x-5,suggest_y+5, suggest_x+5,suggest_y-5,RCLR,0xff) ; suggestion = -1 ; } for (flips = 0; flips < 4; flips++) { pw_batch_on(canvas_pw) ; rop = flips % 2 ? RSRC : RCLR ; FOR_BOARD(i) { if (board.SQUARE[i] != old_board.SQUARE[i]) { x = (i & 7) * CELL_WIDTH + PIECE_MARGIN ; y = (i >> 3) * CELL_WIDTH + PIECE_MARGIN ; draw_piece(board.SQUARE[i],x,y,rop) ; } } pw_batch_off(canvas_pw) ; PAUSE(j) ; } score_msg(sprintf(line,"White: %2d, Black: %2d", count(board,WHITE),count(board,BLACK))) ; } static void canvas_init() { int x0 ; pw_writebackground(canvas_pw,0,0,TOTAL_WIDTH+1,TOTAL_WIDTH+1,RSRC) ; pw_batch_on(canvas_pw) ; for (x0 = 0; x0 <= TOTAL_WIDTH; x0 += CELL_WIDTH) { pw_vector(canvas_pw,x0,0,x0,TOTAL_WIDTH,RSRC,0xff) ; pw_vector(canvas_pw,0,x0,TOTAL_WIDTH,x0,RSRC,0xff) ; } pw_batch_off(canvas_pw) ; update_board_image() ; } static void animate_move(move) int move ; { int x0,y0,x1,y1,x,y,dx,dy,ctr ; static struct rect r = {0,0,TOTAL_WIDTH+1,TOTAL_WIDTH+1} ; pw_lock(canvas_pw,&r) ; dx = x1 = (move & 7) * CELL_WIDTH + PIECE_MARGIN ; dy = y1 = (move >> 3) * CELL_WIDTH + PIECE_MARGIN ; if (x1 > y1) { ctr = dx/2 ; x = 0 ; y = 0 ; draw_piece(WHITE,x,y,RINV) ; while (x < x1) { x0 = x ; y0 = y ; x += MOVE_DELTA ; if ((ctr -= dy) < 0) { ctr += dx ; y += MOVE_DELTA ; } draw_piece(WHITE,x,y,RINV) ; draw_piece(WHITE,x0,y0,RINV) ; } draw_piece(WHITE,x,y,RINV) ; } else { ctr = dy/2 ; x = 0 ; y = 0 ; draw_piece(WHITE,x,y,RINV) ; while (y < y1) { x0 = x ; y0 = y ; y += MOVE_DELTA ; if ((ctr -= dx) < 0) { ctr += dy ; x += MOVE_DELTA ; } draw_piece(WHITE,x,y,RINV) ; draw_piece(WHITE,x0,y0,RINV) ; } draw_piece(WHITE,x,y,RINV) ; } pw_unlock(canvas_pw) ; } static void who_wins() { int cs,ps ; ps = count(board,WHITE) ; cs = count(board,BLACK) ; if (ps > cs) score_msg(sprintf(line,"White wins %d-%d",ps,cs)) ; else if (ps == cs) score_msg(sprintf(line,"A tie %d-%d",ps,cs)) ; else score_msg(sprintf(line,"Black wins %d-%d",cs,ps)) ; if (cretin_flag) remark_msg("*** CRETIN! ***") ; else remark_msg("") ; } do_move(player,who) int player ; char who[3][9] ; { int taken ; /* Number of pieces flipped this go. */ taken = formfliplist(move,player) ; update_board_image() ; if (taken == 1) panel_msg(sprintf(line,"%s took 1 piece",who[(int) play_mode])) ; else panel_msg(sprintf(line,"%s took %d pieces",who[(int) play_mode],taken)) ; } formfliplist(move,player) int move,player ; { int cnt,i,old_cnt ; old_cnt = count(board,player) ; FOR_BOARD(i) old_board.SQUARE[i] = board.SQUARE[i] ; old_board.MOVES_LEFT = board.MOVES_LEFT ; domove(&old_board,move,&board,player) ; cnt = count(board,player) ; return(cnt - old_cnt - 1) ; } check(player,who,be) int player ; char who[3][9],be[3][4] ; /* This routine checks to see if a move can be made for this player. * If not, various checks are made to see if the game is finished. * Return value indicates if a move can be made. */ { if ((!count(board,BLACK)) || (!count(board,WHITE)) || ((count(board,BLACK) + count(board,WHITE)) == 64)) { who_wins() ; canvas_mode = game_over ; panel_msg("Game over") ; return(FALSE) ; } if ((move = makemove(board,player,0)) == NOMOVE) { panel_msg(sprintf(line,"%s %s forced to pass", who[(int) play_mode],be[(int) play_mode])) ; if ((move = makemove(board,OPPONENT(player),0)) == NOMOVE) { who_wins() ; canvas_mode = game_over ; panel_msg("Game over") ; } return(FALSE) ; } return(TRUE) ; } think(player,who) int player ; char who[3][9] ; { window_set(canvas,WIN_CURSOR,hglass_cursor,0) ; move = makemove(board,player,depth) ; window_set(canvas,WIN_CURSOR,canvas_cursor,0) ; animate_move(move) ; do_move(player,who) ; last_move = move ; canvas_mode = (enum cmode) (OPPONENT(player) + 1) ; } move_and_check(player,who,be,opp_who,opp_be) int player ; char who[3][9],be[3][4],opp_who[3][9],opp_be[3][4] ; { do_move(player,who) ; for (;;) if (check(OPPONENT(player),opp_who,opp_be) == TRUE) { think(OPPONENT(player),opp_who) ; if (check(player,who,be) == TRUE) break ; if (canvas_mode == game_over) break ; } else { canvas_mode = (enum cmode) ((int) canvas_mode - 1) ; return ; } } do_action(event) Event *event ; { int cx,cy ; switch (event_id(event)) { case LOC_MOVE : case LOC_DRAG : case LOC_TRAJECTORY : draw_piece(next_player,piece_x,piece_y,RINV) ; piece_x = event_x(event) - PIECE_RAD ; piece_y = event_y(event) - PIECE_RAD ; draw_piece(next_player,piece_x,piece_y,RINV) ; break ; case LOC_WINENTER : case LOC_WINEXIT : case LOC_RGNENTER : case LOC_RGNEXIT : case WIN_STOP : draw_piece(next_player,piece_x,piece_y,RINV) ; canvas_mode = (enum cmode) ((int) canvas_mode - 1) ; break ; case MS_LEFT : cursor_set(canvas_cursor,CURSOR_SHOW_CURSOR,TRUE,0) ; window_set(canvas,WIN_CURSOR,canvas_cursor,0) ; if (event_is_down(event)) { draw_piece(next_player,piece_x,piece_y,RINV) ; canvas_mode = (enum cmode) ((int) canvas_mode - 1) ; } else { draw_piece(next_player,piece_x,piece_y,RINV) ; cx = (piece_x + PIECE_RAD) / CELL_WIDTH ; cy = (piece_y + PIECE_RAD) / CELL_WIDTH ; move = cy * BOARDSIZE + cx ; if (legal(move,next_player,&board) == FALSE) { panel_msg("Invalid move") ; canvas_mode = (enum cmode) ((int) canvas_mode - 1) ; } else { if (canvas_mode == white_moving) move_and_check(WHITE,w_who,w_be,b_who,b_be) ; else move_and_check(BLACK,b_who,b_be,w_who,w_be) ; } } break ; } } /*ARGSUSED*/ static void canvas_proc(win,event,arg) Canvas win ; Event *event ; caddr_t arg ; { switch (canvas_mode) { case white_start : case black_start : next_player = (int) canvas_mode - 1 ; if (event_id(event) == MS_LEFT && event_is_down(event)) { cursor_set(canvas_cursor,CURSOR_SHOW_CURSOR,FALSE,0) ; window_set(canvas,WIN_CURSOR,canvas_cursor,0) ; piece_x = event_x(event) - PIECE_RAD ; piece_y = event_y(event) - PIECE_RAD ; draw_piece(next_player,piece_x,piece_y,RINV) ; canvas_mode = (enum cmode) ((int) canvas_mode + 1) ; } break ; case white_moving : case black_moving : do_action(event) ; break ; case game_over : panel_msg("Game over") ; break ; } } /*ARGSUSED*/ static void computer_plays_proc(item,value,event) Panel_item item ; int value ; Event *event ; { int old_remark ; switch (value) { case 0 : play_mode = play_black ; if (canvas_mode != game_over) if (check(BLACK,b_who,b_be) == TRUE) think(BLACK,b_who) ; break ; case 1 : play_mode = play_white ; if (canvas_mode != game_over) if (check(WHITE,w_who,w_be) == TRUE) think(WHITE,w_who) ; break ; case 2 : if (play_mode != play_both) { play_mode = play_both ; old_remark = remark ; remark = 0 ; while (play_mode == play_both && canvas_mode != game_over) { if (canvas_mode == white_start) { for (;;) if (check(WHITE,w_who,w_be) == TRUE) { think(WHITE,w_who) ; canvas_mode = black_start ; if (check(BLACK,b_who,b_be) == TRUE) break ; if (canvas_mode == game_over) break ; canvas_mode = white_start ; } } else { for (;;) if (check(BLACK,b_who,b_be) == TRUE) { think(BLACK,b_who) ; canvas_mode = white_start ; if (check(WHITE,w_who,w_be) == TRUE) break ; if (canvas_mode == game_over) break ; canvas_mode = black_start ; } } notify_dispatch() ; } } remark = old_remark ; break ; } } static void new_game_proc() { initboard() ; play_mode = play_black ; panel_set_value(computer_plays_choice,0) ; canvas_mode = white_start ; last_move = -1 ; canvas_init() ; remark_msg("") ; panel_msg("To move, use your left mouse button") ; } SHAR_EOF fi if test -f 'remark.c' then echo shar: "will not over-write existing file 'remark.c'" else cat << \SHAR_EOF > 'remark.c' /* remark.c * * Remark making code. * * Copyright: Chris Miller, 1979, 1981, 1984 * * Included with this othello game by * Rich Burridge, Sun Microsystems, Australia - December 1986. */ #include "othello.h" char rem_file[] = LIBPATH(LIB,othello.remarks) ; extern int aspire ; extern int cretin_flag ; extern int last_remark ; extern Panel_item remark_mes ; FILE *rem_fp ; long good_remarks[MAX_REMARKS],bad_remarks[MAX_REMARKS] ; int max_good = 0 ; int max_bad = 0 ; make_remark(machine_piece,value) int machine_piece,value ; { char message[100] ; value *= machine_piece ; if (value < -INFINITY+64) { last_remark = FIRST_REMARK ; /* Always remark next time */ if (aspire == MAXASPIRE) (void) sprintf(message,"You should win (by at most %d)",-value-INFINITY+64) ; else (void) strcpy(message,"You should win.") ; } else if (value < -1000) { if (last_remark != -INFINITY) { cretin_flag = TRUE ; last_remark = -INFINITY ; (void) strcpy(message,"Only a cretin could lose now.") ; } } else if (value <= 1000) printr(value,message) ; else if (value < INFINITY-63) { if (last_remark!=INFINITY) { (void) strcpy(message,"Resign, you dolt! Resistance is futile.") ; last_remark = INFINITY ; } } else { last_remark = FIRST_REMARK ; if (aspire == MAXASPIRE) (void) sprintf(message,"I shall win (by at least %d).", value-INFINITY+64) ; else (void) strcpy(message,"I shall win") ; } remark_msg(message) ; } get_remarks () /* Set up file pointers to remarks; the format of the remarks file is: * * Remarks to the effect that machine is winning, in increasing order of * strength, one per line. * * A line beginning with the character "@". * * Remarks to the effect that human is winning, in increasing order of * strength, one per line. */ { long fp = 0l ; int ch ; rem_fp = fopen(rem_file,"r") ; if (rem_fp == NULL) { remark_msg("Remarks not available") ; return ; } while ((ch = getc(rem_fp)) != EOF) { if (ch == '@') break ; good_remarks[max_good++] = fp++ ; if (max_good == MAX_REMARKS) { remark_msg("Too many remarks") ; return ; } while (ch != '\n') { ch = getc(rem_fp) ; fp++ ; } } max_good-- ; fp++ ; while (ch != '\n') { ch = getc(rem_fp) ; fp++ ; } while ((ch = getc(rem_fp)) != EOF) { bad_remarks[max_bad++] = fp++ ; if (max_bad == MAX_REMARKS) { remark_msg("Too many remarks") ; return ; } while (ch != '\n') { ch = fgetc(rem_fp) ; fp++ ; } } max_bad-- ; } printr(n,string) int n ; char string[100] ; /* Locate file-pointer and print appropriate line. The strange * computation is to cluster remarks towards low evaluations, * since the evaluation function changes more rapidly with small * changes in position as the evaluation gets larger */ { int ch ; int index ; float findex ; int sign_n = (n < 0 ? -1 : 1) ; char *stringp = string ; if (rem_fp == NULL) return ; findex = (float) abs(n) / 1000.0 ; index = findex * (2-findex) * (n < 0 ? max_bad : max_good) + 0.5 ; /* Don't make the same remark twice in a row */ if (index * sign_n == last_remark) return ; last_remark = index * sign_n ; fseek(rem_fp,n < 0 ? bad_remarks[index] : good_remarks[index],0) ; while ((ch = getc(rem_fp)) != EOF) { if (ch == '\n') break ; *stringp++ = ch ; } *stringp = '\0' ; } SHAR_EOF fi if test -f 'othello.h' then echo shar: "will not over-write existing file 'othello.h'" else cat << \SHAR_EOF > 'othello.h' /* othello.h * * Definitions used by the Sun Othello game. */ #include <stdio.h> #include <strings.h> #include <suntool/sunview.h> #include <suntool/panel.h> #include <suntool/canvas.h> #define panel_msg(s) (void) panel_set(panel_mes,PANEL_LABEL_STRING,s,0) #define remark_msg(s) (void) panel_set(remark_mes,PANEL_LABEL_STRING,s,0) #define score_msg(s) (void) panel_set(score_mes,PANEL_LABEL_STRING,s,0) #define BOARDSIZE 8 /* 8x8 playing board */ #define RCLR PIX_CLR /* Rasterop operations. */ #define RSRC PIX_SRC #define RINV PIX_SRC ^ PIX_DST #define BLACK 1 /* Piece definitions. */ #define FREE 0 #define WHITE -1 #define FOR_BOARD(i) for (i = 0; i < 64; i++) #define INFINITY 10000 #define INIT_ASPIRE 3 #define INIT_DEPTH 2 #define ISMOVE(pos,player) \ (nextmove(pos,NOMOVE,(BOARD *) NULL, player) != NOMOVE) #define MAXASPIRE 6 #define MAXDEPTH 6 #define NOMOVE -1 #define OPPONENT(p) p * -1 #define PAUSE(i) for (i = 0; i < 150000; i++) #define VINVUL 50 #define MAX_REMARKS 50 #define FIRST_REMARK (INFINITY+1) #define LIBPATH(l,c) l/c" typedef struct { char SQUARE[64] ; int MOVES_LEFT ; } BOARD ; SHAR_EOF fi if test -f 'othello.remarks' then echo shar: "will not over-write existing file 'othello.remarks'" else cat << \SHAR_EOF > 'othello.remarks' Pretty level; I'll start trying soon. Little in it; superior skill will tell. My superior skill is telling. You're beginning to lose your grip. You're really not so hot after all. Wake up! You're falling by the wayside. Come on, concentrate! If I weren't a computer, I'd be bored. They tell me checkers is easier. I'm starting to get bored. I'd win with one CPU behind my back. Who on earth programmed YOU? Quiche-eater! I seem to be hammering you a little. Are you a random-number generator? You should practice with a calculator. When things are going badly DON'T PANIC. I think you should start to panic. I'm thrashing you. Oh for some REAL opposition. What a massacre! My brilliance equals your dullness. Why don't you take up knitting instead? You should take up computer science. So it goes - I win some, you lose some. You've had it. Why not give up now? @ Just about dead level. Maybe you just have a tiny advantage. I'll sweep aside your slight advantage. Maybe you're not as daft as you look. Good! You're getting the hang of it now. I ought to start playing properly soon. All right, no need to show off. Have you played this game before? Hmmm. This may be harder than I thought. Are you fifth-generation? I wish to register a complaint. I didn't want to play this, you know. Give me a chance! I'm only a machine. I suppose you think your pretty smart. Do you beat up old ladies as well? You may be ahead, but it isn't over yet. Well, it isn't QUITE over yet. You seem to know this game pretty well. Wait till I get my brother Kray onto you. I think I'm getting a slight headache. You should pick on someone your own size. Megarats! You're winning heavily. SHAR_EOF fi if test -f 'patch.1' then echo shar: "will not over-write existing file 'patch.1'" else cat << \SHAR_EOF > 'patch.1' This patchfile does two things: (1) Adds in the CANVAS_FAST_MONO option to the window_create system call (V3.2 onwards). (2) Changes TABS back to spaces in the panel_create_item call and forces this item to be on the next line. *** otool.c~ Mon Jan 5 12:40:42 1987 --- otool.c Mon Jan 5 14:05:29 1987 *************** *** 20,26 **** #include "othello.h" ! #define PATCHLEVEL 0 /* Compilation parameters: */ --- 20,26 ---- #include "othello.h" ! #define PATCHLEVEL 1 /* Compilation parameters: */ *************** *** 144,150 **** WIN_HEIGHT, TOTAL_WIDTH+1, WIN_WIDTH, TOTAL_WIDTH+1, WIN_IGNORE_PICK_EVENTS, KBD_REQUEST, WIN_ASCII_EVENTS, 0, ! /* CANVAS_FAST_MONO, TRUE, */ 0) ; window_fit(base_frame) ; --- 144,150 ---- WIN_HEIGHT, TOTAL_WIDTH+1, WIN_WIDTH, TOTAL_WIDTH+1, WIN_IGNORE_PICK_EVENTS, KBD_REQUEST, WIN_ASCII_EVENTS, 0, ! CANVAS_FAST_MONO, TRUE, 0) ; window_fit(base_frame) ; *************** *** 252,259 **** 0) ; remark_mes = panel_create_item(panel,PANEL_MESSAGE, ! PANEL_LABEL_STRING," ", ! 0) ; panel_mes = panel_create_item(panel,PANEL_MESSAGE, PANEL_LABEL_STRING,"To move, use your left mouse button", --- 252,259 ---- 0) ; remark_mes = panel_create_item(panel,PANEL_MESSAGE, ! PANEL_LABEL_STRING," ", ! 0) ; panel_mes = panel_create_item(panel,PANEL_MESSAGE, PANEL_LABEL_STRING,"To move, use your left mouse button", SHAR_EOF fi exit 0 # End of shell archive D D