/* TTT.c
*
* I consulted the following pages to increase my understanding of servers that
* use select and to guide my thoughts of how I should construct my own server
* using the select function.
* http://www.binarytides.com/multiple-socket-connections-fdset-select-linux/
* http://www.lowtek.com/sockets/select.html
*
* Utilizes code from P. Kearns - 1987, 2009
*
* Adam Carpenter - 2017
*/
#include "ttt.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>
#include <arpa/inet.h>
int checkEndGame(char *board) {
int i;
int tie = TRUE;
// Check rows for win.
if (board[0] + board[1] + board[2] == 'X' + 'X' + 'X') return X_WIN;
else if (board[0] + board[1] + board[2] == 'O' + 'O' + 'O') return O_WIN;
if (board[3] + board[4] + board[5] == 'X' + 'X' + 'X') return X_WIN;
else if (board[3] + board[4] + board[5] == 'O' + 'O' + 'O') return O_WIN;
if (board[6] + board[7] + board[8] == 'X' + 'X' + 'X') return X_WIN;
else if (board[6] + board[7] + board[8] == 'O' + 'O' + 'O') return O_WIN;
// Check columns for win.
if (board[0] + board[3] + board[6] == 'X' + 'X' + 'X') return X_WIN;
else if (board[0] + board[3] + board[6] == 'O' + 'O' + 'O') return O_WIN;
if (board[1] + board[4] + board[7] == 'X' + 'X' + 'X') return X_WIN;
else if (board[1] + board[4] + board[7] == 'O' + 'O' + 'O') return O_WIN;
if (board[2] + board[5] + board[8] == 'X' + 'X' + 'X') return X_WIN;
else if (board[2] + board[5] + board[8] == 'O' + 'O' + 'O') return O_WIN;
// Check diagonals for win.
if (board[0] + board[4] + board[8] == 'X' + 'X' + 'X') return X_WIN;
else if (board[0] + board[4] + board[8] == 'O' + 'O' + 'O') return O_WIN;
if (board[2] + board[4] + board[6] == 'X' + 'X' + 'X') return X_WIN;
else if (board[2] + board[4] + board[6] == 'O' + 'O' + 'O') return O_WIN;
// Check for tie win.
for (i = 0; i < 9; i++) {
if (board[i] != 'X' && board[i] != 'O') tie = FALSE;
}
if (tie) {
return TIE_GAME;
}
return 0;
}
int main() {
int i;
char message[MESSAGE_BUFF];
char hostname[MAX_HOSTNAME];
int streamPort;
int querySock;
int listenSock;
int newSock;
int connSockX;
int connSockO;
int queuedPlayers[MAX_CLIENTS];
socklen_t length;
struct sockaddr_in *serverAddr, peerAddr;
struct addrinfo hints, *addrlist;
int hits;
fd_set writeSockets;
fd_set readSockets;
char gameBoard[] = "123456789";
char handleX[MESSAGE_BUFF];
char handleO[MESSAGE_BUFF];
char gameTurn = 'X';
int numPlayers = 0;
int gameOn = FALSE;
int winner = 0;
int qPort = 0;
socklen_t qLength;
struct sockaddr_in *qServerAddr, from;
struct addrinfo qHints, *qAddrlist;
socklen_t fsize;
struct timeval timeout;
// **** Create SOCK_DGRAM socket to send datagrams to clients ****
// Create qServerAddr for dgram.
memset(&qHints, 0, sizeof(qHints));
qHints.ai_family = AF_INET;
qHints.ai_socktype = SOCK_DGRAM;
qHints.ai_flags = AI_NUMERICSERV | AI_PASSIVE;
qHints.ai_protocol = 0;
qHints.ai_canonname = NULL;
qHints.ai_addr = NULL;
qHints.ai_next = NULL;
// Do getaddrinfo on qHints.
if (getaddrinfo(NULL, "0", &qHints, &qAddrlist) != 0) {
fprintf(stderr, "TTT: couldn't getaddrinfo for dgrams.\n");
exit(1);
}
// Set server address.
qServerAddr = (struct sockaddr_in *) qAddrlist->ai_addr;
// Create socket.
if ((querySock = socket(qAddrlist->ai_family, qAddrlist->ai_socktype, 0)) < 0) {
fprintf(stderr, "TTT: couldn't create socket for dgrams.\n");
exit(1);
}
// Bind socket.
if (bind(querySock, (struct sockaddr *) qServerAddr, sizeof(struct sockaddr_in)) < 0) {
fprintf(stderr, "TTT: couldn't bind socket for dgrams.\n");
exit(1);
}
// Get the socket name for the querySock.
qLength = sizeof(struct sockaddr_in);
if (getsockname(querySock, (struct sockaddr *)qServerAddr, &qLength) < 0) {
fprintf(stderr, "TTT: couldn't get sock name for dgrams.\n");
exit(1);
}
qPort = ntohs(qServerAddr->sin_port);
timeout.tv_sec = 10;
timeout.tv_usec = 0;
setsockopt(querySock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &timeout, sizeof(timeout));
// *************************************************************
// **** Create SOCK_STREAM socket to listen for connections ****
// Create serverAddr for stream vc.
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
if (getaddrinfo(NULL, "0", &hints, &addrlist) != 0) {
fprintf(stderr, "TTT: couldn't getaddrinfo.\n");
exit(1);
}
// Set server address.
serverAddr = (struct sockaddr_in *) addrlist->ai_addr;
// Create socket.
if ((listenSock = socket(addrlist->ai_family, addrlist->ai_socktype, 0)) < 0) {
fprintf(stderr, "TTT: couldn't create socket.\n");
exit(1);
}
// Bind socket.
if (bind(listenSock, (struct sockaddr *) serverAddr, sizeof(struct sockaddr_in)) < 0) {
fprintf(stderr, "TTT: couldn't bind socket.\n");
exit(1);
}
length = sizeof(struct sockaddr_in);
if (getsockname(listenSock, (struct sockaddr *)serverAddr, &length) < 0) {
fprintf(stderr, "TTT: couldn't get sock name.\n");
exit(1);
}
// Write ip and port numbers to game connection file.
FILE *addrFile;
if (!(addrFile = fopen("prof.falken", "w"))) {
fprintf(stderr, "TTT: couldn't open file!\n");
exit(1);
}
if (gethostname(hostname, MAX_HOSTNAME) < 0) {
fprintf(stderr, "TTT: failed to get hostname.\n");
exit(1);
}
streamPort = ntohs(serverAddr->sin_port);
fprintf(addrFile, "%s\n", hostname);
fprintf(addrFile, "%d\n", streamPort);
fprintf(addrFile, "%d\n", qPort);
fclose(addrFile);
freeaddrinfo(addrlist);
listen(listenSock, 6);
length = sizeof(peerAddr);
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
for (i = 0; i < MAX_CLIENTS; i++) {
queuedPlayers[i] = 0;
}
// **** Main server loop ****
for (;;) {
// Set up select sockets.
FD_ZERO(&readSockets);
FD_SET(listenSock, &readSockets);
FD_ZERO(&writeSockets);
FD_SET(querySock, &readSockets);
if (connSockX != 0) {
FD_SET(connSockX, &writeSockets);
FD_SET(connSockX, &readSockets);
}
if (connSockO != 0) {
FD_SET(connSockO, &writeSockets);
FD_SET(connSockO, &readSockets);
}
for (i = 0; i < MAX_CLIENTS; i++) {
if (queuedPlayers[i] > 0) {
if (queuedPlayers[i] != 0) {
FD_SET(queuedPlayers[i], &writeSockets);
}
}
}
// Select sockets that need attention.
if ((hits = select(FD_SETSIZE, &readSockets, &writeSockets, NULL, NULL)) < 0) {
fprintf(stderr, "TTT: select failed.\n");
exit(1);
}
// Handle query requests.
if (FD_ISSET(querySock, &readSockets)) {
fsize = sizeof(from);
recvfrom(querySock, message, MESSAGE_BUFF, 0, (struct sockaddr *) &from, &fsize);
if (gameOn == TRUE) {
strcpy(message, "Game in session between ");
strcat(message, handleX);
strcat(message, " and ");
strcat(message, handleO);
strcat(message, "\n");
}
else {
strcpy(message, "No game in session...\n");
if (queuedPlayers[0] != 0) {
strcat(message, "Players waiting to play:\n");
strcat(message, handleX);
strcat(message, "\n");
}
else {
strcat(message, "No players waiting to play.\n");
}
}
strcat(message, "\0");
sendto(querySock, message, MESSAGE_BUFF, 0, (struct sockaddr *) &from, sizeof(struct sockaddr_in));
}
// Handle new incoming connections.
if (FD_ISSET(listenSock, &readSockets)) {
if ((newSock = accept(listenSock, (struct sockaddr *) &peerAddr, &length)) < 0) {
fprintf(stderr, "TTT: couldn't accept socket.\n");
exit(1);
}
// Queue up player.
for (i = 0; i < MAX_CLIENTS; i++) {
if (queuedPlayers[i] == 0 && newSock != 0) {
queuedPlayers[i] = newSock;
newSock = 0;
}
}
newSock = 0;
}
// **** Game logic ****
if (numPlayers == 0) { // Get first opponent.
if (queuedPlayers[0] != 0) {
// Pull most recent client to be X
connSockX = queuedPlayers[0];
for (i = 1; i < MAX_CLIENTS; i++) {
queuedPlayers[i - 1] = queuedPlayers[i];
}
queuedPlayers[MAX_CLIENTS] = 0;
if (sendMessage(connSockX, HANDLE) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
if (getMessage(connSockX, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
else {
strcpy(handleX, message);
numPlayers++;
}
}
}
else if (numPlayers == 1) { // Get second opponent.
if (queuedPlayers[0] != 0) {
// Pull most recent client to be O
connSockO = queuedPlayers[0];
for (i = 1; i < MAX_CLIENTS; i++) {
queuedPlayers[i - 1] = queuedPlayers[i];
}
queuedPlayers[MAX_CLIENTS] = 0;
if (sendMessage(connSockO, HANDLE) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
if (getMessage(connSockO, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
else {
strcpy(handleO, message);
numPlayers++;
}
}
}
else if (numPlayers == 2 && gameOn == FALSE) { // Set up a new game.
if (FD_ISSET(connSockX, &writeSockets) && FD_ISSET(connSockO, &writeSockets)) {
// If both clients are ready, inform them of opponents and sides.
strcpy(message, "p X O ");
strcat(message, handleO);
if (sendMessage(connSockX, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
strcpy(message, "p O X ");
strcat(message, handleX);
if (sendMessage(connSockO, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
strcpy(gameBoard, "123456789");
gameTurn = 'X';
gameOn = TRUE;
}
}
else if (numPlayers == 2 && gameOn == TRUE) { // Continue turn-taking.
if (gameTurn == 'X' && FD_ISSET(connSockX, &writeSockets)) {
strcpy(message, "b ");
strcat(message, gameBoard);
if (sendMessage(connSockO, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
} // send board to O
if (sendMessage(connSockO, WAIT) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
} // tell O to wait for X to play
if (sendMessage(connSockX, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
} // send board to X
if (sendMessage(connSockX, MOVE) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
} // tell X to make a move
if (getMessage(connSockX, gameBoard)) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
} // get move from X
gameTurn = 'O';
// Check for endgame.
winner = checkEndGame(gameBoard);
if (winner) {
strcpy(message, "b ");
strcat(message, gameBoard);
if (sendMessage(connSockX, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
if (sendMessage(connSockO, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
message[0] = ENDGAME;
message[1] = winner;
message[2] = '\0';
if (sendMessage(connSockX, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
if (sendMessage(connSockO, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
}
else if (gameTurn == 'O' && FD_ISSET(connSockO, &writeSockets)) {
strcpy(message, "b ");
strcat(message, gameBoard);
if (sendMessage(connSockX, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
} // send board to X
if (sendMessage(connSockX, WAIT) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
} // tell X to wait for O to play
if (sendMessage(connSockO, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
} // send board to O
if (sendMessage(connSockO, MOVE) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
} // tell O to make a move
if (getMessage(connSockO, gameBoard) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
} // get move from O
gameTurn = 'X';
// Check for endgame.
winner = checkEndGame(gameBoard);
if (winner) {
strcpy(message, "b ");
strcat(message, gameBoard);
if (sendMessage(connSockX, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
if (sendMessage(connSockO, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
message[0] = ENDGAME;
message[1] = winner;
message[2] = '\0';
if (sendMessage(connSockX, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
if (sendMessage(connSockO, message) == -1) {
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
numPlayers = 0;
handleX[0] = '\0';
handleO[0] = '\0';
connSockX = 0;
connSockO = 0;
gameOn = FALSE;
}
}
}
}
exit(0);
}