|
|
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 );
}