home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
listings
/
v_07_03
/
v7n3034a.txt
< prev
next >
Wrap
Text File
|
1989-03-05
|
19KB
|
659 lines
# Makefile for the Connect Four Game c4 - Charlie Havener
HEADERS=vt100.h c4.h
c4.obj: c4.c $(HEADERS)
cl /Zi /Dbugprint /c c4.c
c4ab.obj: c4ab.c $(HEADERS)
cl /Zi /Dbugprint /c c4ab.c
c4move.obj: c4move.c $(HEADERS)
cl /Zi /Dbugprint /c c4move.c
c4subs.obj: c4subs.c $(HEADERS)
cl /Zi /Dbugprint /c c4subs.c
c4.exe: c4.obj c4move.obj c4subs.obj c4ab.obj
cl /Zi /Dbugprint c4.obj c4move.obj c4subs.obj c4ab.obj
/* c4.h header file for Connect Four Game program */
#define XSIZE 7
#define YSIZE 6
#define NEWDISPLAY 1
#define RED 1
#define BLACK 2
#define PLAYABLE 3
#define EMPTY 0
#define YOUWIN 1
#define IWIN 2
#define TIE 3
#define YOURMOVE 4
#define VT100 100
#define TTY 50
#define FALSE 0
#define TRUE 1
#define NIL 0
#define ILLEGAL -1
/* This is a rather clever debug print macro */
#ifdef bugprint
#define Debug(a,b) if ( debuglevel >= (a) ) printf b;
#else
#define Debug(a,b)
#endif
/* some ANSI escape sequence codes */
#define CURHOM "\033[H" /* home the cursor */
#define CURSAV "\033[s" /* save the present location of the cursor */
#define CURRES "\033[u" /* restore cursor to the saved location */
#define EAOP "\033[2J" /* erase all of page */
void init();
int move();
void display();
int askmove();
int c4move();
int c4tie();
/* c4.c - main program for the Connect Four Game, The object
is to get 4 checkers in a row horizontally, vertically or diagonally
on a 6 vertical by 7 horizontal checker board. Checkers enter at the
top of the 7 columns only. The alpha-beta pruning algorithm is used.
Charles D. Havener Dec 1988 */
#include <stdio.h>
#include "c4.h"
/* All the global variables are defined here */
int bd[XSIZE][YSIZE]={0}; /* the board representation */
int search_depth=2; /* the maximum depth of search allowed */
int debuglevel = 0; /* controls amount of debug printout */
int cpu_move = ILLEGAL; /* the last move chosen by the computer */
int terminal_type=TTY ; /* only TTY used now, future expansion */
/*-----------------------------------------------------------*/
main(argc,argv)
int argc;
char *argv[];
{
int status = YOURMOVE;
int player[22]; /* at most each player can make 21 moves */
int computer[22]; /* this is the history of the moves */
int turn = 0;
int mv = 0;
int replay = FALSE; /* set TRUE if instant replay in progress */
char buf[40];
if ( argc == 2 )
search_depth = atoi(argv[1]);
begin:
if ( search_depth < 1 || search_depth > 8 )
fatal("\n search_depths must be between 1 and 8 \n");
init();
turn = 0;
status = YOURMOVE;
printf("%s%s",EAOP,"Welcome to CONNECT FOUR Version 1.1\n");
display(bd,NEWDISPLAY,terminal_type);
do /* henceforth the player is RED & goes 1st, cpu is BLACK */
{
if ( replay )
{
mv = player[turn];
move(mv,RED,bd);
}
else
mv = askmove(RED);
if ( mv != ILLEGAL )
{
status = c4move(BLACK); /* cpu moves & returns status */
display(bd,NEWDISPLAY,terminal_type);
if ( replay )
{
if ( cpu_move != computer[turn] )
{
replay = FALSE;
printf("\n Different computer move! \n");
}
}
player[turn] = mv;
computer[turn++] = cpu_move;
switch ( status )
{
case YOUWIN: printf("You Win Lucky\n");
break;
case IWIN: printf("I win. I'm so smart!\n") ;
break;
case TIE: printf("Amazing - a tie!\n");
break;
case YOURMOVE:
default:
break;
}
}
} while ( status == YOURMOVE ) ;
printf("\nPlayer ");
for ( mv=0; mv < turn ; mv++ )
printf(" %d",player[mv]);
printf("\nComputer ");
for ( mv=0; mv < turn ; mv++ )
printf(" %d",computer[mv]);
printf("\nThank you for the game at depth %d\n",search_depth);
printf("For instant replay at new search depth,\n");
printf("Enter depth 1 to 8 (0 to quit): ");
gets(buf);
search_depth = atoi(buf);
if ( search_depth != 0 )
{
replay = TRUE;
goto begin;
}
return 0;
}
/* c4ab.c - This is the alpha-beta algorithm for the Connect Four
game. f2() and g2() stop generate() of new boards if a win or loss
has occured on the brd. Static evaluator tests for loss first and
the initial value of bestmove was made illegal if the c4pick()
function didn't choose a move.
*/
#include "c4.h"
static int bestmove = ILLEGAL; /* will be picked by computer */
static int nstats = 0; /* number of static evaluations */
extern int bd[XSIZE][YSIZE];
extern int terminal_type;
extern int debuglevel;
extern int search_depth;
int suckertrap();
int earlywarning();
int evalbd();
int *c4cpy();
void generate();
/*-------------------------------------------------------------------*/
/* f2 - the alpha beta maximizer algorithm from Knuth's paper AI 1975*/
int f2(brd,alpha,beta,depth,maxdepth)
int brd[XSIZE][YSIZE]; /* apologies to lint for passing in int* */
int alpha;
int beta;
int depth;
int maxdepth;
{
int m,t,x,i;
int *q;
int *boards[XSIZE+1]; /* filled in by generate() */
int play[XSIZE]; /* filled in by generate() */
q = NIL;
Debug(1,("\n in f2() a=%d b=%d",alpha,beta))
if ( depth < maxdepth & !( c4won(BLACK,brd) || c4won(RED,brd) ) )
{
generate(boards,play,brd,depth+1); /* generate new boards */
/* no imbedded NILs. Last one NIL for sure */
i = 0;
q = boards[ i++ ]; /* i.e. q = first(p) Knuths notation */
}
if ( q == NIL )
{
m = stateval(brd); /* no legal moves or at maxdepth */
Debug(1,("\nLeaving f2 stateval = m =%d",m))
return m;
}
m = alpha;
while ( q != NIL && m < beta )
{
t = g2(q,m,beta,depth+1,maxdepth);
if ( t > m )
{
m = t;
if ( depth == 0 ) /* save the best move! */
bestmove = play[i-1]; /* c4pick uses bestmove */
}
q = boards[ i++ ]; /* q = next(q) Knuth */
}
for ( x=0 ; boards[x] ; x++ ) /* test for non NIL before freeing */
free( boards[x] ); /* recover memory space */
Debug(1,("\nLeaving f2 m=%d",m))
return m;
}
/*-------------------------------------------------------------------*/
/* g2 - the alpha beta minimizer algorithm from Knuth's paper AI 1975*/
g2(brd,alpha,beta,depth,maxdepth)
int brd[XSIZE][YSIZE];
int alpha;
int beta;
int depth;
int maxdepth;
{
int m,t,x,i;
int *q;
int *boards[XSIZE+1]; /* filled in by generate, last slot NIL */
int play[XSIZE];
q = NIL;
Debug(1,("\n in g2() a=%d b=%d",alpha,beta))
if ( depth < maxdepth & !( c4won(BLACK,brd) || c4won(RED,brd) ) )
{
generate(boards,play,brd,depth+1);
/* fills boards array with ptrs to boards */
/* no imbedded NILs. Last one NIL for sure */
i = 0;
q = boards[ i++ ]; /* i.e. q = first(p) Knuths notation */
}
if ( q == NIL )
{
m = stateval(brd); /* no legal moves or at maxdepth */
Debug(1,("\nLeaving g2 stateval = m =%d",m))
return m;
}
m = beta;
while ( q != NIL && m > alpha )
{
t = f2(q,alpha,m,depth+1,maxdepth);
if ( t < m )
m = t;
q = boards[ i++ ]; /* q = next(q) Knuth */
}
for ( x=0 ; boards[x] ; x++ ) /* test for non NIL before freeing */
free( boards[x] ); /* recover memory space */
Debug(1,("\nLeaving g2 m=%d",m))
return m;
}
/*---------------------------------------------------------------------*/
/* generate - given a brd, fill in array of ptrs with next legal moves */
void generate(positions,play,brd,depth)
int play[XSIZE];
int *positions[XSIZE+1];
int brd[XSIZE][YSIZE];
int depth;
{
int x;
int i;
int *p;
static int plausible_moves[XSIZE] = {3,4,2,1,5,0,6};
Debug(2,("\n gen depth=%d ",depth))
for ( x=0 ; x<= XSIZE ; x++ )
positions[x] = NIL;
for ( x=0, i=0 ; x<XSIZE ; x++ )
{
p = c4cpy(brd); /* allocates space and copys brd into it */
if ( move(plausible_moves[x],(depth & 01 ? BLACK : RED ),p) )
{
play[i] = plausible_moves[x];
positions[i++] = p; /* legal move made */
}
else
free(p); /* recover space */
}
}
/*-------------------------------------------------------------------*/
/* stateval - static evaluation of brd position, high good for BLACK */
stateval(brd)
int brd[XSIZE][YSIZE];
{
int x,y;
int score=100; /* nominal score */
++nstats; /* increment total number of static evals */
if ( debuglevel && terminal_type == VT100 )
display(brd,NEWDISPLAY,terminal_type);
for ( x=0 ; x<XSIZE ; x++ )
move(x,PLAYABLE,brd); /* mark playable spots */
if ( c4won(RED,brd) )
score = 0;
else if ( c4won(BLACK,brd) )
score = 1000.;
else if (suckertrap(brd) )
score = 2; /* very bad for BLACK */
else if ( earlywarning(brd) )
score = 4; /* also bad for BLACK */
else
{
for ( x=2 ; x<5 ; x++ )
for ( y=0 ; y<YSIZE ; y++ )
{
if ( brd[x][y] == BLACK )
{
score += 3;
if ( x == 3 )
score += 3;
}
else if ( brd[x][y] == RED )
{
score--;
if ( x == 3 )
score--;
}
else
break; /* rest of column EMPTY */
}
x = 3; /* most wins will start and pass thru the center col */
for ( y=0; y<YSIZE ; y++ )
{
score += 5*evalbd(x,y,BLACK,brd,3);
score -= 3*evalbd(x,y,RED,brd,3);
}
}
return score;
}
/*---------------------------------------------------------*/
/* suckertrap - tests for 3 in row with both ends PLAYABLE */
int suckertrap(brd)
int brd[XSIZE][YSIZE];
{
int x,y=0;
for ( y=0 ; y<YSIZE ; y++ )
{
for ( x=0 ; x<XSIZE-4 ; x++ )
{
if ( brd[x][y] == PLAYABLE && brd[x+1][y] == RED
&& brd[x+2][y] == RED && brd[x+3][y] == RED
&& brd[x+4][y] == PLAYABLE )
return TRUE;
}
}
return FALSE;
}
/*----------------------------------------------------------------*/
/* earlywarning - early warning of suckertrap, 2 PLAYABLE on ends */
int earlywarning(brd)
int brd[XSIZE][YSIZE];
{
int x,y;
for ( y=0 ; y<YSIZE ; y++ )
{
for ( x=0 ; x<XSIZE-5 ; x++ )
{
if ( brd[x][y] == PLAYABLE && brd[x+1][y] == PLAYABLE
&& brd[x+2][y] == RED && brd[x+3][y] == RED
&& brd[x+4][y] == PLAYABLE && brd[x+5][y] == PLAYABLE)
return TRUE;
}
}
return FALSE;
}
/*------------------------------------------------------------------*/
/* c4cpy - allocates space for new brd, copies into it, returns ptr */
int *c4cpy(brd)
int brd[XSIZE][YSIZE];
{
int *p;
p = (int *)malloc(XSIZE*YSIZE*2);
if ( p == 0 )
fatal("Could not allocate more board space");
memmove(p,brd,sizeof(bd) );
return p;
}
/*----------------------------------------------------------------*/
/* c4pick - picks the computers move by calling alpha-beta search */
int c4pick(color)
int color;
{
int score;
long max = 7;
int k = search_depth;;
nstats = 0; /* zero total number of static evals counter */
while ( --k )
max *= 7; /* 7 to the search_depth is max no. boards */
bestmove = ILLEGAL; /* insures illegal if f2 fails to pick one */
score = f2(bd,-30000,30000,0,search_depth); /* call a-b algorithm */
printf("\n Score = %d #static evals=%d out of %ld\n",score,nstats,max);
return bestmove;
}
/*--------------------------------------------------------------*/
/* evalbd - evaluates if N in a row is present in any direction */
int evalbd(x,y,color,brd,in_a_row)
int in_a_row;
int x,y;
int color;
int brd[XSIZE][YSIZE];
{
int d[8]; /* number of squares in each of 8 directions */
int deltax,deltay = -1;
int k,xx,yy,attack_lines,i = 0;
for ( deltax = -1 ; deltax<2 ; ++deltax )
{
for ( deltay = -1 ; deltay<2 ; ++deltay )
{
if ( deltax == 0 && deltay == 0 )
++deltay; /* or you may loop 'till doomsday */
for (k=0,xx=x,yy=y ; (0 <= xx) && (xx < XSIZE)
&& (0<=yy) && (yy<YSIZE) ; xx += deltax,yy += deltay)
{
if ( brd[xx][yy] == color )
{
if ( ++k == in_a_row )
break;
}
else
break; /* escape the for loop */
}
if ( k > 0 )
d[i++] = --k; /* save no. of squares in this direc */
/* Dont count start square twice */
else
d[i++] = 0;
}
}
attack_lines = (1+ d[0] + d[7]) / in_a_row /* right diagonal */
+ (1+ d[1] + d[6]) / in_a_row /* horizontal */
+ (1+ d[2] + d[5])/in_a_row /* left diagonal */
+ (1 + d[3] + d[4])/in_a_row ; /* vertical */
return attack_lines; /* how many lines of N in a row attacks */
}
/* score table
dx dy i direction
-1 -1 0 / left down
-1 0 1 <-----
-1 1 2 \ left up
0 -1 3 | down
0 0 skipped
0 1 4 | up
0 -1 5 \ right down
1 0 6 --->
1 1 7 / right up
used in computing score
*/
/* c4move.c - makes and checks moves on the board for legality etc */
#include <stdio.h>
#include "c4.h"
extern int bd[XSIZE][YSIZE];
extern int cpu_move;
/*-----------------------------------------------------------*/
/* c4move - this routine picks the cpu's move - calls c4pick */
int c4move(mycolor)
int mycolor;
{
int hiscolor,x;
if ( mycolor == BLACK )
hiscolor = RED;
else
hiscolor = BLACK;
if ( c4won(hiscolor,bd) ) /* check if he just won! */
return YOUWIN;
if ( c4tie() ) /* any moves left for computer? */
return TIE;
x = c4pick(mycolor); /* invokes alpha-beta search algorithm */
cpu_move = x;
if ( !move(x,mycolor,bd) )
{
printf("Illegal computer move [%d] attempted\n",x);
return YOUWIN; /* computer move was illegal!? */
}
if ( c4won(mycolor,bd) )
return IWIN;
else if ( c4tie() )
return TIE;
else
return YOURMOVE;
}
/*--------------------------------------------------*/
/* c4won - returns TRUE if specified color just won */
int c4won(color,brd)
int color;
int brd[XSIZE][YSIZE];
{
int k,x,y,xx,yy;
for ( x=0 ; x < XSIZE ; x++ )
{
for ( y=0 ; y < YSIZE ; y++ )
{
if ( brd[x][y] == color )
{
for ( k=0, xx=x ; xx < XSIZE ; xx++ ) /* horizontal */
{
if ( brd[xx][y] != color )
break;
if ( ++k == 4 )
return TRUE;
}
for ( k=0, yy=y ; yy<YSIZE ; yy++ ) /* vertical */
{
if ( brd[x][yy] != color )
break;
if ( ++k == 4 )
return TRUE;
}
for ( k=0,xx=x,yy=y ; xx<XSIZE && yy<YSIZE ; ++xx,++yy )
{
if ( brd[xx][yy] != color )
break;
if ( ++k == 4 )
return TRUE;
}
for ( k=0,xx=x,yy=y ; xx>=0 && yy<YSIZE ; --xx,++yy)
{
if ( brd[xx][yy] != color )
break;
if ( ++k == 4 )
return TRUE;
}
}
}
}
return FALSE; /* specified color has not won */
}
/*------------------------------------------------------------*/
/* c4tie - returns TRUE if there are no moves left to be made */
int c4tie()
{
int x;
for ( x=0 ; x<XSIZE ; ++x )
if (bd[x][YSIZE-1] == EMPTY )
return FALSE;
return TRUE;
}
/* c4subs.c - misc routines needed by the Connect Four Game */
#include "c4.h"
#include <stdio.h>
extern int bd[XSIZE][YSIZE];
extern int terminal_type;
/*------------------------------------------------------*/
/* init - initializes the board to empty */
void init()
{
int x,y = 0;
for ( x = 0 ; x < XSIZE ; x++ )
for ( y=0 ; y < YSIZE ; y++)
bd[x][y] = EMPTY;
}
/*------------------------------------------------------*/
/* move - makes the specified move on the board passed to it */
int move(x,color,brd)
int x;
int color;
int brd[XSIZE][YSIZE];
{
int y = 0;
if ( x >= 0 && x < XSIZE )
{
for ( y=0 ; y < YSIZE ; y++ )
{
if ( brd[x][y] == EMPTY )
{
brd[x][y] = color;
return TRUE;
}
}
}
return FALSE; /* Illegal move! */
}
/*------------------------------------------------------*/
/* fatal - prints out error message on stderr and exits */
void fatal(message)
char *message;
{
fprintf(stderr,"\n%s\n",message);
exit(1);
}
/*------------------------------------------------------*/
/* display - updates the terminal display from the given array */
void display(brd,option,term_type)
int brd[XSIZE][YSIZE];
int option,term_type;
{
int i,x,y;
if ( option == NEWDISPLAY && term_type == VT100 )
{
printf("%s%s",CURSAV,CURHOM);
}
printf("\n\t 0 1 2 3 4 5 6\n");
for ( y = YSIZE-1 ; y >= 0 ; y-- )
{
printf("\n\t");
for ( x = 0 ; x < XSIZE ; x++ )
{
i = brd[x][y];
if ( i == EMPTY )
printf("__ ");
else if ( i == RED )
printf("00 ");
else
printf("XX ");
}
}
if ( term_type == VT100 )
printf("%s",CURRES);
}
/*------------------------------------------------------*/
/* askmove - obtains players move and checks if valid */
int askmove(color)
int color;
{
int x;
char string [80];
char *p;
extern int cpu_move;
printf("\n\n");
if ( cpu_move >= 0 )
printf(" My move = %d\n",cpu_move);
printf("Enter the column (0-6) for your move: ");
p = gets(string);
if ( p == 0 )
fatal("Game terminated by EOF");
if ( string[0] == '\014' ) /* Cntrl L */
{
printf("%s",EAOP);
display(bd,NEWDISPLAY,terminal_type);
return ILLEGAL;
}
x = string[0] - '0';
if ( move(x,color,bd) )
return x;
else
{
printf("\n Come on now that's an illegal move");
return ILLEGAL;
}
}