DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T c

⟦f772aa7a3⟧ TextFile

    Length: 19990 (0x4e16)
    Types: TextFile
    Names: »c4.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Connect4/c4.c« 

TextFile

/*
 * c4.h - Connect Four game using curses.
 *
 *
 * Terry Jones. December 28, 1988.
 *
 *     Department Of Computer Science,  University Of Waterloo
 *     Waterloo Ontario Canada N2L 3G1. Phone: 1-519-8884674
 *     UUCP:                    ...!watmath!watdragon!tcjones
 *     CSNET, Internet, CDNnet: tcjones@dragon.waterloo.{cdn,edu}
 *     BITNET:                  tcjones@WATER.bitnet
 *     Canadian domain:         tcjones@dragon.uwaterloo.ca
 *
 * Patched for SysV Rel3.1 by Tim Ramsey on 27 April 89.
 *   ..!rutgers!ksuvax1!tar, tar@ksuvax1.cis.ksu.edu
 */

#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include <pwd.h>

#define SYSV  /* TAR: could do this in Makefile but screen.c would complain */

#ifdef SYSV  /* TAR */
# include <unistd.h>  /* For lockf() */
# include <sys/utsname.h>  /* For uname() */

# define index strchr
# define u_char unchar
# define u_short ushort

struct passwd *getpwuid(), *getpwnam();

#else  /* !SYSV */

# include <sys/time.h>
# include <sys/file.h>
# include <sys/param.h>
#endif  /* SYSV */

#include "c4.h"
#include "types.h"
#include "tables.h"

#define DP if (d) fprintf
#define DD if (d)
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64	/* from sys/param.h on vanilla 4.3bsd */
#endif

win_list_t win_list;
board_t board;
int moves_played = 0;
char *myname;
u_char game_record[SQUARES];
FILE *scorefile;
FILE *d = NULL;
FILE *save = NULL;
char *u_name = "Bozo";
char *version = "3.2";
char *release_date = "8pm, January 1st, 1988";
int turn = GAME_OVER;
int first;
char wizard[] = "tcjones";  /* TAR: added "[]" */

main(argc, argv, envp)
int argc;
char **argv;
char **envp;
{
    register int move;
    register int square;
    register int winner = DRAW;
	int temp;

    envmesg(envp, "Connect Four");
    do_args(argc, argv);
    open_scorefile("a");
    ask_help();
    first = turn = turn == GAME_OVER ? ask_turn() : turn;
    init_win_list_t(&win_list);
    init_board_t(&board);
    init_screen();
    accept_move(turn);

    for (;;){
        if (moves_played == SQUARES){
            turn = GAME_OVER;
        }

        DP(d, "\n<<< M O V E  =  %d >>>\n", moves_played + 1);

        if (turn == GAME_OVER) break;

        if (turn == THEIRS){
            move = get_move();
        }
        else{
            move = think_of_fucking_clever_move();
        }

        square = update_column(move);

        if (square < 0){
            bell();
            report("That column is full!");
            if (turn == OURS){
                winner = CONFUSED;
                turn = GAME_OVER;
                report("column %2d, square %2d", move, square);
            }
            accept_move(turn);
            continue;
        }

        fill_column(move, turn);
        show_move(move, turn);

        update_game_record(move);
        update_board(&board, square, turn);
        update_win_list(&win_list, square, turn);
        if ((temp = count(&win_list, (u_char) 4, turn))){
			DP(d, "Temp from count is %d\n", temp);
            winner = turn;
            turn = GAME_OVER;
        }
        else{
            turn = turn == OURS ? THEIRS : OURS;
        }
    }
    plot_finish();
    print_game_record(winner);
    goodbye();
}

void
init_win_list_t(w_list)
win_list_t *w_list;
{
    register int i;

    for (i = 0; i < WINS; i++){
        w_list->us[i] = w_list->them[i] = (u_char) 0;
    }
}

void
init_board_t(bd)
board_t *bd;
{
    register int i;

    for (i = 0; i < SQUARES; i++){
            bd->square[i] = EMPTY;
    }
}


void
print_in_what_wins()
{
    register int i;
    for (i = 0; i < SQUARES; i++){
        int index = cum_index[i];
        printf("Sq %2d (offset = %3d) :", i, index);
        while (in_what_wins[index] != (u_char) -1){
            printf(" %d", in_what_wins[index++]);
        }
        printf("\n");
    }
}


