summaryrefslogblamecommitdiff
path: root/pgm7/ttt.c
blob: da728b3495ccea9eaebbc3909415d52ad116eb1f (plain) (tree)

































































































































































































































































































































































































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