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