# 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 #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 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 #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=0 && yy 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; } }