summaryrefslogblamecommitdiff
path: root/pgm4/ttt.c
blob: 572c58815ee8ff041f5c27f6d5582a0518011345 (plain) (tree)
































































































































































































































































                                                                                                                          
/* ttt.c
 *
 * Utilizes code from P. Kearns - 1987, 2009
 * Adam Carpenter - 2017
 */

#include "ttt.h"

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

int main(int argc, char *argv[]) {
    int queryFlag;
    struct timeval timeout;
    int i;
    int errno;
    int hits;
    fd_set readSockets;
    // fd_set writeSockets;

    char message[MESSAGE_BUFF];
    char playerHandle[MESSAGE_BUFF];
    char opponentHandle[MESSAGE_BUFF];
    char playerSide;
    char opponentSide;
    char gameBoard[9];
    char playerMove;
    int connSock;

    char hostname[MAX_HOSTNAME];
    char port[100];
    struct sockaddr_in *serverAddr;
    struct addrinfo hints, *addrlist;

    i = 1;
    queryFlag = FALSE;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;

    while (argv[i]) {

        if (strcmp(argv[i], "-q") == 0) {
            queryFlag = TRUE;
        }
        else if (strcmp(argv[i], "-t") == 0) {

            if (!argv[i + 1]) {
                fprintf(stderr, "usage: ./ttt {-q} {-t} [timeout]\n");
                exit(1);
            }

            timeout.tv_sec = strtol(argv[i + 1], NULL, 10);
            errno = 0;

            if (timeout.tv_sec <= 0) {
                fprintf(stderr, "ttt: timeout must be a positive, nonzero integer.\n");
                exit(1);
            }
            else if (errno == ERANGE) {
                fprintf(stderr, "ttt: timeout must be of integer size.\n");
                exit(1);
            }
            else if (errno == EINVAL) {
                fprintf(stderr, "ttt: timeout must be integer type.\n");
                exit(1);
            }

            i++;
        }
        else {
            fprintf(stderr, "usage: ./ttt {-q} {-t} [timeout]\n");
            exit(1);
        }

        i++;
    }

    // Get server sockaddr.
    memset( &hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;

    if (queryFlag) {
        hints.ai_socktype = SOCK_DGRAM;
    }
    else {
        hints.ai_socktype = SOCK_STREAM;

    }

    hints.ai_flags = AI_NUMERICSERV;
    hints.ai_protocol = 0;
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;

    // (Read name and port from file.)
    FILE *addrfile;

    if (!(addrfile = fopen("prof.falken", "r"))) {
        sleep(60);
        if (!(addrfile = fopen("prof.falken", "r"))) {
            fprintf(stderr, "ttt: couldn't open file -- is server running?\n");
            exit(1);
        }
    }

    fscanf(addrfile, "%s\n", hostname);
    fscanf(addrfile, "%s\n", port);

    if (queryFlag) {
        fscanf(addrfile, "%s\n", port);
    }

    fclose(addrfile);

    if (getaddrinfo(hostname, port, &hints, &addrlist) != 0) {
        fprintf(stderr, "ttt: couldn't getaddrinfo.\n");
        exit(1);
    }

    serverAddr = (struct sockaddr_in *) addrlist->ai_addr;

    if ((connSock = socket(addrlist->ai_family, addrlist->ai_socktype, 0)) < 0) {
        fprintf(stderr, "ttt: couldn't create socket.\n");
        exit(1);
    }

    if (queryFlag) { // Wait for query from server if requested.
        printf("Querying server...\n");
        message[0] = '\0';
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
        setsockopt(connSock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &timeout, sizeof(timeout));
        sendto(connSock, "q\0", strlen("q\0"), 0, (struct sockaddr *) serverAddr, sizeof(struct sockaddr_in));
        recvfrom(connSock, message, MESSAGE_BUFF, 0, NULL, NULL);

        if (message[0] == '\0') {
            printf("Connection timed out.\n");
        }
        else {
            printf("%s\n", message);
        }

        exit(0);
    }
    else { // Else connect to virtual circuit with server.
        if (connect(connSock, (struct sockaddr *) serverAddr, sizeof(struct sockaddr_in)) < 0) {
            fprintf(stderr, "ttt: couldn't connect to socket.\n");
            exit(1);
        }
    }

    printf("Waiting for server...\n");
    playerHandle[0] = '\0';

    // Enter main client loop.
    for(;;) {
        FD_ZERO(&readSockets);
        FD_SET(connSock, &readSockets);

        if (timeout.tv_sec == 0) {
            hits = select(FD_SETSIZE, &readSockets, NULL, NULL, NULL);
        }
        else {
            hits = select(FD_SETSIZE, &readSockets, NULL, NULL, &timeout);
        }

        if (hits < 0) {
            fprintf(stderr, "ttt: failed to select.\n");
            exit(1);
        }
        else if (hits == 0) {
            fprintf(stderr, "ttt: connection timed out.\n");
            exit(0);
        }

        // Get instructions from server.
        getMessage(connSock, message);

        if (strcmp(message, HANDLE) == 0) { // Get and send handle.

            if (playerHandle[0] == '\0') {
                printf("Who are you? ");
                scanf("%20[^\n]", playerHandle);
            }

            sendMessage(connSock, playerHandle);
            printf("Waiting for opponent...\n");
        }
        else if (strcmp(message, ABORT) == 0) { // Abort.
            printf("Server ordered shutdown. Buhbye!\n");
            exit(0);
        }
        else if (message[0] == PLAYERID) { // Print name and sides.
            sscanf(message, "p %c %c %[^\n]", &playerSide, &opponentSide, opponentHandle);

            if (playerSide == 'X') {
                opponentSide = 'O';
            }
            else {
                opponentSide = 'X';
            }

            printf("%s, you are %cs. Your opponent %s is %cs.\n", playerHandle, playerSide, opponentHandle, opponentSide);
            timeout.tv_sec = 0; // Game established -- end timeout.
        }
        else if (message[0] == BOARD) { // Draw the game board.
            sscanf(message, "b %s", gameBoard);
            printf("\n %c | %c | %c\n-----------\n %c | %c | %c\n-----------\n %c | %c | %c\n\n",
                gameBoard[0], gameBoard[1], gameBoard[2],
                gameBoard[3], gameBoard[4], gameBoard[5],
                gameBoard[6], gameBoard[7], gameBoard[8]);
        }
        else if (strcmp(message, WAIT) == 0) { // Wait for opponent.
            printf("Waiting for opponent...\n");
        }
        else if (strcmp(message, MOVE) == 0) { // Get and send move.
            playerMove = '0';

            while (playerMove < '1' ||  playerMove > '9' || gameBoard[playerMove - '0' - 1] != playerMove) {
                if (playerMove != '\n') {
                    printf("Choose an available number on the board.\nYour move: ");
                }

                playerMove = getchar();
            }

            gameBoard[playerMove - '0' - 1] = playerSide;
            strcpy(message, gameBoard);
            sendMessage(connSock, message);
            printf("Waiting for opponent...");
        }
        else if (message[0] == ENDGAME) { // End Game.

            if (message[1] == playerSide) {
                printf("\nYou won %s! Congratulations!\n", playerHandle);
            }
            else if (message[1] == opponentSide) {
                printf("\nYou lost! %s won the match.\nBetter luck next time.\n", opponentHandle);
            }
            else {
                printf("\nThe match was a tie!\nBetter luck next time.\n");
            }
            exit(0);
        }

    }

    exit(0);
}