int
get_move()
{
    register int move;

    get: move = getchar();

	switch(move){

		/* COLUMN # */
		case '0': case '1': case '2': case '3': case '4': case '5': case '6':{
    		return move - '0';
		}

		/* QUIT */
    	case 'q' :
		case 'x' :{
        	print_game_record(QUIT);
        	plot_finish();
        	printf("Chicken...\n");
        	goodbye();
    	}

		case 'v':{
			report("Version %s. Released %s", version, release_date);
			accept_move(turn);
			goto get;
		}

		/* DEBUG */
    	case 'd':{
        	if (d)
            	debug_off();
        	else
            	debug_on();
        	goto get;
    	}

		/* CONTROL L (redraw screen) */
    	case 0xc:{
			register int i;
        	plot_screen();
        	reset_row_levels();
        	turn = first;
        	for (i = 0; i < moves_played; i++){
            	fill_column(game_record[i], turn);
            	turn = turn == OURS ? THEIRS : OURS;
        	}
        	accept_move(turn);
        	goto get;
    	}

		/* RUBBISH */
		default:{
			goto get;
		}
	}
}

int
ask_turn()
{
    char line[128];
    register char *cp;

    printf("Would you like to go first? (yes/no) -> ");
    if (!gets(line)){
        plot_finish();
        fprintf(stderr, "Could not read input line.\n");
        goodbye();
    }

    cp = line;
    while (*cp == ' ' || *cp == '\t') cp++;

    if (*cp == 'n' || *cp == 'N')
        return OURS;
    return THEIRS;
}

int
update_column(col)
int col;
{
    int square = next_in_col[col];
    next_in_col[col] -= 7;
    return square;
}

void
update_game_record(move)
int move;
{
    game_record[moves_played] = move;
    moves_played++;
}

