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