|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T m
Length: 12084 (0x2f34)
Types: TextFile
Names: »makemove.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/Sun/Othello/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 ;
}
}