[comp.sources.games] v06i051: connect4 - connect four opponent

games@tekred.CNA.TEK.COM (04/27/89)

Submitted-by: speedboat jones <tcjones@watdragon.waterloo.edu>
Posting-number: Volume 6, Issue 51
Archive-name: connect4

[from the author:]
[[Hi there. Lots of people have asked about Connect Four here in the past
(or so it seems). Some months ago I wrote this Connect Four opponent
(i.e. you against the machine) and intended to clean it up alot before
ever posting it. Anyway now I'm leaving the net world and so I thought
I'd let it out. Caveat emptor and all that.

This should compile just fine on BSD with curses, others will have to
make some changes though. See the README and Install files for
information.

Terry Jones]]
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  README MANIFEST Install Makefile c4.c c4.h screen.c
#   tables.h types.h
# Wrapped by billr@saab on Wed Apr 26 11:16:15 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2266 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XREADME.
X
X
XThis is a Connect Four opponent. It was written under BSD4.3 and uses
Xcurses.  Modifications to make it work on other Unices should be
Xrelatively minor - I can think of perhaps 4 or 5 things that would need
Xdoing.
X
XI am a little hesitant about releasing this now as it is far from what
XI would call a finished thing and the code is ugly. It came about after
Xa few hours thinking about how I could write such a thing (data
Xstructures and strategy and so forth) and then one night of hacking to
Xget something working. Since then it has been through several revisions
Xof strategy, but I still consider it to be missing at least one ability
Xthat humans seem to employ when playing. I intend to add this, but not
Xin the immediate future.
X
XI am also hesitant about putting this into widespread distribution as
XConnect Four is undoubtedly a trade mark or some such of Milton Bradley
Xand I don't want to be woken up by phone calls from their lawyers.
X
XI just took another look at all the code etc and I'm really not very
Xhappy with it. Oh well. Flames meekly accepted - as long as you let me
Xsay "I already knew that..." :-)
X
XI'm happy to discuss strategies and improvements you might like to
Xsuggest. I'd also be happy to see this code developed further, but at
Xthe same time feel a little possessive about it :-( But, since I am too
Xbusy to do anything more for the present, you have permission to do
Xanything with this code and there is no real need to consult me.
X
XThere are very few comments in the code. I am planning to spend quite a
Xfew hours adding some before I lay this to rest and get back to my
Xschoolwork - just so that when I return to it in a year (or whenever) I
Xwon't get that sinking feeling of not understanding one's own code
Xanymore.  Or something like that. 
X
XAbove all - have fun etc etc. See the file 'Install' for instructions on
Xgetting this up and going.
X
XRegards,
XTerry Jones
X
XDepartment Of Computer Science,  University Of Waterloo     
XWaterloo Ontario Canada N2L 3G1. Phone: 1-519-8884674       
XUUCP:                    ...!watmath!watdragon!tcjones      
XCSNET, Internet, CDNnet: tcjones@dragon.waterloo.{cdn,edu}  
XBITNET:                  tcjones@WATER.bitnet               
XCanadian domain:         tcjones@dragon.uwaterloo.ca
END_OF_FILE
if test 2266 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(393 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X Install                    1	
X MANIFEST                   1	This shipping list
X Makefile                   1	
X README                     1	
X c4.c                       1	
X c4.h                       1	
X screen.c                   1	
X tables.h                   1	
X types.h                    1	
END_OF_FILE
if test 393 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'Install' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Install'\"
else
echo shar: Extracting \"'Install'\" \(2209 characters\)
sed "s/^X//" >'Install' <<'END_OF_FILE'
XInstall
X
XIf you are running BSD then lucky for you! Just type make and it should
Xcompile ok. If not then you'll have to make some fixes. If you can't
Xfigure out what they are then I may be willing to help - send
Xcompiler messages etc and I will try and make it work on a system5 or
Xultrix or dynix machine here. I am only able to give limited support
Xbecause I don't have alot of time for this stuff during term.
X
XA few things to note are....
X
XIf SCOREFILE is not defined in the Makefile or header file,
Xc4 should be chmod'd to 4511 and will keep a log in the home directory
Xof the file's owner. The log will show the results of every game that
Xis played. This is very useful for looking for bugs and finding out
Xwhat sort of things the game does wrong in an attempt to improve its
Xstrategy etc. There is a debug option available that writes a file
Xcalled 'debug' in the current directory.  (You need to change 'wizard'
Xin c4.c if you wish to use debugging - you shouldn't have to).  There
Xis alot of output and so you can toggle debug on and off using a 'd'
Xwhilst playing. The debug on/off routines check to see that your userid
Xis 'tcjones' so you will probably want to change that. All debugging
Xprintfs are done with the DP #define - which will eventually vanish if
Xthis ever gets released properly.
X
XAlso there is a char *[] table called 'experts' in tables.h. If a
Xuserid in this table is playing, it will not ask whether help is
Xrequired. This also I will change if ever I rewrite this thing - it
Xshould check the scorefile to see if this person has played before.
X
XThere is a -S option to list the scorefile. I tend to find that people
Xare less willing to play the game if they know that others can look at
Xtheir results (hence S not s). I plan to make this better too - so that
Xyou can ask for the scores for a particular person etc.
X
XThere is a -1 option to say "I want to go first" and a -2 option to say
X"I want to go second".
X
XThere are MANY things I want to add and change. This grew out of a
Xsocket based program that let one person play another, but that has
Xdisappeared. I'd like to put it back. I'd like to make it possible to
Xwithdraw moves, to save/restore games and other things.
X
XTerry
END_OF_FILE
if test 2209 -ne `wc -c <'Install'`; then
    echo shar: \"'Install'\" unpacked with wrong size!
fi
# end of 'Install'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(235 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# Makefile for c4
X
XCFLAGS = -g #-DSCOREFILE=\"/usr/games/lib/c4.scores\" 
XCURSES = -lcurses -ltermcap
X
Xc4 : c4.o screen.o c4.h tables.h types.h
X	cc $(CFLAGS) -o c4 c4.o screen.o $(CURSES)
X
Xscreen.o : c4.h
X
Xc4.o : c4.h tables.h types.h
END_OF_FILE
if test 235 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'c4.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'c4.c'\"
else
echo shar: Extracting \"'c4.c'\" \(18926 characters\)
sed "s/^X//" >'c4.c' <<'END_OF_FILE'
X/*
X * c4.h - Connect Four game using curses.
X *
X *
X * Terry Jones. December 28, 1988.
X *
X *     Department Of Computer Science,  University Of Waterloo
X *     Waterloo Ontario Canada N2L 3G1. Phone: 1-519-8884674
X *     UUCP:                    ...!watmath!watdragon!tcjones
X *     CSNET, Internet, CDNnet: tcjones@dragon.waterloo.{cdn,edu}
X *     BITNET:                  tcjones@WATER.bitnet
X *     Canadian domain:         tcjones@dragon.uwaterloo.ca
X *
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <sys/time.h>
X#include <sys/file.h>
X#include <sys/param.h>
X#include "c4.h"
X#include "types.h"
X#include "tables.h"
X
X#define DP if (d) fprintf
X#define DD if (d)
X#ifndef MAXHOSTNAMELEN
X#define MAXHOSTNAMELEN 64	/* from sys/param.h on vanilla 4.3bsd */
X#endif
X
Xwin_list_t win_list;
Xboard_t board;
Xint moves_played = 0;
Xchar *myname;
Xu_char game_record[SQUARES];
XFILE *scorefile;
XFILE *d = NULL;
XFILE *save = NULL;
Xchar *u_name = "Bozo";
Xchar *version = "3.2";
Xchar *release_date = "8pm, January 1st, 1988";
Xint turn = GAME_OVER;
Xint first;
Xchar *wizard = "tcjones";
X
Xmain(argc, argv, envp)
Xint argc;
Xchar **argv;
Xchar **envp;
X{
X    register int move;
X    register int square;
X    register int winner = DRAW;
X	int temp;
X
X    envmesg(envp, "Connect Four");
X    do_args(argc, argv);
X    open_scorefile("a");
X    ask_help();
X    first = turn = turn == GAME_OVER ? ask_turn() : turn;
X    init_win_list_t(&win_list);
X    init_board_t(&board);
X    init_screen();
X    accept_move(turn);
X
X    for (;;){
X        if (moves_played == SQUARES){
X            turn = GAME_OVER;
X        }
X
X        DP(d, "\n<<< M O V E  =  %d >>>\n", moves_played + 1);
X
X        if (turn == GAME_OVER) break;
X
X        if (turn == THEIRS){
X            move = get_move();
X        }
X        else{
X            move = think_of_fucking_clever_move();
X        }
X
X        square = update_column(move);
X
X        if (square < 0){
X            bell();
X            report("That column is full!");
X            if (turn == OURS){
X                winner = CONFUSED;
X                turn = GAME_OVER;
X                report("column %2d, square %2d", move, square);
X            }
X            accept_move(turn);
X            continue;
X        }
X
X        fill_column(move, turn);
X        show_move(move, turn);
X
X        update_game_record(move);
X        update_board(&board, square, turn);
X        update_win_list(&win_list, square, turn);
X        if ((temp = count(&win_list, (u_char) 4, turn))){
X			DP(d, "Temp from count is %d\n", temp);
X            winner = turn;
X            turn = GAME_OVER;
X        }
X        else{
X            turn = turn == OURS ? THEIRS : OURS;
X        }
X    }
X    plot_finish();
X    print_game_record(winner);
X    goodbye();
X}
X
Xvoid
Xinit_win_list_t(w_list)
Xwin_list_t *w_list;
X{
X    register int i;
X
X    for (i = 0; i < WINS; i++){
X        w_list->us[i] = w_list->them[i] = (u_char) 0;
X    }
X}
X
Xvoid
Xinit_board_t(bd)
Xboard_t *bd;
X{
X    register int i;
X
X    for (i = 0; i < SQUARES; i++){
X            bd->square[i] = EMPTY;
X    }
X}
X
X
Xvoid
Xprint_in_what_wins()
X{
X    register int i;
X    for (i = 0; i < SQUARES; i++){
X        int index = cum_index[i];
X        printf("Sq %2d (offset = %3d) :", i, index);
X        while (in_what_wins[index] != (u_char) -1){
X            printf(" %d", in_what_wins[index++]);
X        }
X        printf("\n");
X    }
X}
X
X
Xint
Xget_move()
X{
X    register int move;
X
X    get: move = getchar();
X
X	switch(move){
X
X		/* COLUMN # */
X		case '0': case '1': case '2': case '3': case '4': case '5': case '6':{
X    		return move - '0';
X		}
X
X		/* QUIT */
X    	case 'q' :
X		case 'x' :{
X        	print_game_record(QUIT);
X        	plot_finish();
X        	printf("Chicken...\n");
X        	goodbye();
X    	}
X
X		case 'v':{
X			report("Version %s. Released %s", version, release_date);
X			accept_move(turn);
X			goto get;
X		}
X
X		/* DEBUG */
X    	case 'd':{
X        	if (d)
X            	debug_off();
X        	else
X            	debug_on();
X        	goto get;
X    	}
X
X		/* CONTROL L (redraw screen) */
X    	case 0xc:{
X			register int i;
X        	plot_screen();
X        	reset_row_levels();
X        	turn = first;
X        	for (i = 0; i < moves_played; i++){
X            	fill_column(game_record[i], turn);
X            	turn = turn == OURS ? THEIRS : OURS;
X        	}
X        	accept_move(turn);
X        	goto get;
X    	}
X
X		/* RUBBISH */
X		default:{
X			goto get;
X		}
X	}
X}
X
Xint
Xask_turn()
X{
X    char line[128];
X    register char *cp;
X
X    printf("Would you like to go first? (yes/no) -> ");
X    if (!gets(line)){
X        plot_finish();
X        fprintf(stderr, "Could not read input line.\n");
X        goodbye();
X    }
X
X    cp = line;
X    while (*cp == ' ' || *cp == '\t') cp++;
X
X    if (*cp == 'n' || *cp == 'N')
X        return OURS;
X    return THEIRS;
X}
X
Xint
Xupdate_column(col)
Xint col;
X{
X    int square = next_in_col[col];
X    next_in_col[col] -= 7;
X    return square;
X}
X
Xvoid
Xupdate_game_record(move)
Xint move;
X{
X    game_record[moves_played] = move;
X    moves_played++;
X}
X
Xvoid
Xprint_game_record(winner)
Xint winner;
X{
X    register int i;
X    char host[MAXHOSTNAMELEN];
X    char *win_str = "won";
X    extern char *date();
X
X    bell();
X    switch (winner){
X        case OURS:{
X            printf("I won after %d moves.\n", moves_played);
X            break;
X        }
X        case THEIRS:{
X            printf("I lost after %d moves.\n", moves_played);
X            win_str = "lost";
X            break;
X        }
X        case QUIT:{
X            win_str = "quit";
X            break;
X        }
X        case DRAW:{
X            printf("The game is a draw!\n");
X            win_str = "drew";
X            break;
X        }
X        default:
X        case CONFUSED:{
X            win_str = "confused";
X        }
X    }
X
X    if (gethostname(host, sizeof(host)) == -1){
X        host[0] = '\0';
X        strcat(host, "Unknown");
X    }
X
X    if (flock(fileno(scorefile), LOCK_EX) == -1){
X        fprintf(stderr, "Could not lock scorefile!\n");
X        return;
X    }
X
X	if (fseek(scorefile, 0L, 2) == -1){
X		fprintf(stderr, "Could not unlock scorefile!\n");
X		if (flock(fileno(scorefile), LOCK_UN) == -1){
X			fprintf(stderr, "Could not unlock scorefile!\n");
X		}
X		return;
X	}
X
X    fprintf(scorefile, "Opponent   : %s@%s\n", u_name, host);
X    fprintf(scorefile, "Date       : %s [version %s]\n", date(), version);
X    fprintf(scorefile, "Result     : %s\n", win_str);
X    fprintf(scorefile, "First Move : %s\n", first == OURS ? "Me" : "Them");
X    fprintf(scorefile, "Moves      : %d\n", moves_played);
X    fprintf(scorefile, "Record     : ");
X
X    for (i = 0; i < moves_played && i < 21; i++){
X        fprintf(scorefile, "%d ", game_record[i]);
X    }
X    if (moves_played > 21)
X        fprintf(scorefile, "\n             ");
X    for (i = 21; i < moves_played; i++){
X        fprintf(scorefile, "%d ", game_record[i]);
X    }
X    fprintf(scorefile, "\n\n");
X
X    if (flock(fileno(scorefile), LOCK_UN) == -1){
X        fprintf(stderr, "Could not unlock scorefile!\n");
X        return;
X    }
X}
X
Xvoid
Xupdate_board(bd, sq, whose)
Xboard_t *bd;
Xint sq;
Xint whose;
X{
X    bd->square[sq] = whose;
X}
X
Xint
Xupdate_win_list(w_list, sq, whose)
Xwin_list_t *w_list;
Xint sq;
Xint whose;
X{
X    /*
X     * Update the win list specified by 'whose' and return a measure of
X     * the value of this move.
X     */
X    register int index = (int) cum_index[sq];
X    register u_char *my_list;
X    register u_char *their_list;
X    register int goodness = 0;
X    register int win;
X    int a_good[5];
X    int d_good[5];
X    int i;
X
X    for (i = 0; i < 5; i++){
X        a_good[i] = attack_good[i];
X        d_good[i] = defend_good[i];
X    }
X
X    if (whose == OURS){
X        my_list = w_list->us;
X        their_list = w_list->them;
X    }
X    else{
X        my_list = w_list->them;
X        their_list = w_list->us;
X    }
X
X    DP(d, "\t\tUpdate for square %d, turn = %d\n", sq, turn);
X    while((win = (int)in_what_wins[index]) != (u_char) -1){
X
X        if (their_list[win] == 0){
X            /* We just put something into a win in which they have none. */
X            goodness += a_good[my_list[win] + 1];
X            DP(d, "\t\tmake a %d in win %d", my_list[win] + 1, win);
X            DP(d, "\t\tgood+=%d goodness=%d\n", a_good[my_list[win] + 1],
X                goodness);
X            if (my_list[win])
X                a_good[my_list[win] + 1] = 0;
X        }
X        else if (my_list[win] == 0){
X            /* We just closed off a win for them. */
X            goodness += d_good[their_list[win] + 1];
X            DP(d, "\t\tstop a %d in win %d", their_list[win] + 1, win);
X            DP(d, "\t\tgood+=%d goodness=%d\n", d_good[their_list[win]+1],
X                goodness);
X            if (their_list[win])
X                d_good[their_list[win] + 1] = 0;
X        }
X        else{
X            DP(d, "\t\tNothing for win %d\n", win);
X        }
X
X        my_list[win]++;
X        index++;
X    }
X    return goodness;
X}
X
Xvoid
Xprint_board(bd, f)
Xboard_t *bd;
XFILE *f;
X{
X    register int row = 0;
X    register int col = 0;
X
X    fprintf(f, "\t 0123456\n");
X
X    for (row = 0; row < ROWS; row++){
X        fprintf(f, "\t%d", row);
X
X        for (col = 0; col < COLUMNS; col++){
X
X            switch(bd->square[row * COLUMNS + col]){
X                case OURS:{
X                    fprintf(f, OUR_SYM);
X                    break;
X                }
X                case THEIRS:{
X                    fprintf(f, THEIR_SYM);
X                    break;
X                }
X                case EMPTY:{
X                    fprintf(f, BLANK_SYM);
X                    break;
X                }
X                default:{
X                    fprintf(f, "fucking ugh! board screwup\n");
X                    break;
X                }
X            }
X        }
X
X        fprintf(f, "%d\n", row);
X    }
X    fprintf(f, "\t 0123456\n");
X}
X
Xvoid
Xgoodbye()
X{
X    exit(1);
X}
X
Xint
Xthink_of_fucking_clever_move()
X{
X    register int i;
X    register int j;
X    register int k;
X    win_list_t tmp_win_i;
X    win_list_t tmp_win_j;
X    win_list_t tmp_win_k;
X    int tmp_cols_i[COLUMNS];
X    int tmp_cols_j[COLUMNS];
X    int col_values_i[COLUMNS];
X    int col_values_j[COLUMNS];
X    int col_values_k[COLUMNS];
X    int max_i;
X    int best_col;
X    board_t tmp_board_i;
X    board_t tmp_board_j;
X    board_t tmp_board_k;
X
X    for (i = 0; i < COLUMNS; i++){
X        col_values_i[i] = 0;
X    }
X
X    DP(d, "CLEVER MOVE... board looks like\n");
X    DD print_board(&board, d);
X    DP(d, "---------------------------------\n");
X
X    for (i = 0; i < COLUMNS; i++){
X
X        register int max_j;
X
X        DP(d, "my column %d\n", i);
X
X        if (next_in_col[i] < 0){
X        DP(d, "my column %d is full\n", i);
X            continue;
X        }
X
X        tmp_win_i = win_list;
X        tmp_board_i = board;
X
X        update_board(&tmp_board_i, next_in_col[i], OURS);
X        DP(d, "Updated board...\n");
X        DD print_board(&tmp_board_i, d);
X        col_values_i[i] = update_win_list(&tmp_win_i, next_in_col[i], OURS);
X
X        if (col_values_i[i] >= attack_good[4])
X            return i;
X
X        DP(d, "my column %d gets value %d after update\n", i, col_values_i[i]);
X
X        for (j = 0; j < COLUMNS; j++){
X            tmp_cols_i[j] = next_in_col[j];
X            col_values_j[j] = 0;
X        }
X        tmp_cols_i[i] -= 7;
X
X        DP(d, "Temporary columns for inner loop... ");
X        for (j = 0; j < COLUMNS; j++){
X            DP(d, "%2d ", tmp_cols_i[j]);
X        }
X        DP(d, "\n");
X
X        for (j = 0; j < COLUMNS; j++){
X
X            register int max_k;
X
X            DP(d, "\this column %d\n", j);
X            if (tmp_cols_i[j] < 0){
X                DP(d, "\this column %d is full\n", j);
X                continue;
X            }
X
X            tmp_win_j = tmp_win_i;
X            tmp_board_j = tmp_board_i;
X
X            update_board(&tmp_board_j, tmp_cols_i[j], THEIRS);
X            DP(d, "\tUpdated board...\n");
X            DD print_board(&tmp_board_j, d);
X            col_values_j[j] = update_win_list(&tmp_win_j, tmp_cols_i[j], THEIRS);
X
X            if (col_values_j[j] >= attack_good[4]){
X                /* He gets a win if we do column i. fudge the value up. */
X                col_values_j[j] += (attack_good[4] << 1);
X            }
X
X            DP(d, "\this column %d gets value %d after update\n", j, col_values_j[j]);
X            for (k = 0; k < COLUMNS; k++){
X                tmp_cols_j[k] = tmp_cols_i[k];
X                col_values_k[k] = 0;
X            }
X            tmp_cols_j[j] -= 7;
X            for (k = 0; k < COLUMNS; k++){
X
X                if (tmp_cols_j[k] < 0){
X                    continue;
X                }
X
X                tmp_win_k = tmp_win_j;
X                tmp_board_k = tmp_board_j;
X                update_board(&tmp_board_k, tmp_cols_j[k], OURS);
X                col_values_k[k] = update_win_list(&tmp_win_k, tmp_cols_j[k], OURS);
X            }
X            max_k = 0;
X            for (k = 0; k < COLUMNS; k++){
X                if (col_values_k[k] > max_k){
X                    max_k = col_values_k[k];
X                }
X            }
X
X            col_values_j[j] -= max_k;
X        }
X
X        max_j = 0;
X        for (j = 0; j < COLUMNS; j++){
X            if (col_values_j[j] > max_j){
X                max_j = col_values_j[j];
X            }
X        }
X        DP(d, "the maximum reply for him has value %d\n", max_j);
X
X        col_values_i[i] -= max_j;
X        DP(d, "column %d reduced by %d to %d\n", i, max_j, col_values_i[i]);
X    }
X
X    DP(d, "All of my options have been calculated... ");
X    for (j = 0; j < COLUMNS; j++){
X        DP(d, "%2d ", col_values_i[j]);
X    }
X    DP(d, "\n");
X
X    best_col = -1;
X    max_i = -4 * attack_good[4];
X    for (i = 0; i < COLUMNS; i++){
X        if (next_in_col[i] >= 0 && col_values_i[i] > max_i){
X            max_i = col_values_i[i];
X            best_col = i;
X        }
X    }
X    DP(d, "The best column is %d\n", best_col);
X    DP(d, "Returning.\n\n\n", best_col);
X
X    if (best_col == -1){
X        print_game_record(CONFUSED);
X        plot_finish();
X        printf("Heavens above, I'm confused... Sorry.\n");
X        goodbye();
X
X    }
X
X    return best_col;
X}
X
X
Xvoid
Xask_help()
X{
X    char line[128];
X    char *cp;
X    struct passwd *pwd;
X    register int i;
X
X    
X    if (!(pwd = getpwuid(getuid())))
X        return;
X
X    u_name = pwd->pw_name;
X
X    for (i = 0; experts[i][0]; i++){
X        if (!strcmp(u_name, experts[i])){
X            return;
X        }
X    }
X
X    printf("Do you need help (n/y)? -> ");
X    if (!gets(line)){
X        fprintf(stderr, "Could not read input line\n");
X        goodbye();
X    }
X
X    cp = line;
X
X    while (*cp == ' ' || *cp == '\t') cp++;
X
X    if (*cp != 'y' && *cp != 'Y') return;
X    print_help();
X}
X
Xvoid
Xprint_help()
X{
X    fprintf(stderr, "The columns are numbered 0 to 6 from left to right.\n");
X    fprintf(stderr, "To enter a move just type the column number.\n");
X    fprintf(stderr, "My tiles will be the X'd ones.\n");
X    fprintf(stderr, "'q' or 'x' on your move will quit the game early.\n");
X    fprintf(stderr, "^L will redraw the screen.\n");
X	fprintf(stderr, "Invoke with -h for this help, -1 to go 1st, -2 to go 2nd.\n");
X}
X
X
Xvoid
Xopen_scorefile(mode)
Xchar *mode;
X{
X    struct passwd *pwd;
X    char file[256];
X
X#ifndef SCOREFILE
X    pwd = getpwuid(geteuid());
X    sprintf(file, "%s/.connect4rc", pwd->pw_dir);
X#else
X	strcpy(file, SCOREFILE);
X#endif
X
X    scorefile = fopen(file, mode);
X    if (!scorefile){
X        fprintf(stderr, "Can't open score file (%s)\n", file);
X        exit(1);
X    }
X
X    if (setuid(getuid())){
X        fprintf(stderr, "Can't set permissions correctly!  Exiting!\n");
X        exit(0);
X    }
X
X    if (setgid(getgid())){
X        fprintf(stderr, "Can't set group id correctly!  Exiting!\n");
X        exit(0);
X    }
X}
X
Xchar *
Xdate()
X{
X    /* return pointer to NULL terminated date string */
X
X    extern char *index();
X    static char time[26];
X    struct timeval v;
X    struct timezone z;
X    char *nl;
X
X    gettimeofday(&v, &z);
X    sprintf(time,"%s", ctime(&v.tv_sec));
X
X    if ((nl = index(time, '\n')) == NULL){
X        fprintf(stderr,"date: ctime returned non-newline terminated string.\n");
X        exit(1);
X    }
X
X    *nl = '\0';
X    return time;
X}
X
Xvoid
Xdo_args(argc, argv)
Xint argc;
Xchar **argv;
X{
X    myname = argv[0];
X    argv++;
X
X    while (--argc){
X        if (**argv == '-'){
X            switch (argv[0][1]){
X                case 'S':{
X                    do_scores();
X                    goodbye();
X                }
X                case 'h':{
X                    print_help();
X                    goodbye();
X                }
X				case 'v':{
X					printf("Version %s\n", version);
X					printf("Released : %s\n", release_date);
X					goodbye();
X				}
X				case '1':{
X					turn = THEIRS;
X					break;
X				}
X				case '2':{
X					turn = OURS;
X					break;
X				}
X            }
X        }
X        argv++;
X    }
X}
X
X
Xvoid
Xdo_scores()
X{
X    register int c;
X
X    open_scorefile("r");
X    while ((c = getc(scorefile)) != EOF)
X        putchar(c);
X}
X
X
Xint
Xcount(w, n, who)
Xwin_list_t *w;
Xu_char n;
Xint who;
X{
X    register u_char *list = who == OURS ? w->us : w->them;
X    register int i;
X    register int total = 0;
X
X    for (i = 0; i < WINS; i++){
X        if (list[i] == n){
X            total++;
X        }
X    }
X
X    return total;
X}
X
Xvoid
Xbell()
X{
X    putchar(7);
X}
X
Xvoid
Xdebug_on()
X{
X    if (d) return;
X    if (save){
X        d = save;
X        fprintf(d, "\n\nDEBUG RESUMED after move %d\n\n", moves_played);
X        report("debug resumed");
X    }
X    else{
X        struct passwd *pwd = getpwnam(wizard);
X        if (!pwd || getuid() != pwd->pw_uid){
X            return;
X        }
X        else{
X            d = fopen("debug", "w");
X            if (!d){
X                report("debug could be started (fopen fails)");
X            }
X            else{
X                fprintf(d, "\n\nDEBUG ON after move %d\n\n", moves_played);
X                report("debug on");
X            }
X        }
X    }
X    accept_move(turn);
X}
X
Xvoid
Xdebug_off()
X{
X    if (!d) return;
X    fprintf(d, "\n\nDEBUG OFF after move %d\n\n", moves_played);
X    report("debug off");
X    fflush(d);
X    save = d;
X    d = NULL;
X    accept_move(turn);
X}
X
X/* 
X * Modify the environment to put message in the right place for
X * the w and ps commands to find.  Code stolen from MFCF lock
X * program, originally by Ian!
X *
X */
X
Xvoid
Xenvmesg(environ, message)
Xchar **environ;
Xchar *message;
X{
X    /* 
X	 * Note that this clobbers the environment, so we have to
X     * do it last, after all the getenv and termcap calls.
X     */
X
X    char *last, *address;
X    while( environ[1] != 0 ) ++environ;
X    last = environ[0] + strlen(environ[0]);/* address of '\0' */
X    last = (char *)((int)last&(~03));/* word boundary */
X    *(int *)last = 0;       /* clean out last word */
X    address = last +3 -strlen(message);
X    address = (char *)((int)address&(~03));/* word boundary */
X    *(int *)(address-4) = 0;    /* clean out word below */
X    *(int *)(address-8) = 0;    /* clean out word below */
X    strcpy( address, message );
X}
X
END_OF_FILE
if test 18926 -ne `wc -c <'c4.c'`; then
    echo shar: \"'c4.c'\" unpacked with wrong size!
fi
# end of 'c4.c'
fi
if test -f 'c4.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'c4.h'\"
else
echo shar: Extracting \"'c4.h'\" \(1221 characters\)
sed "s/^X//" >'c4.h' <<'END_OF_FILE'
X/*
X * c4.h
X *
X */
X
X#define WINS 69
X#define SQUARES 42
X#define ROWS 6
X#define COLUMNS 7
X
X#define GAME_OVER -1
X#define OURS 0
X#define THEIRS 1
X#define EMPTY 2
X#define DRAW 3
X#define QUIT 4
X#define CONFUSED 5
X
X#define OUR_SYM "+"
X#define THEIR_SYM "*"
X#define BLANK_SYM " "
X
Xextern char *date();
Xextern int ask_turn();
Xextern int count();
Xextern int get_move();
Xextern int make();
Xextern int opportune();
Xextern int stop();
Xextern int think_of_fucking_clever_move();
Xextern int update_column();
Xextern int update_win_list();
Xextern void accept_move();
Xextern void ask_help();
Xextern void bell();
Xextern void debug_off();
Xextern void debug_on();
Xextern void do_args();
Xextern void do_scores();
Xextern void envmesg();
Xextern void fill_column();
Xextern void fill_square();
Xextern void goodbye();
Xextern void init_board_t();
Xextern void init_screen();
Xextern void init_win_list_t();
Xextern void open_scorefile();
Xextern void plot_finish();
Xextern void plot_screen();
Xextern void print_board();
Xextern void print_game_record();
Xextern void print_help();
Xextern void print_in_what_wins();
Xextern void report();
Xextern void reset_row_levels();
Xextern void show_move();
Xextern void update_board();
Xextern void update_game_record();
END_OF_FILE
if test 1221 -ne `wc -c <'c4.h'`; then
    echo shar: \"'c4.h'\" unpacked with wrong size!
fi
# end of 'c4.h'
fi
if test -f 'screen.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'screen.c'\"
else
echo shar: Extracting \"'screen.c'\" \(3076 characters\)
sed "s/^X//" >'screen.c' <<'END_OF_FILE'
X#include <stdio.h>
X#include <curses.h>
X#include "c4.h"
X
X#define  BASEY 0
X#define  BASEX 0 
X#define  LINE0  "    0       1       2       3       4       5       6    "
X#define  LINE1  ".-------------------------------------------------------."
X#define  LINE2  "|       |       |       |       |       |       |       |"
X#define  LINE3  "|-------+-------+-------+-------+-------+-------+-------|"
X#define  LINE4  "`-------------------------------------------------------'"
X
Xchar  *filler[] = {"XXXXXXX", "OOOOOOO" };
X
Xstatic int row_levels[] = {5, 5, 5, 5, 5, 5, 5};
X
Xvoid
Xinit_screen()
X{
X    initscr();
X    crmode();
X    noecho();
X    plot_screen();
X}
X
Xvoid
Xplot_screen()
X{
X    register int i;
X    register int j;
X
X    clear();
X    move(0,60);
X    standout();
X    printw("C O N N E C T   4");
X    standend();
X    move(4,60);
X    printw("  YOUR MOVE:  ");
X    move(7,60);
X    printw("    MY MOVE:  ");
X    move(BASEY,BASEX);
X    printw(LINE0);
X    move(BASEY+1,BASEX);
X    printw(LINE1);
X    for (i = 0; i < 6; i++){
X        for (j = 0; j < 2; j++){
X            move(BASEY + 2 + 3*i + j , BASEX);
X            printw(LINE2);
X        }
X        move(BASEY + 4 + 3*i , BASEX);
X        printw(LINE3);
X    }
X    move(BASEY + 19, BASEX);
X    printw(LINE4);  
X    refresh();
X}
X
Xvoid
Xshow_move(column, who)
Xint column;
Xint who;
X{
X    int row;
X    int other_row;
X
X    if (who == OURS){
X        row = 7;
X        other_row = 4;
X    }
X    else{
X        row = 4;
X        other_row = 7;
X    }
X
X    move(row, 73);
X    printw("%1d",column);
X    move(other_row, 73);
X    printw(" ");
X    move(other_row, 73);
X    refresh();
X} 
X
Xvoid
Xaccept_move(who)
Xint who;
X{
X    int row;
X
X    if (who == OURS)
X        row = 7;
X    else
X        row = 4;
X
X    move(row, 73);
X    printw(" ");
X    move(row, 73);
X    refresh();
X}
X
X
X/* VARARGS1 */
Xvoid
Xreport(fmt, a, b, c, d, e, f)
Xchar *fmt;
Xchar *a, *b, *c, *d, *e, *f;
X{
X    move(22, 0);
X    printw(fmt, a, b, c, d, e, f);
X    refresh();
X}
X
Xvoid
Xplot_finish()
X{
X    move(23,0);
X    refresh();
X    nocrmode();
X    echo();
X}
X
X
Xvoid
Xfill_column(column, colour)
Xint column;
Xint colour;
X{
X    register int i;
X    register int row;
X
X    row = row_levels[column];
X    row_levels[column]--;
X
X    for (i = 2; i < 4; i++){
X        move(BASEY + i + 3*row, BASEX + 1 + 8*column);
X        if (colour == OURS) {
X            standout();
X            printw(filler[colour]);
X            standend();
X        }
X        else{
X            printw(filler[colour]);
X        }
X    }
X    refresh();
X}
X
Xvoid
Xfill_square(square, colour)
Xint square;
Xint colour;
X{
X    register int i;
X    register int row;
X    register int column;
X
X    row = square / 7;
X    column = square % 7;
X
X    for (i = 2; i < 4; i++){
X        move(BASEY + i + 3*row, BASEX + 1 + 8*column);
X        if (colour == OURS) {
X            standout();
X            printw(filler[colour]);
X            standend();
X        }
X        else{
X            printw(filler[colour]);
X        }
X    }
X    refresh();
X}
X
X
Xvoid
Xreset_row_levels()
X{
X    register int i;
X
X    for (i = 0; i < COLUMNS; i++){
X        row_levels[i] = 5;
X    }
X}
END_OF_FILE
if test 3076 -ne `wc -c <'screen.c'`; then
    echo shar: \"'screen.c'\" unpacked with wrong size!
fi
# end of 'screen.c'
fi
if test -f 'tables.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tables.h'\"
else
echo shar: Extracting \"'tables.h'\" \(7047 characters\)
sed "s/^X//" >'tables.h' <<'END_OF_FILE'
Xu_char dist_counts[SQUARES] = {
X     3,  4,  5,  7,  5,  4,  3,    /* Row 0 */
X     4,  6,  8, 10,  8,  6,  4,    /* Row 1 */
X     5,  8, 11, 13, 11,  8,  5,    /* Row 2 */
X     5,  8, 11, 13, 11,  8,  5,    /* Row 3 */
X     4,  6,  8, 10,  8,  6,  4,    /* Row 4 */
X     3,  4,  5,  7,  5,  4,  3     /* Row 5 */
X};
X
Xu_char win_in_what[WINS][4] = {
X    {  0,  1,  2,  3 },   /* Win  0 */
X    {  1,  2,  3,  4 },   /* Win  1 */
X    {  2,  3,  4,  5 },   /* Win  2 */
X    {  3,  4,  5,  6 },   /* Win  3 */
X    {  7,  8,  9, 10 },   /* Win  4 */
X    {  8,  9, 10, 11 },   /* Win  5 */
X    {  9, 10, 11, 12 },   /* Win  6 */
X    { 10, 11, 12, 13 },   /* Win  7 */
X    { 14, 15, 16, 17 },   /* Win  8 */
X    { 15, 16, 17, 18 },   /* Win  9 */
X    { 16, 17, 18, 19 },   /* Win 10 */
X    { 17, 18, 19, 20 },   /* Win 11 */
X    { 21, 22, 23, 24 },   /* Win 12 */
X    { 22, 23, 24, 25 },   /* Win 13 */
X    { 23, 24, 25, 26 },   /* Win 14 */
X    { 24, 25, 26, 27 },   /* Win 15 */
X    { 28, 29, 30, 31 },   /* Win 16 */
X    { 29, 30, 31, 32 },   /* Win 17 */
X    { 30, 31, 32, 33 },   /* Win 18 */
X    { 31, 32, 33, 34 },   /* Win 19 */
X    { 35, 36, 37, 38 },   /* Win 20 */
X    { 36, 37, 38, 39 },   /* Win 21 */
X    { 37, 38, 39, 40 },   /* Win 22 */
X    { 38, 39, 40, 41 },   /* Win 23 */
X    {  0,  7, 14, 21 },   /* Win 24 */
X    {  1,  8, 15, 22 },   /* Win 25 */
X    {  2,  9, 16, 23 },   /* Win 26 */
X    {  3, 10, 17, 24 },   /* Win 27 */
X    {  4, 11, 18, 25 },   /* Win 28 */
X    {  5, 12, 19, 26 },   /* Win 29 */
X    {  6, 13, 20, 27 },   /* Win 30 */
X    {  7, 14, 21, 28 },   /* Win 31 */
X    {  8, 15, 22, 29 },   /* Win 32 */
X    {  9, 16, 23, 30 },   /* Win 33 */
X    { 10, 17, 24, 31 },   /* Win 34 */
X    { 11, 18, 25, 32 },   /* Win 35 */
X    { 12, 19, 26, 33 },   /* Win 36 */
X    { 13, 20, 27, 34 },   /* Win 37 */
X    { 14, 21, 28, 35 },   /* Win 38 */
X    { 15, 22, 29, 36 },   /* Win 39 */
X    { 16, 23, 30, 37 },   /* Win 40 */
X    { 17, 24, 31, 38 },   /* Win 41 */
X    { 18, 25, 32, 39 },   /* Win 42 */
X    { 19, 26, 33, 40 },   /* Win 43 */
X    { 20, 27, 34, 41 },   /* Win 44 */
X    {  0,  8, 16, 24 },   /* Win 45 */
X    {  1,  9, 17, 25 },   /* Win 46 */
X    {  2, 10, 18, 26 },   /* Win 47 */
X    {  3, 11, 19, 27 },   /* Win 48 */
X    {  3,  9, 15, 21 },   /* Win 49 */
X    {  4, 10, 16, 22 },   /* Win 50 */
X    {  5, 11, 17, 23 },   /* Win 51 */
X    {  6, 12, 18, 24 },   /* Win 52 */
X    {  7, 15, 23, 31 },   /* Win 53 */
X    {  8, 16, 24, 32 },   /* Win 54 */
X    {  9, 17, 25, 33 },   /* Win 55 */
X    { 10, 18, 26, 34 },   /* Win 56 */
X    { 10, 16, 22, 28 },   /* Win 57 */
X    { 11, 17, 23, 29 },   /* Win 58 */
X    { 12, 18, 24, 30 },   /* Win 59 */
X    { 13, 19, 25, 31 },   /* Win 60 */
X    { 14, 22, 30, 38 },   /* Win 61 */
X    { 15, 23, 31, 39 },   /* Win 62 */
X    { 16, 24, 32, 40 },   /* Win 63 */
X    { 17, 25, 33, 41 },   /* Win 64 */
X    { 17, 23, 29, 35 },   /* Win 65 */
X    { 18, 24, 30, 36 },   /* Win 66 */
X    { 19, 25, 31, 37 },   /* Win 67 */
X    { 20, 26, 32, 38 }    /* Win 68 */
X};
X
X
Xu_char in_what_wins[] = {
X
X     0, 24, 45, -1,                                          /* Square 0 */
X     0,  1, 25, 46, -1,                                      /* Square 1 */
X     0,  1,  2, 26, 47, -1,                                  /* Square 2 */
X     0,  1,  2,  3, 27, 48, 49, -1,                          /* Square 3 */
X     1,  2,  3, 28, 50, -1,                                  /* Square 4 */
X     2,  3, 29, 51, -1,                                      /* Square 5 */
X     3, 30, 52, -1,                                          /* Square 6 */
X
X     4, 24, 31, 53, -1,                                      /* Square 7 */
X     4,  5, 25, 32, 45, 54, -1,                              /* Square 8 */
X     4,  5,  6, 26, 33, 46, 49, 55, -1,                      /* Square 9 */
X     4,  5,  6,  7, 27, 34, 47, 50, 56, 57, -1,              /* Square 10 */
X     5,  6,  7, 28, 35, 48, 51, 58, -1,                      /* Square 11 */
X     6,  7, 29, 36, 52, 59, -1,                              /* Square 12 */
X     7, 30, 37, 60, -1,                                      /* Square 13 */
X
X     8, 24, 31, 38, 61, -1,                                  /* Square 14 */
X     8,  9, 25, 32, 39, 49, 53, 62, -1,                      /* Square 15 */
X     8,  9, 10, 26, 33, 40, 45, 50, 54, 57, 63, -1,          /* Square 16 */
X     8,  9, 10, 11, 27, 34, 41, 46, 51, 55, 58, 64, 65, -1,  /* Square 17 */
X     9, 10, 11, 28, 35, 42, 47, 52, 56, 59, 66, -1,          /* Square 18 */
X    10, 11, 29, 36, 43, 48, 60, 67, -1,                      /* Square 19 */
X    11, 30, 37, 44, 68, -1,                                  /* Square 20 */
X
X    12, 24, 31, 38, 49, -1,                                  /* Square 21 */
X    12, 13, 25, 32, 39, 50, 57, 61, -1,                      /* Square 22 */
X    12, 13, 14, 26, 33, 40, 51, 53, 58, 62, 65, -1,          /* Square 23 */
X    12, 13, 14, 15, 27, 34, 41, 45, 52, 54, 59, 63, 66, -1,  /* Square 24 */
X    13, 14, 15, 28, 35, 42, 46, 55, 60, 64, 67, -1,          /* Square 25 */
X    14, 15, 29, 36, 43, 47, 56, 68, -1,                      /* Square 26 */
X    15, 30, 37, 44, 48, -1,                                  /* Square 27 */
X
X    16, 31, 38, 57, -1,                                      /* Square 28 */
X    16, 17, 32, 39, 58, 65, -1,                              /* Square 29 */
X    16, 17, 18, 33, 40, 59, 61, 66, -1,                      /* Square 30 */
X    16, 17, 18, 19, 34, 41, 53, 60, 62, 67, -1,              /* Square 31 */
X    17, 18, 19, 35, 42, 54, 63, 68, -1,                      /* Square 32 */
X    18, 19, 36, 43, 55, 64, -1,                              /* Square 33 */
X    19, 37, 44, 56, -1,                                      /* Square 34 */
X
X    20, 38, 65, -1,                                          /* Square 35 */
X    20, 21, 39, 66, -1,                                      /* Square 36 */
X    20, 21, 22, 40, 67, -1,                                  /* Square 37 */
X    20, 21, 22, 23, 41, 61, 68, -1,                          /* Square 38 */
X    21, 22, 23, 42, 62, -1,                                  /* Square 39 */
X    22, 23, 43, 63, -1,                                      /* Square 40 */
X    23, 44, 64, -1                                           /* Square 41 */
X};
X
Xu_short cum_index[SQUARES] = {
X      0,   4,   9,  15,  23,  29,  34,  /* Squares  0 to  6 */
X     38,  43,  50,  59,  70,  79,  86,  /* Squares  7 to 13 */
X     91,  97, 106, 118, 132, 144, 153,  /* Squares 14 to 20 */
X    159, 165, 174, 186, 200, 212, 221,  /* Squares 21 to 27 */
X    227, 232, 239, 248, 259, 268, 275,  /* Squares 28 to 34 */
X    280, 284, 289, 295, 303, 309, 314   /* Squares 35 to 41 */
X};
X
Xint next_in_col[COLUMNS] = {
X    35, 36, 37, 38, 39, 40, 41
X};
X
Xint attack_good[] = {
X    0, 1, 25, 500, 100000
X};
X
Xint defend_good[] = {
X    0, 1, 20, 400, 10000
X};
X
Xchar *experts[] = {
X    "tcjones",
X    "ljpoconnor",
X    "pjyamamoto",
X    "gjerawlins",
X    "jshen",
X    "rvklassen",
X	"vlestivill",
X	"rastroobossc",
X    ""
X};
END_OF_FILE
if test 7047 -ne `wc -c <'tables.h'`; then
    echo shar: \"'tables.h'\" unpacked with wrong size!
fi
# end of 'tables.h'
fi
if test -f 'types.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'types.h'\"
else
echo shar: Extracting \"'types.h'\" \(132 characters\)
sed "s/^X//" >'types.h' <<'END_OF_FILE'
Xtypedef struct {
X    u_char us[WINS];
X    u_char them[WINS];
X} win_list_t;
X
Xtypedef struct {
X    u_char square[SQUARES];
X} board_t;
END_OF_FILE
if test 132 -ne `wc -c <'types.h'`; then
    echo shar: \"'types.h'\" unpacked with wrong size!
fi
# end of 'types.h'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0