|
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 c
Length: 18926 (0x49ee) Types: TextFile Names: »c4.c.orig«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Connect4/c4.c.orig«
/* * 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 * */ #include <stdio.h> #include <sys/types.h> #include <ctype.h> #include <pwd.h> #include <sys/time.h> #include <sys/file.h> #include <sys/param.h> #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"; 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(); 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"; } } if (gethostname(host, sizeof(host)) == -1){ host[0] = '\0'; strcat(host, "Unknown"); } if (flock(fileno(scorefile), LOCK_EX) == -1){ fprintf(stderr, "Could not lock scorefile!\n"); return; } if (fseek(scorefile, 0L, 2) == -1){ fprintf(stderr, "Could not unlock scorefile!\n"); if (flock(fileno(scorefile), LOCK_UN) == -1){ 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"); if (flock(fileno(scorefile), LOCK_UN) == -1){ 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 time[26]; struct timeval v; struct timezone z; char *nl; gettimeofday(&v, &z); sprintf(time,"%s", ctime(&v.tv_sec)); if ((nl = index(time, '\n')) == NULL){ fprintf(stderr,"date: ctime returned non-newline terminated string.\n"); exit(1); } *nl = '\0'; return time; } 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 ); }