diff options
Diffstat (limited to 'pgm7')
-rw-r--r-- | pgm7/TTT.c | 282 | ||||
-rw-r--r-- | pgm7/child.c | 75 | ||||
-rw-r--r-- | pgm7/child.h | 11 | ||||
-rw-r--r-- | pgm7/common.h | 68 | ||||
-rw-r--r-- | pgm7/makefile | 21 | ||||
-rw-r--r-- | pgm7/msg.c | 125 | ||||
-rw-r--r-- | pgm7/ttt.c | 386 | ||||
-rw-r--r-- | pgm7/ttt.tcl | 123 |
8 files changed, 1091 insertions, 0 deletions
diff --git a/pgm7/TTT.c b/pgm7/TTT.c new file mode 100644 index 0000000..cec9023 --- /dev/null +++ b/pgm7/TTT.c @@ -0,0 +1,282 @@ +/************************************************************************ + TTT.c + Castrated TTT server. No datagram queries, no timeouts. Assumes + completely well-behaved clients. Uses deprecated network address + translation. + + Phil Kearns + April 12, 1998 + Modified March, 2014 + + Adapted with GUI by Adam Carpenter - acarpent - acarpenter@email.wm.edu + Modified April 2017 +*************************************************************************/ + +#include "common.h" + +char board[9]; /* The tic-tac-toe board */ + +int main(int argc, char **argv) + +{ + int listener; /* fd for socket on which we get connection requests */ + int Xfd, Ofd; /* fds for sockets onto VCs to clients */ + + struct sockaddr_in s1, Xaddr, Oaddr; + int length, sfile, i, moveto, result, Xresult, Oresult; + unsigned short lport; + int currentmove; /* 0->X, 1->O */ + char hostid[128], Xhandle[32], Ohandle[32]; + struct tttmsg outmsg, inmsg; + + if (argc != 1) { + fprintf(stderr,"TTT:usage is TTT\n"); + exit(1); + } + + if ( (sfile = open(SFILE, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) { + perror("TTT:sfile"); + exit(1); + } + + if (gethostname(hostid,128) < 0){ + perror("TTT:gethostname"); + exit(1); + } + + i=0; + while (hostid[i] != '\0') + write(sfile,&hostid[i++], 1); + write(sfile,"\0", 1); + + if ( (listener = socket( AF_INET, SOCK_STREAM, 0 )) < 0 ) { + perror("TTT:socket"); + exit(1); + } + + bzero((char *) &s1, sizeof(s1)); + s1.sin_family = AF_INET; + s1.sin_addr.s_addr = INADDR_ANY; /* Any of this host's interfaces is OK. */ + s1.sin_port = 0; /* bind() will gimme unique port. */ + if (bind(listener, (struct sockaddr *)&s1, sizeof(s1)) < 0) { + perror("TTT:bind"); + exit(1); + } + + length = sizeof(s1); + if (getsockname(listener, (struct sockaddr *)&s1, (socklen_t *)&length) < 0) { + perror("TTT:getsockname"); + exit(1); + } + lport = s1.sin_port; + write(sfile, &lport, sizeof(unsigned short)); + close(sfile); + + listen(listener,5); + + length = sizeof(Xaddr); + if ((Xfd = accept(listener, (struct sockaddr *)&Xaddr, (socklen_t *)&length)) < 0) { + perror("TTT:accept X"); + exit(1); + } + + /* Send WHO to X */ + + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type = WHO; + putmsg(Xfd, &outmsg); + + /* Await HANDLE from X */ + + bzero((char *)&inmsg, sizeof(inmsg)); + getmsg(Xfd, &inmsg); + if (inmsg.type != HANDLE) protocol_error(HANDLE, &inmsg); + strncpy(Xhandle, inmsg.data, 31); + Xhandle[31] = '\0'; + + length = sizeof(Oaddr); + if ((Ofd = accept(listener, (struct sockaddr *)&Oaddr, (socklen_t *)&length)) < 0) { + perror("TTT:accept O"); + exit(1); + } + + /* Send WHO to O */ + + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type = WHO; + putmsg(Ofd, &outmsg); + + /* Await HANDLE from O */ + + bzero((char *)&inmsg, sizeof(inmsg)); + getmsg(Ofd, &inmsg); + if (inmsg.type != HANDLE) protocol_error(HANDLE, &inmsg); + strncpy(Ohandle, inmsg.data, 31); + Ohandle[31] = '\0'; + + /* WE HAVE A MATCH */ + + for(i=0; i<9; i++) board[i]=' '; + + /* Send MATCH to X */ + + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type=MATCH; + strncpy(outmsg.data, Ohandle, 31); + outmsg.data[31] = '\0'; + outmsg.board[0] = 'X'; + putmsg(Xfd, &outmsg); + + /* Send MATCH to O */ + + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type=MATCH; + strncpy(outmsg.data, Xhandle, 31); + outmsg.data[31] = '\0'; + outmsg.board[0] = 'O'; + putmsg(Ofd, &outmsg); + + while(1) { + + /* Send WHATMOVE to X */ + + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type=WHATMOVE; + for(i=0; i<9; i++) outmsg.board[i]=board[i]; + putmsg(Xfd, &outmsg); + + /* Await MOVE from X */ + + bzero((char *)&inmsg, sizeof(inmsg)); + getmsg(Xfd, &inmsg); + + // End the match if player X resigned. + if (inmsg.type == RESIGNED) { + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type=ABORT; + putmsg(Ofd, &outmsg); + exit(0); + } + else if (inmsg.type != MOVE) { + protocol_error(MOVE, &inmsg); + } + + currentmove = 0; /* X's move being reported*/ + moveto = inmsg.res & 0xF; + if (board[moveto] != ' ') { + fprintf(stderr, "TTT: invalid move\n"); + exit(1); + } + board[moveto] = 'X'; + if ((result = check_board(currentmove)) != 0) break; + + /* Send WHATMOVE to O */ + + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type=WHATMOVE; + for(i=0; i<9; i++) outmsg.board[i]=board[i]; + putmsg(Ofd, &outmsg); + + /* Await MOVE from O */ + + bzero((char *)&inmsg, sizeof(inmsg)); + getmsg(Ofd, &inmsg); + + // End the match if player O resigned. + if (inmsg.type == RESIGNED) { + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type=ABORT; + putmsg(Xfd, &outmsg); + exit(0); + } + else if (inmsg.type != MOVE) { + protocol_error(MOVE, &inmsg); + } + + if (inmsg.type != MOVE) protocol_error(MOVE, &inmsg); + currentmove = 1; /* O's move being reported*/ + moveto = inmsg.res & 0xF; + if (board[moveto] != ' ') { + fprintf(stderr, "TTT: invalid move\n"); + exit(1); + } + board[moveto] = 'O'; + if ((result = check_board(currentmove)) != 0) break; + } /* while */ + + /* Match is over, report the result to both clients */ + + if (result == 2) { + Xresult = Oresult = 'D'; + } + else { /* result must be 1, we have a winner */ + if (currentmove == 0) { Xresult = 'W'; Oresult = 'L';} + else { Xresult = 'L'; Oresult = 'W';} + } + + /* Send RESULT to X */ + + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type = RESULT; + outmsg.res = Xresult; + for(i=0; i<9; i++) outmsg.board[i]=board[i]; + putmsg(Xfd, &outmsg); + + /* Send RESULT to O */ + + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type = RESULT; + outmsg.res = Oresult; + for(i=0; i<9; i++) outmsg.board[i]=board[i]; + putmsg(Ofd, &outmsg); + + /* The game is finished */ + + return(0); +} + + +/************************************************************************ + check_board() returns 0 if no winner + 1 if winner + 2 if draw + Brute force. +************************************************************************/ +int +check_board(player) +int player; /* X is 0, O is 1 */ +{ + char match; + int i,spaces; + + match = (player?'O':'X'); + + if (((board[0] == match) && (board[1] == match) && (board[2] == match)) || + ((board[3] == match) && (board[4] == match) && (board[5] == match)) || + ((board[6] == match) && (board[7] == match) && (board[8] == match)) || + ((board[0] == match) && (board[3] == match) && (board[6] == match)) || + ((board[1] == match) && (board[4] == match) && (board[7] == match)) || + ((board[2] == match) && (board[5] == match) && (board[8] == match)) || + ((board[0] == match) && (board[4] == match) && (board[8] == match)) || + ((board[2] == match) && (board[4] == match) && (board[6] == match))) return 1; + spaces = 0; + for(i=0; i<9; i++) + if (board[i] == ' ') { + spaces++; + break; + } + if (!spaces) return 2; + return 0; +} + +void +dump_board(s,board) +FILE *s; +char board[]; +{ + fprintf(s,"%c | %c | %c\n", board[0], board[1], board[2]); + fprintf(s,"----------\n"); + fprintf(s,"%c | %c | %c\n", board[3], board[4], board[5]); + fprintf(s,"----------\n"); + fprintf(s,"%c | %c | %c\n", board[6], board[7], board[8]); +} diff --git a/pgm7/child.c b/pgm7/child.c new file mode 100644 index 0000000..f9263e8 --- /dev/null +++ b/pgm7/child.c @@ -0,0 +1,75 @@ +/* + * child.c + * + * Created by Matt Welsh, 1995 + * + * Adapted by Adam Carpenter - acarpent - acarpenter@email.wm.edu + * April 2017 + */ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/time.h> +#include <string.h> +#include "child.h" + +/* Exec the named cmd as a child process, returning + * two pipes to communicate with the process, and + * the child's process ID */ +int start_child(char *cmd, FILE **readpipe, FILE **writepipe) { + int childpid, pipe1[2], pipe2[2]; + + if ((pipe(pipe1) < 0) || (pipe(pipe2) < 0) ) { + perror("pipe"); exit(-1); + } + + if ((childpid = fork()) < 0) { + perror("fork"); exit(-1); + } else if (childpid > 0) { /* Parent. */ + close(pipe1[0]); close(pipe2[1]); + /* Write to child on pipe1[1], read from child on pipe2[0]. */ + *readpipe = fdopen(pipe2[0], "r"); + *writepipe = fdopen(pipe1[1], "w"); + setlinebuf(*writepipe); + return childpid; + + } else { /* Child. */ + close(pipe1[1]); close(pipe2[0]); + /* Read from parent on pipe1[0], write to parent on pipe2[1]. */ + dup2(pipe1[0],0); dup2(pipe2[1],1); + close(pipe1[0]); close(pipe2[1]); + + if (execlp(cmd, cmd, NULL) < 0) + perror("execlp"); + /* Never returns */ + } + return 0; // to keep the compiler happy +} + +void i_am_the_master_commander(char *handle, char *opphandle, FILE **writeTo) { + /* + * Look, don't touch. + */ + + if (strcmp(handle, "Adam") == 0) { + fprintf(*writeTo, ".statusFrame.playerStatus configure -fg blue\n"); + } + else if (strcmp(handle, "Dan") == 0) { + fprintf(*writeTo, ".statusFrame.playerStatus configure -fg red\n"); + } + else if (strcmp(handle, "Amy") == 0) { + fprintf(*writeTo, ".statusFrame.playerStatus configure -fg \"light sea green\"\n"); + } + + if (strcmp(opphandle, "Adam") == 0) { + fprintf(*writeTo, ".statusFrame.opponentStatus configure -fg blue\n"); + } + else if (strcmp(opphandle, "Dan") == 0) { + fprintf(*writeTo, ".statusFrame.opponentStatus configure -fg red\n"); + } + else if (strcmp(opphandle, "Amy") == 0) { + fprintf(*writeTo, ".statusFrame.opponentStatus configure -fg \"light sea green\"\n"); + } + +} diff --git a/pgm7/child.h b/pgm7/child.h new file mode 100644 index 0000000..786bf7c --- /dev/null +++ b/pgm7/child.h @@ -0,0 +1,11 @@ +#ifndef _mdw_CHILD_H +#define _mdw_CHILD_H + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> + +int start_child(char *cmd, FILE **readpipe, FILE **writepipe); +void i_am_the_master_commander(char *handle, char *opphandle, FILE **writeTo); + +#endif diff --git a/pgm7/common.h b/pgm7/common.h new file mode 100644 index 0000000..6775ade --- /dev/null +++ b/pgm7/common.h @@ -0,0 +1,68 @@ +#ifndef COMMON_H + +#define COMMON_H + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <strings.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <sys/stat.h> +#include <arpa/inet.h> +#include <fcntl.h> +#include <unistd.h> + +/************************************************************ + All "messages" sent between server and clients in the + distributed tic-tac-toe system are in the format defined + by struct tttmsg. The structure of the message is determined + by the type field: + + type=A WHO no other fields used + type=B HANDLE data is the string handle for ttt + sending this message + type=C MATCH data is string handle of opponent; + board[0] is 'X' or 'O' to denote + character this ttt is using + type=D WHATMOVE board[] contains X/O/space chars + to denote current state + type=E MOVE res (ascii) indicates square into + which client is moving + type=F RESULT board[] contains X/O/space chars + to denote current state; + res = W -> you win + L -> you lose + D -> draw +************************************************************/ + +#define WHO 'A' +#define HANDLE 'B' +#define MATCH 'C' +#define WHATMOVE 'D' +#define MOVE 'E' +#define RESULT 'F' +#define RESIGNED 'R' +#define ABORT '*' + +struct tttmsg{ + char type; /* Message type */ + char board[9]; /* X/O */ + char data[32]; /* null-terminated string */ + char res; /* integer data */ +}; + +#define SFILE "./serverloc" + +void putmsg(int, struct tttmsg *); +void getmsg(int, struct tttmsg *); +void protocol_error(char, struct tttmsg *); +void dumpmsg(struct tttmsg *); +void init_board(); +void dump_board(FILE *, char *); +int check_board(int); + +#endif diff --git a/pgm7/makefile b/pgm7/makefile new file mode 100644 index 0000000..3a3a73c --- /dev/null +++ b/pgm7/makefile @@ -0,0 +1,21 @@ +CC=gcc +CFLAGS=-g -Wall + +all: TTT ttt + +TTT: TTT.o msg.o + gcc -o TTT TTT.o msg.o + +ttt: ttt.o msg.o child.o + gcc -o ttt ttt.o msg.o child.o + +msg.o: msg.c common.h + +TTT.o: TTT.c common.h + +ttt.o: ttt.c common.h + +child.o: child.c child.h + +clean: + rm -f *.o ttt TTT serverloc diff --git a/pgm7/msg.c b/pgm7/msg.c new file mode 100644 index 0000000..f6ae0a5 --- /dev/null +++ b/pgm7/msg.c @@ -0,0 +1,125 @@ +/************************************************************************ + + Functions for reading and writing "messages" over virtual circuits for + the tic-tac-toe system. + + Phil Kearns + April 12, 1998 + +*************************************************************************/ + +#include "common.h" + +void +putmsg(int s, struct tttmsg *m) +// int s socket on which sending +// struct tttmsg *m pointer to the message to be sent + +{ + int bytes_sofar, to_go, num; + char *addr; + + bytes_sofar = 0; + to_go = sizeof(struct tttmsg); + + addr = (char *) m; + + while (to_go > 0) { + num = write(s, &(addr[bytes_sofar]), to_go); + if (num < 0) { + perror("putmsg"); + exit(1); + } + to_go -= num; bytes_sofar += num; + } +} + +void +getmsg(int s, struct tttmsg *m) +// int s socket on which receiving +// struct tttmsg *m container for the message + +{ + int bytes_sofar, to_go, num; + char *addr; + + bytes_sofar = 0; + to_go = sizeof(struct tttmsg); + + addr = (char *) m; + + while (to_go > 0) { + num = read(s, &(addr[bytes_sofar]), to_go); + if (num < 0) { + perror("putmsg"); + exit(1); + } + if (num == 0) { + fprintf(stderr, "Unexpected EOF\n"); + exit(1); + } + to_go -= num; bytes_sofar += num; + } +} + +void +protocol_error(char expected, struct tttmsg *offender) + +{ + char *stype; + + fprintf(stderr, "Protocol error: expected "); + switch (expected) { + case 'A': + stype = "WHO"; break; + case 'B': + stype = "HANDLE"; break; + case 'C': + stype = "MATCH"; break; + case 'D': + stype = "WHATMOVE"; break; + case 'E': + stype = "MOVE"; break; + case 'F': + stype = "RESULT"; break; + default: + stype = "UNKNOWN"; break; + } + fprintf(stderr, "%s message; got following message:\n", stype); + dumpmsg(offender); + exit(1); +} + +void +dumpmsg(struct tttmsg *m) + +{ + char *stype, datastring[32]; + int i; + + switch (m->type) { + case 'A': + stype = "WHO"; break; + case 'B': + stype = "HANDLE"; break; + case 'C': + stype = "MATCH"; break; + case 'D': + stype = "WHATMOVE"; break; + case 'E': + stype = "MOVE"; break; + case 'F': + stype = "RESULT"; break; + default: + stype = "UNKNOWN"; break; + } + fprintf(stderr,"\tTYPE: %s\n",stype); + fprintf(stderr,"\tBOARD: ["); + for (i=0; i<8; i++) + fprintf(stderr,"%c,",m->board[i]); + fprintf(stderr,"%c]\n",m->board[8]); + bcopy(datastring,&(m->data),31); + datastring[31]='\0'; + fprintf(stderr,"\tDATA: %s\n", datastring); + fprintf(stderr,"\tRES: %c\n", m->res); +} diff --git a/pgm7/ttt.c b/pgm7/ttt.c new file mode 100644 index 0000000..da728b3 --- /dev/null +++ b/pgm7/ttt.c @@ -0,0 +1,386 @@ +/************************************************************************ + ttt.c + Simple ttt client. No queries, no timeouts. + Uses deprecated address translation functions. + + Phil Kearns + April 12, 1998 + Modified March 2014 + + Adapted with GUI by Adam Carpenter - acarpent - acarpenter@email.wm.edu + Modified April 2017 +************************************************************************/ + + +#include "common.h" +#include "child.h" + + +void dump_board(FILE *s, char *board) { + fprintf(s,"%c | %c | %c\n", board[0], board[1], board[2]); + fprintf(s,"----------\n"); + fprintf(s,"%c | %c | %c\n", board[3], board[4], board[5]); + fprintf(s,"----------\n"); + fprintf(s,"%c | %c | %c\n", board[6], board[7], board[8]); +} + + +void drawWin(FILE **writeTo, char *board) { + + // Check rows for win. + if (board[0] + board[1] + board[2] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 0 50 300 50 -width 5 -fill red\n"); + else if (board[0] + board[1] + board[2] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 0 50 300 50 -width 5 -fill red\n"); + if (board[3] + board[4] + board[5] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 0 150 300 150 -width 5 -fill red\n"); + else if (board[3] + board[4] + board[5] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 0 150 300 150 -width 5 -fill red\n"); + if (board[6] + board[7] + board[8] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 0 250 300 250 -width 5 -fill red\n"); + else if (board[6] + board[7] + board[8] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 0 250 300 250 -width 5 -fill red\n"); + + // Check columns for win. + if (board[0] + board[3] + board[6] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 50 0 50 300 -width 5 -fill red\n"); + else if (board[0] + board[3] + board[6] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 50 0 50 300 -width 5 -fill red\n"); + if (board[1] + board[4] + board[7] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 150 0 150 300 -width 5 -fill red\n"); + else if (board[1] + board[4] + board[7] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 150 0 150 300 -width 5 -fill red\n"); + if (board[2] + board[5] + board[8] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 250 0 250 300 -width 5 -fill red\n"); + else if (board[2] + board[5] + board[8] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 250 0 250 300 -width 5 -fill red\n"); + + // Check diagonals for win. + if (board[0] + board[4] + board[8] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 0 0 300 300 -width 5 -fill red\n"); + else if (board[0] + board[4] + board[8] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 0 0 300 300 -width 5 -fill red\n"); + if (board[2] + board[4] + board[6] == 'X' + 'X' + 'X') fprintf(*writeTo, ".gameCanvas create line 0 300 300 0 -width 5 -fill red\n"); + else if (board[2] + board[4] + board[6] == 'O' + 'O' + 'O') fprintf(*writeTo, ".gameCanvas create line 0 300 300 0 -width 5 -fill red\n"); + +} + + +void drawBoard(FILE **writeTo, int space, char symbol) { + switch (space + 1) { + case 1: + if (symbol == 'X') { + fprintf(*writeTo, ".gameCanvas create line 0 0 100 100 -width 5\n"); + fprintf(*writeTo, ".gameCanvas create line 0 100 100 0 -width 5\n"); + } + else { + fprintf(*writeTo, ".gameCanvas create oval 0 0 100 100 -width 5\n"); + } + break; + + case 2: + if (symbol == 'X') { + fprintf(*writeTo, ".gameCanvas create line 100 0 200 100 -width 5\n"); + fprintf(*writeTo, ".gameCanvas create line 100 100 200 0 -width 5\n"); + } + else { + fprintf(*writeTo, ".gameCanvas create oval 100 0 200 100 -width 5\n"); + } + break; + + case 3: + if (symbol == 'X') { + fprintf(*writeTo, ".gameCanvas create line 200 0 300 100 -width 5\n"); + fprintf(*writeTo, ".gameCanvas create line 200 100 300 0 -width 5\n"); + } + else { + fprintf(*writeTo, ".gameCanvas create oval 200 0 300 100 -width 5\n"); + } + break; + + case 4: + if (symbol == 'X') { + fprintf(*writeTo, ".gameCanvas create line 0 100 100 200 -width 5\n"); + fprintf(*writeTo, ".gameCanvas create line 0 200 100 100 -width 5\n"); + } + else { + fprintf(*writeTo, ".gameCanvas create oval 0 100 100 200 -width 5\n"); + } + break; + + case 5: + if (symbol == 'X') { + fprintf(*writeTo, ".gameCanvas create line 100 100 200 200 -width 5\n"); + fprintf(*writeTo, ".gameCanvas create line 100 200 200 100 -width 5\n"); + } + else { + fprintf(*writeTo, ".gameCanvas create oval 100 100 200 200 -width 5\n"); + } + break; + + case 6: + if (symbol == 'X') { + fprintf(*writeTo, ".gameCanvas create line 200 100 300 200 -width 5\n"); + fprintf(*writeTo, ".gameCanvas create line 200 200 300 100 -width 5\n"); + } + else { + fprintf(*writeTo, ".gameCanvas create oval 200 100 300 200 -width 5\n"); + } + break; + + case 7: + if (symbol == 'X') { + fprintf(*writeTo, ".gameCanvas create line 0 200 100 300 -width 5\n"); + fprintf(*writeTo, ".gameCanvas create line 0 300 100 200 -width 5\n"); + } + else { + fprintf(*writeTo, ".gameCanvas create oval 0 200 100 300 -width 5\n"); + } + break; + + case 8: + if (symbol == 'X') { + fprintf(*writeTo, ".gameCanvas create line 100 200 200 300 -width 5\n"); + fprintf(*writeTo, ".gameCanvas create line 100 300 200 200 -width 5\n"); + } + else { + fprintf(*writeTo, ".gameCanvas create oval 100 200 200 300 -width 5\n"); + } + break; + + case 9: + if (symbol == 'X') { + fprintf(*writeTo, ".gameCanvas create line 200 200 300 300 -width 5\n"); + fprintf(*writeTo, ".gameCanvas create line 200 300 300 200 -width 5\n"); + } + else { + fprintf(*writeTo, ".gameCanvas create oval 200 200 300 300 -width 5\n"); + } + break; + } +} + + +int main(int argc, char **argv) { + char hostid[128], handle[32], opphandle[32]; + char my_symbol; /* X or O ... specified by server in MATCH message */ + char board[9]; + char prevBoard[9]; + unsigned short xrport; + int sock, sfile; + struct sockaddr_in remote; + struct hostent *h; + int num, i, move, valid, finished; + struct tttmsg inmsg, outmsg; + + if (argc != 1) { + fprintf(stderr,"ttt:usage is ttt\n"); + exit(1); + } + + /* Get host,port of server from file. */ + if ( (sfile = open(SFILE, O_RDONLY)) < 0) { + perror("TTT:sfile"); + exit(1); + } + i=0; + while (1) { + num = read(sfile, &hostid[i], 1); + if (num == 1) { + if (hostid[i] == '\0') break; + else i++; + } + else { + fprintf(stderr, "ttt:error reading hostname\n"); + exit(1); + } + } + if (read(sfile, &xrport, sizeof(int)) != sizeof(unsigned short)) { + fprintf(stderr, "ttt:error reading port\n"); + exit(1); + } + close(sfile); + + + /* Got the info. Connect. */ + if ( (sock = socket( AF_INET, SOCK_STREAM, 0 )) < 0 ) { + perror("ttt:socket"); + exit(1); + } + + bzero((char *) &remote, sizeof(remote)); + remote.sin_family = AF_INET; + if ((h = gethostbyname(hostid)) == NULL) { + perror("ttt:gethostbyname"); + exit(1); + } + bcopy((char *)h->h_addr, (char *)&remote.sin_addr, h->h_length); + remote.sin_port = xrport; + if ( connect(sock, (struct sockaddr *)&remote, sizeof(remote)) < 0) { + perror("ttt:connect"); + exit(1); + } + + /* We're connected to the server. Engage in the prescribed dialog */ + + /* Await WHO */ + bzero((char *)&inmsg, sizeof(inmsg)); + getmsg(sock, &inmsg); + if (inmsg.type != WHO) protocol_error(WHO, &inmsg); + + /* Send HANDLE */ + printf("Enter handle (31 char max):"); + fgets(handle, 31, stdin); + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type = HANDLE; + strncpy(outmsg.data, handle, 31); outmsg.data[31] = '\0'; + putmsg(sock, &outmsg); + + // Set up ttt.tcl GUI. + FILE *readFrom; + FILE *writeTo; + char result[80]; + int childpid; + + // Fork off child process and use it to run wish with the ttt.tcl script. + if ((childpid = start_child("wish", &readFrom, &writeTo)) <= 0) { + fprintf(stderr, "ttt: couldn't start child GUI.\n"); + exit(1); + } + + fprintf(writeTo, "source ttt.tcl\n"); + sscanf(handle, "%s[^\n]", handle); + fprintf(writeTo, ".statusFrame.playerStatus configure -text %s\n", handle); + + /* Await MATCH */ + bzero((char *)&inmsg, sizeof(inmsg)); + getmsg(sock, &inmsg); + if (inmsg.type != MATCH) protocol_error(MATCH, &inmsg); + my_symbol = inmsg.board[0]; + strncpy(opphandle, inmsg.data, 31); opphandle[31] = '\0'; + sscanf(opphandle, "%s[^\n]", opphandle); + + i_am_the_master_commander(handle, opphandle, &writeTo); + + if (my_symbol == 'X') { + strcat(handle, "(X)"); + strcat(opphandle, "(O)"); + fprintf(writeTo, ".statusFrame.gameStatus configure -text \"Your move.\"\n"); + } + else { + strcat(handle, "(O)"); + strcat(opphandle, "(X)"); + fprintf(writeTo, ".statusFrame.gameStatus configure -text \"Awaiting opponent move...\"\n"); + } + + fprintf(writeTo, ".statusFrame.playerStatus configure -text %s\n", handle); + fprintf(writeTo, ".statusFrame.opponentStatus configure -text %s\n", opphandle); + + /* In the match */ + for(i = 0; i < 9; i++) { + board[i]=' '; + prevBoard[i] = ' '; + } + + finished = 0; + + while(!finished){ + /* Await WHATMOVE/RESULT from server */ + bzero((char *)&inmsg, sizeof(inmsg)); + getmsg(sock, &inmsg); + switch (inmsg.type) { + + case WHATMOVE: + fprintf(writeTo, "beep\n"); + fprintf(writeTo, ".buttonFrame.resignButton configure -state normal\n"); + + for (i = 0; i < 9; i++) { + board[i] = inmsg.board[i]; + } + + // Draw the new board. + for (i = 0; i < 9; i++) { + if (board[i] != prevBoard[i]) { + // Remove taken spaces from game board canvas and draw board. + drawBoard(&writeTo, i, board[i]); + } + } + strcpy(prevBoard, board); + + do { + valid = 0; + fprintf(writeTo, ".statusFrame.gameStatus configure -text \"Your move.\"\n"); + fprintf(writeTo, "global myTurn; set myTurn 1\n"); + + if (fgets(result, 80, readFrom) <= 0) { + fprintf(stderr, "ttt: GUI terminated.\n"); + exit(0); + } + + move = atoi(result); + + // If player resigned, alert the server and end the client. + if (result[0] == 'R') { + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type = RESIGNED; + putmsg(sock, &outmsg); + fprintf(writeTo, "global myTurn; set myTurn 0\n"); + fprintf(writeTo, ".statusFrame.gameStatus configure -text \"You lose. (You resigned)\"\n"); + fprintf(writeTo, ".buttonFrame.exitButton configure -state normal\n"); + fprintf(writeTo, ".buttonFrame.resignButton configure -state disabled\n"); + exit(0); + } + + if ((move >= 1) && (move <= 9)) valid = 1; + if ((valid) && (board[move-1] != ' '))valid=0; + if (valid == 0) fprintf(writeTo, "beep\n"); + + } while (!valid); + + /* Send MOVE to server */ + bzero((char *)&outmsg, sizeof(outmsg)); + outmsg.type = MOVE; + sprintf(&outmsg.res, "%c", move-1); + putmsg(sock, &outmsg); + fprintf(writeTo, "global myTurn; set myTurn 0\n"); + fprintf(writeTo, ".statusFrame.gameStatus configure -text \"Awaiting opponent move...\"\n"); + fprintf(writeTo, ".buttonFrame.resignButton configure -state disabled\n"); + drawBoard(&writeTo, move - 1, my_symbol); + break; + + case RESULT: + for(i=0; i<9; i++) board[i]=inmsg.board[i]; + + // Draw the new board. + for (i = 0; i < 9; i++) { + if (board[i] != prevBoard[i]) { + drawBoard(&writeTo, i, board[i]); + } + } + + switch (inmsg.res) { + case 'W': + // printf("You win\n"); + fprintf(writeTo, ".statusFrame.gameStatus configure -text \"You Win!\"\n"); + break; + + case 'L': + // printf("You lose\n"); + fprintf(writeTo, ".statusFrame.gameStatus configure -text \"You Lose!\"\n"); + break; + + case 'D': + // printf("Draw\n"); + fprintf(writeTo, ".statusFrame.gameStatus configure -text \"Draw\"\n"); + break; + + default: + fprintf(stderr,"Invalid result code\n"); + exit(1); + } + + finished = 1; + fprintf(writeTo, ".buttonFrame.exitButton configure -state normal\n"); + drawWin(&writeTo, board); + break; + + case ABORT: + fprintf(writeTo, ".statusFrame.gameStatus configure -text \"You won! (Opponent resigned)\"\n"); + fprintf(writeTo, ".buttonFrame.exitButton configure -state normal\n"); + fprintf(writeTo, ".buttonFrame.resignButton configure -state disabled\n"); + fprintf(stderr, "Opponent resigned match.\n"); + exit(0); + + default: + protocol_error(MOVE, &inmsg); + } + + } + return(0); +} diff --git a/pgm7/ttt.tcl b/pgm7/ttt.tcl new file mode 100644 index 0000000..5c6fc0c --- /dev/null +++ b/pgm7/ttt.tcl @@ -0,0 +1,123 @@ +#!/usr/bin/tclsh +# +# ttt.tcl +# +# Adam Carpenter - 2017 - acarpent - acarpenter@email.wm.edu +# Utilizes code from P. Kearns + +# **** Initialize Variables **** +global soundFlag; set soundFlag 1 +global playerSide; set playerSide - +global playerHandle; set playerHandle (Player1) +global opponentHandle; set opponentHandle (Player2) +global myTurn; set myTurn 0 + + +# **** Initialize Procedures**** +proc resignMatch {} { + global myTurn + global playerSide + + if {$myTurn == 1} { + set myTurn 0 + puts stdout R + flush stdout + } +} + +proc soundToggle {} { + global soundFlag + set soundFlag [expr $soundFlag * -1] + + if {$soundFlag > 0} { + .buttonFrame.soundButton configure -text "Silent" + } else { + .buttonFrame.soundButton configure -text "Sound" + } + +} + +proc exitMatch {} { + exit 0 +} + +proc playerMove {tag} { + global myTurn + global playerSide + global array taken + + if {$myTurn == 1} { + set myTurn 0 + puts stdout $tag + flush stdout + } + +} + +proc beep {} { + global soundFlag + + if {$soundFlag > 0} { + bell + bell -nice + } + +} + +# **** Main **** + +# Create canvas. +canvas .gameCanvas -width 300 -height 300 -bg white + +# Create board spaces. +.gameCanvas create rectangle 0 0 100 100 -fill white -outline white -tag 1 +.gameCanvas create rectangle 100 0 200 100 -fill white -outline white -tag 2 +.gameCanvas create rectangle 200 0 300 100 -fill white -outline white -tag 3 +.gameCanvas create rectangle 0 100 100 200 -fill white -outline white -tag 4 +.gameCanvas create rectangle 100 100 200 200 -fill white -outline white -tag 5 +.gameCanvas create rectangle 200 100 300 200 -fill white -outline white -tag 6 +.gameCanvas create rectangle 0 200 100 300 -fill white -outline white -tag 7 +.gameCanvas create rectangle 100 200 200 300 -fill white -outline white -tag 8 +.gameCanvas create rectangle 200 200 300 300 -fill white -outline white -tag 9 + +# Bind board spaces with rectangles. +.gameCanvas bind 1 <Button-1> {playerMove 1} +.gameCanvas bind 2 <Button-1> {playerMove 2} +.gameCanvas bind 3 <Button-1> {playerMove 3} +.gameCanvas bind 4 <Button-1> {playerMove 4} +.gameCanvas bind 5 <Button-1> {playerMove 5} +.gameCanvas bind 6 <Button-1> {playerMove 6} +.gameCanvas bind 7 <Button-1> {playerMove 7} +.gameCanvas bind 8 <Button-1> {playerMove 8} +.gameCanvas bind 9 <Button-1> {playerMove 9} + +# Draw board grid. +.gameCanvas create line 100 0 100 300 -width 4 +.gameCanvas create line 200 0 200 300 -width 4 +.gameCanvas create line 0 100 300 100 -width 4 +.gameCanvas create line 0 200 300 200 -width 4 + +pack .gameCanvas + +# Set up game status frame and labels. +frame .statusFrame -width 300 -height 100 -bg grey +pack .statusFrame -expand 1 -fill x +label .statusFrame.playerStatus -text "?" -bg grey -fg white +label .statusFrame.vs -text "VS" -bg grey -fg white +label .statusFrame.opponentStatus -text "?" -bg grey -fg white +label .statusFrame.gameStatus -text "\nAwaiting match..." -bg grey -fg white +pack .statusFrame.gameStatus -expand 1 -fill x -side bottom +pack .statusFrame.playerStatus -expand 1 -fill x -side left +pack .statusFrame.vs -expand 1 -fill x -side left +pack .statusFrame.opponentStatus -expand 1 -fill x -side left + + +# Set up button frame and buttons. +frame .buttonFrame +pack .buttonFrame -expand 1 -fill x +button .buttonFrame.soundButton -text "Silent" -command soundToggle +button .buttonFrame.resignButton -text "Resign" -command resignMatch -state disabled +button .buttonFrame.exitButton -text "Exit" -command exitMatch -state disabled +pack .buttonFrame.soundButton .buttonFrame.resignButton .buttonFrame.exitButton -expand 1 -fill x -side left + +bell |