void
print_game_record(winner)
int winner;
{
    register int i;
    char host[MAXHOSTNAMELEN];
    char *win_str = "won";
    extern char *date();

#ifdef SYSV  /* TAR */
    struct utsname sysname;
#endif

    bell();
    switch (winner){
        case OURS:{
            printf("I won after %d moves.\n", moves_played);
            break;
        }
        case THEIRS:{
            printf("I lost after %d moves.\n", moves_played);
            win_str = "lost";
            break;
        }
        case QUIT:{
            win_str = "quit";
            break;
        }
        case DRAW:{
            printf("The game is a draw!\n");
            win_str = "drew";
            break;
        }
        default:
        case CONFUSED:{
            win_str = "confused";
        }
    }

#ifdef SYSV  /* TAR */
    if (uname(&sysname) != -1){
	strcpy(host, sysname.nodename);
    }
    else{
#else
    if (gethostname(host, sizeof(host)) == -1){
#endif
        host[0] = '\0';
        strcat(host, "Unknown");
    }

#ifdef SYSV  /* TAR */
    if (lockf(fileno(scorefile), F_LOCK, 0) == -1){
#else
    if (flock(fileno(scorefile), LOCK_EX) == -1){
#endif
        fprintf(stderr, "Could not lock scorefile!\n");
        return;
    }

	if (fseek(scorefile, 0L, 2) == -1){
		fprintf(stderr, "Could not unlock scorefile!\n");
#ifdef SYSV  /* TAR */
		if (lockf(fileno(scorefile), F_ULOCK, 0) == -1){
#else
		if (flock(fileno(scorefile), LOCK_UN) == -1){
#endif
			fprintf(stderr, "Could not unlock scorefile!\n");
		}
		return;
	}

    fprintf(scorefile, "Opponent   : %s@%s\n", u_name, host);
    fprintf(scorefile, "Date       : %s [version %s]\n", date(), version);
    fprintf(scorefile, "Result     : %s\n", win_str);
    fprintf(scorefile, "First Move : %s\n", first == OURS ? "Me" : "Them");
    fprintf(scorefile, "Moves      : %d\n", moves_played);
    fprintf(scorefile, "Record     : ");

    for (i = 0; i < moves_played && i < 21; i++){
        fprintf(scorefile, "%d ", game_record[i]);
    }
    if (moves_played > 21)
        fprintf(scorefile, "\n             ");
    for (i = 21; i < moves_played; i++){
        fprintf(scorefile, "%d ", game_record[i]);
    }
    fprintf(scorefile, "\n\n");

#ifdef SYSV  /* TAR */
    if (lockf(fileno(scorefile), F_ULOCK, 0) == -1){
#else
    if (flock(fileno(scorefile), LOCK_UN) == -1){
#endif
        fprintf(stderr, "Could not unlock scorefile!\n");
        return;
    }
}

void
update_board(bd, sq, whose)
board_t *bd;
int sq;
int whose;
{
    bd->square[sq] = whose;
}

int
update_win_list(w_list, sq, whose)
win_list_t *w_list;
int sq;
int whose;
{
    /*
     * Update the win list specified by 'whose' and return a measure of
     * the value of this move.
     */
    register int index = (int) cum_index[sq];
    register u_char *my_list;
    register u_char *their_list;
    register int goodness = 0;
    register int win;
    int a_good[5];
    int d_good[5];
    int i;

    for (i = 0; i < 5; i++){
        a_good[i] = attack_good[i];
        d_good[i] = defend_good[i];
    }

    if (whose == OURS){
        my_list = w_list->us;
        their_list = w_list->them;
    }
    else{
        my_list = w_list->them;
        their_list = w_list->us;
    }

    DP(d, "\t\tUpdate for square %d, turn = %d\n", sq, turn);
    while((win = (int)in_what_wins[index]) != (u_char) -1){

        if (their_list[win] == 0){
            /* We just put something into a win in which they have none. */
            goodness += a_good[my_list[win] + 1];
            DP(d, "\t\tmake a %d in win %d", my_list[win] + 1, win);
            DP(d, "\t\tgood+=%d goodness=%d\n", a_good[my_list[win] + 1],
                goodness);
            if (my_list[win])
                a_good[my_list[win] + 1] = 0;
        }
        else if (my_list[win] == 0){
            /* We just closed off a win for them. */
            goodness += d_good[their_list[win] + 1];
            DP(d, "\t\tstop a %d in win %d", their_list[win] + 1, win);
            DP(d, "\t\tgood+=%d goodness=%d\n", d_good[their_list[win]+1],
                goodness);
            if (their_list[win])
                d_good[their_list[win] + 1] = 0;
        }
        else{
            DP(d, "\t\tNothing for win %d\n", win);
        }

        my_list[win]++;
        index++;
    }
    return goodness;
}

void
print_board(bd, f)
board_t *bd;
FILE *f;
{
    register int row = 0;
    register int col = 0;

    fprintf(f, "\t 0123456\n");

    for (row = 0; row < ROWS; row++){
        fprintf(f, "\t%d", row);

        for (col = 0; col < COLUMNS; col++){

            switch(bd->square[row * COLUMNS + col]){
                case OURS:{
                    fprintf(f, OUR_SYM);
                    break;
                }
                case THEIRS:{
                    fprintf(f, THEIR_SYM);
                    break;
                }
                case EMPTY:{
                    fprintf(f, BLANK_SYM);
                    break;
                }
                default:{
                    fprintf(f, "fucking ugh! board screwup\n");
                    break;
                }
            }
        }

        fprintf(f, "%d\n", row);
    }
    fprintf(f, "\t 0123456\n");
}

void
goodbye()
{
    exit(1);
}

int
think_of_fucking_clever_move()
{
    register int i;
    register int j;
    register int k;
    win_list_t tmp_win_i;
    win_list_t tmp_win_j;
    win_list_t tmp_win_k;
    int tmp_cols_i[COLUMNS];
    int tmp_cols_j[COLUMNS];
    int col_values_i[COLUMNS];
    int col_values_j[COLUMNS];
    int col_values_k[COLUMNS];
    int max_i;
    int best_col;
    board_t tmp_board_i;
    board_t tmp_board_j;
    board_t tmp_board_k;

    for (i = 0; i < COLUMNS; i++){
        col_values_i[i] = 0;
    }

    DP(d, "CLEVER MOVE... board looks like\n");
    DD print_board(&board, d);
    DP(d, "---------------------------------\n");

    for (i = 0; i < COLUMNS; i++){

        register int max_j;

        DP(d, "my column %d\n", i);

        if (next_in_col[i] < 0){
        DP(d, "my column %d is full\n", i);
            continue;
        }

        tmp_win_i = win_list;
        tmp_board_i = board;

        update_board(&tmp_board_i, next_in_col[i], OURS);
        DP(d, "Updated board...\n");
        DD print_board(&tmp_board_i, d);
        col_values_i[i] = update_win_list(&tmp_win_i, next_in_col[i], OURS);

        if (col_values_i[i] >= attack_good[4])
            return i;

        DP(d, "my column %d gets value %d after update\n", i, col_values_i[i]);

        for (j = 0; j < COLUMNS; j++){
            tmp_cols_i[j] = next_in_col[j];
            col_values_j[j] = 0;
        }
        tmp_cols_i[i] -= 7;

        DP(d, "Temporary columns for inner loop... ");
        for (j = 0; j < COLUMNS; j++){
            DP(d, "%2d ", tmp_cols_i[j]);
        }
        DP(d, "\n");

        for (j = 0; j < COLUMNS; j++){

            register int max_k;

            DP(d, "\this column %d\n", j);
            if (tmp_cols_i[j] < 0){
                DP(d, "\this column %d is full\n", j);
                continue;
            }

            tmp_win_j = tmp_win_i;
            tmp_board_j = tmp_board_i;

            update_board(&tmp_board_j, tmp_cols_i[j], THEIRS);
            DP(d, "\tUpdated board...\n");
            DD print_board(&tmp_board_j, d);
            col_values_j[j] = update_win_list(&tmp_win_j, tmp_cols_i[j], THEIRS);

            if (col_values_j[j] >= attack_good[4]){
                /* He gets a win if we do column i. fudge the value up. */
                col_values_j[j] += (attack_good[4] << 1);
            }

            DP(d, "\this column %d gets value %d after update\n", j, col_values_j[j]);
            for (k = 0; k < COLUMNS; k++){
                tmp_cols_j[k] = tmp_cols_i[k];
                col_values_k[k] = 0;
            }
            tmp_cols_j[j] -= 7;
            for (k = 0; k < COLUMNS; k++){

                if (tmp_cols_j[k] < 0){
                    continue;
                }

                tmp_win_k = tmp_win_j;
                tmp_board_k = tmp_board_j;
                update_board(&tmp_board_k, tmp_cols_j[k], OURS);
                col_values_k[k] = update_win_list(&tmp_win_k, tmp_cols_j[k], OURS);
            }
            max_k = 0;
            for (k = 0; k < COLUMNS; k++){
                if (col_values_k[k] > max_k){
                    max_k = col_values_k[k];
                }
            }

            col_values_j[j] -= max_k;
        }

        max_j = 0;
        for (j = 0; j < COLUMNS; j++){
            if (col_values_j[j] > max_j){
                max_j = col_values_j[j];
            }
        }
        DP(d, "the maximum reply for him has value %d\n", max_j);

        col_values_i[i] -= max_j;
        DP(d, "column %d reduced by %d to %d\n", i, max_j, col_values_i[i]);
    }

    DP(d, "All of my options have been calculated... ");
    for (j = 0; j < COLUMNS; j++){
        DP(d, "%2d ", col_values_i[j]);
    }
    DP(d, "\n");

    best_col = -1;
    max_i = -4 * attack_good[4];
    for (i = 0; i < COLUMNS; i++){
        if (next_in_col[i] >= 0 && col_values_i[i] > max_i){
            max_i = col_values_i[i];
            best_col = i;
        }
    }
    DP(d, "The best column is %d\n", best_col);
    DP(d, "Returning.\n\n\n", best_col);

    if (best_col == -1){
        print_game_record(CONFUSED);
        plot_finish();
        printf("Heavens above, I'm confused... Sorry.\n");
        goodbye();

    }

    return best_col;
}


void
ask_help()
{
    char line[128];
    char *cp;
    struct passwd *pwd;
    register int i;

    
    if (!(pwd = getpwuid(getuid())))
        return;

    u_name = pwd->pw_name;

    for (i = 0; experts[i][0]; i++){
        if (!strcmp(u_name, experts[i])){
            return;
        }
    }

    printf("Do you need help (n/y)? -> ");
    if (!gets(line)){
        fprintf(stderr, "Could not read input line\n");
        goodbye();
    }

    cp = line;

    while (*cp == ' ' || *cp == '\t') cp++;

    if (*cp != 'y' && *cp != 'Y') return;
    print_help();
}

void
print_help()
{
    fprintf(stderr, "The columns are numbered 0 to 6 from left to right.\n");
    fprintf(stderr, "To enter a move just type the column number.\n");
    fprintf(stderr, "My tiles will be the X'd ones.\n");
    fprintf(stderr, "'q' or 'x' on your move will quit the game early.\n");
    fprintf(stderr, "^L will redraw the screen.\n");
	fprintf(stderr, "Invoke with -h for this help, -1 to go 1st, -2 to go 2nd.\n");
}


void
open_scorefile(mode)
char *mode;
{
    struct passwd *pwd;
    char file[256];

#ifndef SCOREFILE
    pwd = getpwuid(geteuid());
    sprintf(file, "%s/.connect4rc", pwd->pw_dir);
#else
	strcpy(file, SCOREFILE);
#endif

    scorefile = fopen(file, mode);
    if (!scorefile){
        fprintf(stderr, "Can't open score file (%s)\n", file);
        exit(1);
    }

    if (setuid(getuid())){
        fprintf(stderr, "Can't set permissions correctly!  Exiting!\n");
        exit(0);
    }

    if (setgid(getgid())){
        fprintf(stderr, "Can't set group id correctly!  Exiting!\n");
        exit(0);
    }
}

char *
date()
{
    /* return pointer to NULL terminated date string */

    extern char *index();
    static char timestr[26];  /* TAR */
    char *nl;

#ifdef SYSV  /* TAR */
    long secs;

    secs = time((long *) 0);
    sprintf(timestr,"%s", ctime(&secs));
#else
    struct timeval v;
    struct timezone z;

    gettimeofday(&v, &z);
    sprintf(timestr,"%s", ctime(&v.tv_sec));
#endif

    if ((nl = index(timestr, '\n')) == NULL){
        fprintf(stderr,"date: ctime returned non-newline terminated string.\n");
        exit(1);
    }

    *nl = '\0';
    return timestr;
}

void
do_args(argc, argv)
int argc;
char **argv;
{
    myname = argv[0];
    argv++;

    while (--argc){
        if (**argv == '-'){
            switch (argv[0][1]){
                case 'S':{
                    do_scores();
                    goodbye();
                }
                case 'h':{
                    print_help();
                    goodbye();
                }
				case 'v':{
					printf("Version %s\n", version);
					printf("Released : %s\n", release_date);
					goodbye();
				}
				case '1':{
					turn = THEIRS;
					break;
				}
				case '2':{
					turn = OURS;
					break;
				}
            }
        }
        argv++;
    }
}


void
do_scores()
{
    register int c;

    open_scorefile("r");
    while ((c = getc(scorefile)) != EOF)
        putchar(c);
}


int
count(w, n, who)
win_list_t *w;
u_char n;
int who;
{
    register u_char *list = who == OURS ? w->us : w->them;
    register int i;
    register int total = 0;

    for (i = 0; i < WINS; i++){
        if (list[i] == n){
            total++;
        }
    }

    return total;
}

void
bell()
{
    putchar(7);
}

void
debug_on()
{
    if (d) return;
    if (save){
        d = save;
        fprintf(d, "\n\nDEBUG RESUMED after move %d\n\n", moves_played);
        report("debug resumed");
    }
    else{
        struct passwd *pwd = getpwnam(wizard);
        if (!pwd || getuid() != pwd->pw_uid){
            return;
        }
        else{
            d = fopen("debug", "w");
            if (!d){
                report("debug could be started (fopen fails)");
            }
            else{
                fprintf(d, "\n\nDEBUG ON after move %d\n\n", moves_played);
                report("debug on");
            }
        }
    }
    accept_move(turn);
}

void
debug_off()
{
    if (!d) return;
    fprintf(d, "\n\nDEBUG OFF after move %d\n\n", moves_played);
    report("debug off");
    fflush(d);
    save = d;
    d = NULL;
    accept_move(turn);
}

/* 
 * Modify the environment to put message in the right place for
 * the w and ps commands to find.  Code stolen from MFCF lock
 * program, originally by Ian!
 *
 */

void
envmesg(environ, message)
char **environ;
char *message;
{
    /* 
	 * Note that this clobbers the environment, so we have to
     * do it last, after all the getenv and termcap calls.
     */

    char *last, *address;
    while( environ[1] != 0 ) ++environ;
    last = environ[0] + strlen(environ[0]);/* address of '\0' */
    last = (char *)((int)last&(~03));/* word boundary */
    *(int *)last = 0;       /* clean out last word */
    address = last +3 -strlen(message);
    address = (char *)((int)address&(~03));/* word boundary */
    *(int *)(address-4) = 0;    /* clean out word below */
    *(int *)(address-8) = 0;    /* clean out word below */
    strcpy( address, message );
}