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