Browse Source

Completed server program

root 1 year ago
commit
24ebc4b9c3

+ 20 - 0
boggle/Makefile

@@ -0,0 +1,20 @@
+CC = gcc
+CFLAGS = -Wall -std=gnu99
+
+default: boggle
+
+boggle: game.o words.o users.o dictionary.o
+	$(CC) $(CFLAGS) -o boggle game.o words.o users.o dictionary.o
+
+game.o: game.c utils.h
+	$(CC) $(CFLAGS) -c game.c
+words.o: words.c utils.h dictionary.h
+	$(CC) $(CFLAGS) -c words.c dictionary.c
+users.o: users.c utils.h
+	$(CC) $(CFLAGS) -c users.c
+dictionary.o: dictionary.c
+	$(CC) $(CFLAGS) -c dictionary.c
+
+clean:
+	rm game.o words.o users.o
+

BIN
boggle/boggle


+ 79 - 0
boggle/dictionary.c

@@ -0,0 +1,79 @@
+
+#include <string.h>
+#include "dictionary.h"
+
+
+//form hash value for string s
+//this produces a starting value in the dictionary array
+unsigned hash(const char *s) {
+	unsigned hashval;
+	for (hashval = 0; *s != '\0'; s++)
+		hashval = *s + 31 * hashval;
+	return hashval ;
+}
+
+//Performs search for a key in the hashtable.
+//if the string s is in the dictionary, it is in the list of blocks
+//beginning in array entry hash(s).
+//if lookup finds entry for s, it returns a pointer to the node
+//if not - it returns NULL
+DNode * lookup (DNode ** dictionary, int hash_size, const char *key) {
+	DNode * np;
+	unsigned int hashval = hash(key);
+	for (np = dictionary [hashval % hash_size]; np !=NULL; np = np->next)
+		if (strcmp (key, np->key) == 0)
+			return np; //found
+	return NULL; //not found
+}
+
+//insert uses lookup to detemine whether key is already in the dictionary
+//if not, a new entry is created
+DNode * insert (DNode ** dictionary, int hash_size,  const char * key) {
+	unsigned int hashval;
+	DNode *np;
+
+	if ((np = lookup (dictionary, hash_size, key)) == NULL ) { //
+		np = (DNode *) malloc (sizeof (*np));
+
+		if (np == NULL || (np->key = copystr (key)) == NULL)
+			return NULL;
+
+		hashval = hash (key) % hash_size;
+
+		//now links itself on top of array entry
+		np->next = dictionary [hashval];
+		dictionary [hashval] = np;
+	}
+	return np;
+}
+
+void free_dictionary (DNode ** dictionary, int hash_size) {
+	int i;
+	for (i=0; i<hash_size; i++) { //iterate over hash array
+		if (dictionary [i]!=NULL) { //if there is an entry at position i
+			DNode *head = dictionary[i]; //find the head of the linked list
+			DNode *current = head;
+			while (current != NULL) {
+				DNode * temp = current;
+				current = current->next;
+                if (temp->key !=NULL)
+                    free (temp->key);
+				free (temp);
+			}
+			dictionary[i] = NULL;  //BUG fix
+		}
+	}
+}
+
+char *copystr(const char *s) { /* make a duplicate of s */
+	char *p;
+	int len = strlen(s);
+
+	p = (char *) malloc(len+1); /* +1 for �\0� */
+	if (p != NULL)
+		strncpy(p, s, len);
+	p[len] = '\0';
+
+	return p;
+}
+

+ 22 - 0
boggle/dictionary.h

@@ -0,0 +1,22 @@
+#ifndef DICTIONARY_H
+#define DICTIONARY_H
+
+#include <stdlib.h>
+#define BIG_HASH_SIZE 20000
+#define SMALL_HASH_SIZE 100
+
+typedef struct d_node {
+	char* key;
+    struct d_node *next;
+}DNode;
+
+char * copystr (const char *);
+unsigned hash(const char *s);
+
+DNode * lookup (DNode ** dictionary, int hash_size, const char *key);
+
+DNode * insert (DNode ** dictionary, int hash_size, const char * key);
+
+void free_dictionary (DNode ** dictionary, int hash_size);
+
+#endif

BIN
boggle/dictionary.o


+ 162 - 0
boggle/game.c

@@ -0,0 +1,162 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+#include <signal.h>
+#include <unistd.h>
+#include "utils.h"
+
+int round_num;
+int score;
+int word_score;
+int old_score;
+int skip;
+
+char dice[16][6] = {
+  {'R', 'I', 'F', 'O', 'B', 'X'},
+  {'I', 'F', 'E', 'H', 'E', 'Y'},
+  {'D', 'E', 'N', 'O', 'W', 'S'},
+  {'U', 'T', 'O', 'K', 'N', 'D'},
+  {'H', 'M', 'S', 'R', 'A', 'O'},
+  {'L', 'U', 'P', 'E', 'T', 'S'},
+  {'A', 'C', 'I', 'T', 'O', 'A'},
+  {'Y', 'L', 'G', 'K', 'U', 'E'},
+  {'Q', 'B', 'M', 'J', 'O', 'A'},
+  {'E', 'H', 'I', 'S', 'P', 'N'},
+  {'V', 'E', 'T', 'I', 'G', 'N'},
+  {'B', 'A', 'L', 'I', 'Y', 'T'},
+  {'E', 'Z', 'A', 'V', 'N', 'D'},
+  {'R', 'A', 'L', 'E', 'S', 'C'},
+  {'U', 'W', 'I', 'L', 'R', 'G'},
+  {'P', 'A', 'C', 'E', 'M', 'D'}
+};
+char* actions[2] = {"quit", "logout"};
+char* messages[7] = {"Too Short", "Already Found", "Not on Board", "Not a Word", "Welcome to Boggle", "Hello Again", "New Round"};
+
+void swap(int* i, int* j) {
+  int temp = *i;
+  *i = *j;
+  *j = temp;
+}
+
+void generate_board() {
+  srand(time(0));
+  int order[16];
+  int i;
+  for (i = 0; i < 16; i++) {
+    order[i] = i;
+  }
+  for (i = 0; i < 16; i++) {
+    swap(order + i, order + (rand() % (16-i)) + i);
+  }
+  for (i = 0; i < 16; i++) {
+    board[i] = dice[order[i]][rand() % 6];
+  }
+}
+
+void refresh_screen(int score, int bonus, char* prev) {
+  system("clear");
+  printf("Round %d\n", round_num);
+  for (int i = 0; i < 4; i++) {
+    for (int j = 0; j < 4; j++) {
+      printf("%c ", board[4*i+j]);
+    }
+    printf("\n");
+  }
+  printf("Score: %d\n", score);
+  if (bonus > 0) {
+    printf("Last Word: %s (%d points)\n", prev, bonus);
+  }
+  else {
+    printf("%s\n", messages[0-bonus]);
+  }
+}
+
+void handler(int code) {
+  if (code == SIGALRM) {
+    round_num++;
+    reset_words();
+    generate_board();
+    if (word_score > -4) {
+      word_score = -6;
+    }
+    refresh_screen(score, word_score, NULL);
+    skip = 0;
+    alarm(TIME_INTERVAL);
+  }
+  else if (code == SIGINT) {
+    system("clear");
+    scoreboard();
+    printf("Game Over\n");
+    write_users();
+    free_dictionaries();
+    free_players();
+    exit(0);
+  }
+}
+
+int main() {
+  int action;
+  char input[17];
+  strcpy(input, "a");
+  initialzie_dictionary();
+  read_users();
+  struct sigaction newact;
+  newact.sa_handler = handler;
+  newact.sa_flags = 0;
+  sigemptyset(&newact.sa_mask);
+  sigaction(SIGALRM, &newact, NULL);
+  sigaction(SIGINT, &newact, NULL);
+  word_score = login();
+  skip = 0;
+  round_num = 0;
+
+  while (1) {
+    score = 0;
+    old_score = 0;
+    strcpy(input, "a");
+    raise(SIGALRM);
+    skip = 1;
+    while (1) {
+      scanf("%16s", input);
+      if (skip) {
+        word_score = verify_word(input);
+        if (word_score > 0) {
+          score += word_score;
+        }
+        refresh_screen(score, word_score, input);
+      }
+      else
+      {
+        skip = 1;
+      }
+      if (!(strcmp(input, "q") && strcmp(input, "u"))) {
+        if (input[0] == 'q') {
+          action = 0;
+        }
+        else {
+          action = 1;
+        }
+        update_scores(score, old_score);
+        scoreboard();
+        printf("Type y to confirm %s.  Type anything else to cancel\n", actions[action]);
+        scanf("%1s", input);
+        if (input[0] == 'y') {
+          break;
+        }
+        else {
+          old_score = score;
+        }
+      }
+    }
+    if (action == 0) {
+      raise(SIGINT);
+    }
+    else {
+      word_score = login();
+      round_num = 0;
+    }
+  }
+}
+

BIN
boggle/game.o


BIN
boggle/user_data


+ 163 - 0
boggle/users.c

@@ -0,0 +1,163 @@
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "utils.h"
+
+User *head;
+User *current;
+User *array_end;
+
+int total_users;
+
+int read_users() {
+  FILE *users_file;
+  int error;
+
+  users_file = fopen("user_data", "rb");
+  if (users_file == NULL) {
+      fprintf(stderr, "Error: could not open file\n");
+      return 1;
+  }
+
+  error = fread(&total_users, sizeof(int), 1, users_file);
+  if (error) {
+    User* buffer = malloc(total_users*sizeof(User));
+    fread(buffer, sizeof(User), total_users, users_file);
+    head = buffer;
+    User *temp = head;
+    for (int i = 1; i < total_users; i++) {
+      temp->next = buffer+i;
+      temp = buffer+i;
+    }
+    array_end = buffer + total_users - 1;
+  }
+  else
+  {
+    total_users = 0;
+    array_end = NULL;
+  }
+  error = fclose(users_file);
+  if (error != 0) {
+      fprintf(stderr, "Error: fclose failed\n");
+      return 1;
+  }
+  return 0;
+}
+
+int write_users() {
+  User *temp = head;
+  User buffer[total_users];
+  FILE *users_file;
+  int error;
+  users_file = fopen("user_data", "wb");
+  if (users_file == NULL) {
+    fprintf(stderr, "Error: could not open file\n");
+    return 1;
+  }
+  fwrite(&total_users, sizeof(int), 1, users_file);
+  for (int i = 0; i < total_users; i++) {
+    buffer[i] = *temp;
+    temp = temp->next;
+  }
+  error = fwrite(buffer, sizeof(User), total_users, users_file);
+  if (error != total_users) {
+    fprintf(stderr, "Error: fwrite failed\n");
+    return 1;
+  }
+  error = fclose(users_file);
+  if (error != 0) {
+      fprintf(stderr, "Error: fclose failed\n");
+      return 1;
+  }
+  return 0;
+}
+
+int login() {
+  char name[9];
+  char c;
+  int invalid_input = 1;
+  system("clear");
+  while(invalid_input) {
+    printf("Username: ");
+    scanf("%8s", name);
+    invalid_input = 0;
+    for (int i = 0; i < strlen(name); i++) {
+      c = name[i];
+      if ((!isalnum(c)) && (c != '-') && (c != '_')) {
+        printf("Invlaid character: %c\n", c);
+        invalid_input = 1;
+      }
+    }
+  }
+
+  if (head == NULL) {
+    head = malloc(sizeof(User));
+    strcpy(head->name, name);
+    head->max_score = 0;
+    head->total_games = 0;
+    head->total_score = 0;
+    head->next = NULL;
+    current = head;
+    total_users++;
+    return -4;
+  }
+  else {
+    User *temp = head;
+    while(temp->next != NULL && strcmp(name, temp->name)) {
+      temp = temp->next;
+    }
+    if (strcmp(name, temp->name)) {
+      User* new_user = malloc(sizeof(User));
+      strcpy(new_user->name, name);
+      new_user->max_score = 0;
+      new_user->total_games = 0;
+      new_user->total_score = 0;
+      new_user->next = NULL;
+      temp->next = new_user;
+      current = new_user;
+      total_users++;
+      return -4;
+    }
+    else {
+      current = temp;
+      return -5;
+    }
+  }
+}
+
+void update_scores(int new_score, int old_score) {
+  if (new_score > current->max_score) {
+    current->max_score = new_score;
+  }
+  if (!old_score) {
+    current->total_games++;
+  }
+  current->total_score += new_score - old_score;
+}
+
+void scoreboard() {
+  system("clear");
+  User *temp = head;
+  printf("Name | Max Score | Total Games | Total Score\n");
+  while (temp) {
+    printf("%s, %d, %d, %d\n", temp->name, temp->max_score, temp->total_games, temp->total_score);
+    temp = temp->next;
+  }
+}
+
+void free_players() {
+  if (array_end) {
+    User *curr = array_end->next;
+  	User *temp;
+  	while(curr) {
+  		temp = curr->next;
+  		free(curr);
+  		curr = temp;
+    }
+  }
+  if (head) {
+    free(head);
+  }
+}
+

BIN
boggle/users.o


+ 28 - 0
boggle/utils.h

@@ -0,0 +1,28 @@
+#ifndef UTILS_H
+#define UTILS_H
+
+#define TIME_INTERVAL 60
+
+typedef struct user {
+  char name[9];
+  int max_score;
+  int total_games;
+  int total_score;
+  struct user * next;
+} User;
+
+int board[16];
+
+int login();
+int verify_word(char* word);
+void scoreboard();
+int initialzie_dictionary();
+void update_scores(int new_score, int old_score);
+void free_dictionaries();
+void free_players();
+void reset_words();
+int write_users();
+int read_users();
+
+#endif
+

File diff suppressed because it is too large
+ 276644 - 0
boggle/wordlist.txt


+ 144 - 0
boggle/words.c

@@ -0,0 +1,144 @@
+#include "dictionary.h"
+#include "utils.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#define MAX_LINE 100
+
+
+int scores[5] = {1, 1, 2, 3, 5};
+int used[16];
+static DNode* dictionary [BIG_HASH_SIZE];
+static DNode* submitted_words [BIG_HASH_SIZE];
+DNode* result;
+
+int initialzie_dictionary() {
+	FILE *input_FP;
+	char line [MAX_LINE];
+	char * file_name;
+
+	file_name = "wordlist.txt";
+
+	if(!(input_FP = fopen ( file_name , "r" )))    {
+        fprintf(stderr,"Could not open file \"%s\" for reading dictionary words\n", file_name);
+        return 1;
+    }
+
+	while( fgets (line, MAX_LINE, input_FP)!=NULL ) {
+		line[strcspn(line, "\r\n")] = '\0';  //trim new line characters
+		insert (dictionary, BIG_HASH_SIZE, line);
+	}
+	fclose (input_FP);
+  return 0;
+}
+
+int search(char* word, int place, int depth) {
+  if (depth == strlen(word)) {
+    return 1;
+  }
+  else if (board[place] == word[depth] && !used[place]) {
+    used[place] = 1;
+    if (place%4) {
+      if (search(word, place-1, depth+1)) {
+        return 1;
+      }
+      if (place > 3) {
+        if (search(word, place-5, depth+1)) {
+          return 1;
+        }
+      }
+      if (place < 12)  {
+        if (search(word, place+3, depth+1)) {
+          return 1;
+        }
+      }
+    }
+
+    if (place%4 != 3) {
+      if (search(word, place+1, depth+1)) {
+        return 1;
+      }
+      if (place > 3) {
+        if (search(word, place-3, depth+1)) {
+          return 1;
+        }
+      }
+      if (place < 12) {
+        if (search(word, place+5, depth+1)) {
+          return 1;
+        }
+      }
+    }
+
+    if (place > 3) {
+      if (search(word, place-4, depth+1)) {
+        return 1;
+      }
+    }
+    if (place < 12) {
+      if (search(word, place+4, depth+1)) {
+        return 1;
+      }
+    }
+    used[place] = 0;
+    return 0;
+  }
+  else {
+    return 0;
+  }
+}
+
+int in_dictionary(char* word) {
+  result = lookup (dictionary, BIG_HASH_SIZE, word);
+	return result != NULL;
+}
+
+int already_submitted(char* word) {
+  result = lookup(submitted_words, BIG_HASH_SIZE, word);
+  return result != NULL;
+}
+
+int verify_word(char* word) {
+
+  int len = strlen(word);
+  if (len < 3) {
+    return 0;
+  }
+  for (int i = 0; i < len; i++) {
+    word[i] = toupper(word[i]);
+  }
+  if (already_submitted(word)) {
+    return -1;
+  }
+  else if (in_dictionary(word)) {
+    for (int i = 0; i < 16; i++) {
+      if (board[i] == word[0]) {
+        for (int j = 0; j < 16; j++) {
+          used[j] = 0;
+        }
+        if (search(word, i, 0)) {
+          insert (submitted_words, BIG_HASH_SIZE, word);
+          if (len > 7) {
+            return 11;
+          }
+          else {
+            return scores[len-3];
+          }
+        }
+      }
+    }
+    return -2;
+  }
+  return -3;
+}
+
+void reset_words() {
+	free_dictionary(submitted_words, BIG_HASH_SIZE);
+}
+
+void free_dictionaries() {
+  free_dictionary(dictionary, BIG_HASH_SIZE);
+	reset_words();
+}
+

BIN
boggle/words.o


+ 34 - 0
boggle_server/.gitignore

@@ -0,0 +1,34 @@
+# ---> C
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+

File diff suppressed because it is too large
+ 42 - 0
boggle_server/LICENSE


+ 13 - 0
boggle_server/Makefile

@@ -0,0 +1,13 @@
+PORT=9999
+CFLAGS = -Wall -Werror -std=gnu99 -DPORT=\$(PORT)
+
+all: boggle_server
+
+game_server.o: game_server.h
+	$(CC) $(CFLAGS) -c game_server.c
+players.o: game_server.h
+	$(CC) $(CFLAGS) -c players.c
+boggle_server: game_server.o players.o
+	$(CC) $(CFLAGS) -o boggle_server game_server.o players.o
+clean:
+	rm *.o boggle_server

+ 25 - 0
boggle_server/README.md

@@ -0,0 +1,25 @@
+# boggle_server_starter
+
+Program structure and starter code for boggle server (Homework IV).
+
+
+Run <code>./demo_server</code> to see the desired behavior of the server.
+
+
+In a separate terminal, connect to server with netcat:
+<pre><code>nc -C localhost 8888</code>
+</pre>
+
+Test the following commands
+<pre>
+<code>new_game</code>
+<code>add_score</code>
+</pre>
+
+On several separate terminals, run netcat for each new client, run <code>new_game</code>, 
+<code>add_score</code> and then test with:
+
+<pre>
+<code>all_players</code>
+<code>top_3</code>
+</pre>

BIN
boggle_server/boggle_server


BIN
boggle_server/demo_server


+ 393 - 0
boggle_server/game_server.c

@@ -0,0 +1,393 @@
+#define _GNU_SjOURCE 
+#include "game_server.h"
+
+Player *player_list = NULL;
+
+Client *client_list = NULL;
+
+short port = -1;
+
+int board[16];
+
+char dice[16][6] = {
+  {'R', 'I', 'F', 'O', 'B', 'X'},
+  {'I', 'F', 'E', 'H', 'E', 'Y'},
+  {'D', 'E', 'N', 'O', 'W', 'S'},
+  {'U', 'T', 'O', 'K', 'N', 'D'},
+  {'H', 'M', 'S', 'R', 'A', 'O'},
+  {'L', 'U', 'P', 'E', 'T', 'S'},
+  {'A', 'C', 'I', 'T', 'O', 'A'},
+  {'Y', 'L', 'G', 'K', 'U', 'E'},
+  {'Q', 'B', 'M', 'J', 'O', 'A'},
+  {'E', 'H', 'I', 'S', 'P', 'N'},
+  {'V', 'E', 'T', 'I', 'G', 'N'},
+  {'B', 'A', 'L', 'I', 'Y', 'T'},
+  {'E', 'Z', 'A', 'V', 'N', 'D'},
+  {'R', 'A', 'L', 'E', 'S', 'C'},
+  {'U', 'W', 'I', 'L', 'R', 'G'},
+  {'P', 'A', 'C', 'E', 'M', 'D'}
+};
+
+int main(int argc, char* argv[]) {
+    srand(time(0));
+    struct client *p;
+	if (argc > 1)
+		port = (short)(atoi(argv[1]));
+	else
+		port = PORT;
+	
+	//get listen file descriptor
+    int listenfd = setup();
+    
+    //install timer signal handler
+    struct sigaction alarm_handler;
+    alarm_handler.sa_handler = timer_handler;
+    sigemptyset(&alarm_handler.sa_mask);
+    alarm_handler.sa_flags = 0;
+    sigaction(SIGALRM, &alarm_handler, NULL);
+/*
+    struct timeval select_wait_length;
+    select_wait_length.tv_sec = TIMER_TICK - 1;
+    select_wait_length.tv_usec = 0;   
+    alarm(TIMER_TICK);
+*/
+	//: implement select()
+    while(1){
+        fd_set fdlist;
+        int maxfd = listenfd;
+        FD_ZERO(&fdlist);
+        FD_SET(listenfd, &fdlist);
+
+        for(p = client_list; p; p = p->next){
+            FD_SET(p->fd, &fdlist);
+            if(p->fd > maxfd){
+                maxfd = p->fd;
+            }
+        }
+
+        if (select(maxfd + 1, &fdlist, NULL, NULL, NULL) < 0) {
+            perror("select");
+        } else {
+            for (p = client_list; p; p = p->next)
+                if (FD_ISSET(p->fd, &fdlist))
+                    break;
+            if (p)
+                receiveclient(p);  
+            if (FD_ISSET(listenfd, &fdlist))
+                new_connection(listenfd);
+        }
+    }
+    return 0;
+}
+
+/*
+ * Sets up server socket: bind and listen
+ * Returns a file descriptor for listening and accepting new connections
+ */
+int setup (void) {
+	int on = 1, status;
+	struct sockaddr_in self;
+	int listenfd;
+	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
+		perror("socket");
+		exit(1);
+	}
+
+	status = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
+                      (const char *) &on, sizeof(on));
+	if(status == -1) {
+		perror("setsockopt -- REUSEADDR");
+	}
+
+	memset(&self, '\0', sizeof(self));
+	self.sin_family = AF_INET;
+	self.sin_addr.s_addr = INADDR_ANY;
+	self.sin_port = htons(port);
+	printf("Listening on %d\n", port);
+
+	if (bind(listenfd, (struct sockaddr *)&self, sizeof(self)) == -1) {
+		perror("bind"); // probably means port is in use
+		exit(1);
+	}
+
+	if (listen(listenfd, 5) == -1) {
+		perror("listen");
+		exit(1);
+	}
+  
+	return listenfd;
+}
+
+
+/* timer signal handler */
+void timer_handler(int signal) {
+	//broadcast current game end to all connected clients
+	Client *curr;
+	for (curr = client_list; curr!=NULL; curr = curr->next) {
+            sendclient(curr, "Game over\r\n");
+    }
+	
+	//generate new game board
+    int order[16];
+    int i;
+    for (i = 0; i < 16; i++) {
+        order[i] = i;
+    }
+    for (i = 0; i < 16; i++) {
+        swap(order + i, order + (rand() % (16-i)) + i);
+    }
+    for (i = 0; i < 16; i++) {
+        board[i] = dice[order[i]][rand() % 6];
+    }
+	//reset the timer so we get called again in 120 seconds
+	alarm(TIMER_TICK);
+}
+
+/* accepts new connection and adds a client-specific fd */
+void new_connection (int listenfd)  {
+    int fd;
+    struct sockaddr_in r;
+    socklen_t socklen = sizeof(r);
+
+    if ((fd = accept(listenfd, (struct sockaddr *)&r, &socklen)) < 0) {
+        perror("accept");
+        return;
+    } 
+    
+    printf("connection from %s\n", inet_ntoa(r.sin_addr));
+    add_client(fd, r.sin_addr);    
+}
+
+/* creates a new client struct and 
+ - adds it to the list of clients */
+void add_client(int fd, struct in_addr addr){
+    struct client *p = malloc(sizeof(struct client));
+    if (!p) {
+        perror("malloc failure");
+        exit(1);
+    }
+    printf("Adding client %s\n", inet_ntoa(addr));
+    fflush(stdout);
+    p->fd = fd;
+    p->state = NAME; //needs yet to identify new client by name
+    p->inbuf = 0;
+    p->next = client_list;
+    client_list = p;
+    sendclient(p, "What is your player name?\n");
+}
+
+
+void receiveclient(struct client *p) {
+    char *after = p->buf + p->inbuf;
+    int room = BUFFER_SIZE - p->inbuf;
+    int nbytes;
+    if ((nbytes = read(p->fd, after, room)) > 0) {
+        p->inbuf += nbytes;
+        int where = find_network_newline(p->buf, p->inbuf);
+        if (where >= 0) {
+            p->buf[where] = '\0';
+            p->buf[where+1] = '\0';
+           
+            interpret_message(p);
+            where+=2;  // skip over \r\n
+            p->inbuf -= where;
+            memmove(p->buf, p->buf + where, p->inbuf);
+        }
+        room = sizeof(p->buf) - p->inbuf;
+        after = p->buf + p->inbuf;
+    } else {
+        remove_client(p->fd);
+    }
+}
+
+/*
+ * Acts according to the client state
+ */
+void interpret_message(Client *p) {
+    char buf[100];
+    if (p->state == NAME) {
+        strcpy(p->name, p->buf);
+        switch (create_player(p->name, &player_list)) {
+            case 1:
+                sendclient(p, "Welcome back.\r\n");
+                break;
+            case 2: 
+                sprintf(buf, "Username too long, truncated to %d chars.\r\n", (MAX_NAME-1));
+                sendclient(p, buf);                
+                break;
+            case 0: 
+                sendclient(p, "Welcome, new player\r\n");
+                break;
+        }
+        printf("Added %s to the client list\n",p->name);
+        p->state = COMMAND;
+        sendclient(p, "Go ahead and enter player commands>\r\n");
+    } else if (!strcmp(p->buf, "q")) {
+        remove_client(p->fd);
+    } else {
+        parse_line(p);
+    }
+}
+
+int parse_line(Client *p) {
+	char * input = p->buf;
+    
+	// cmd_argv will held arguments to individual commands passed to sub-procedure
+    char *cmd_argv[INPUT_ARG_MAX_NUM];
+    int cmd_argc;
+
+    // tokenize arguments
+    char *next_token = strtok(input, DELIM);
+    cmd_argc = 0;
+    while (next_token != NULL) {        
+        cmd_argv[cmd_argc] = next_token;
+        cmd_argc++;
+        next_token = strtok(NULL, DELIM);
+    }
+	
+    return (cmd_argc > 0 && do_command(p, cmd_argc, cmd_argv));
+}
+
+/* 
+ * Process player commands
+ * Return:  -1 for quit command
+ *          0 otherwise
+ */
+int do_command(struct client * p, int cmd_argc, char **cmd_argv) {
+   
+    if (cmd_argc <= 0) {
+        return 0;
+    } else if (strcmp(cmd_argv[0], "q") == 0 && cmd_argc == 1) {
+        return -1;
+    } else if (strcmp(cmd_argv[0], "all_players") == 0 && cmd_argc == 1) {
+		//produce list of all players and their stats in nice text format and sendclient
+	char scores[100];
+	Player *curr_a = player_list;
+	printf("[Name] [Max Score] [Total Games] [Total Score]\n");
+	while(curr_a != NULL){
+	    sprintf(scores, "[%s] [%d] [%d] [%d]\n", curr_a->name, curr_a->max_score, curr_a->total_games, curr_a->total_score);
+	    sendclient(p, scores);
+	    curr_a = curr_a->next;
+	}
+        
+    } else if (strcmp(cmd_argv[0], "top_3") == 0 && cmd_argc == 1) {
+		//produce list of top 3 players with their total score and sentclient as a text
+	Player *top[3];
+	Player *curr_t = player_list;
+	Player *curr_t_init = player_list;
+	for(int j = 0; j < 3; j++){
+	    top[j] = curr_t_init;
+	    if(curr_t->next != NULL){
+	    	curr_t_init = curr_t_init->next;
+	    }
+	} 
+	for(int i = 1; i < 4 ; i++){
+	    top[i] = player_list;
+	    while(curr_t != NULL){
+		if(i == 1){
+    		    if(curr_t->total_score > top[i]->total_score){
+		    	top[i] = curr_t;
+		    }
+		} else if(i == 2){
+		    if(curr_t->total_score > top[i]->total_score && curr_t->total_score <= top[0]->total_score){
+		    	top[i] = curr_t;
+		    }
+		} else if(i == 3){
+		    if(curr_t->total_score > top[i]->total_score && curr_t->total_score <= top[1]->total_score){
+		    	top[i] = curr_t;
+		    }
+		}
+	    	curr_t = curr_t->next;
+	    }
+	}
+	char top_str[100];
+	sprintf(top_str, "[Rank] [Name] [Total Score]\n");
+	sendclient(p, top_str);
+	for(int k = 0; k < 3; k++){
+	    sprintf(top_str, "[%d] [%s] [%d]\n", k, top[k]->name, top[k]->total_score);
+	    sendclient(p, top_str);
+	}
+    } else if (strcmp(cmd_argv[0], "add_score") == 0 && cmd_argc == 2) {
+        char str [BUFFER_SIZE - 10];
+        int score =atoi(cmd_argv[1]);
+        Player *player = find_player(p->name, player_list);
+        
+        player->total_score+=score;
+        if (score > player->max_score)
+            player->max_score = score;               
+        sprintf (str, "added score %d for player %s\r\n", score, p->name);
+        sendclient(p, str);
+    } else if (strcmp(cmd_argv[0], "new_game") == 0 && cmd_argc == 1) {
+        // -- transmit current board to be presented as a 2D array of chars
+	char print[10];
+	for(int i = 0; i < 4; i++){
+	    for(int j = 0; j < 4; j++){
+		sprintf(print, "%c ", board[4*i + j]);
+		sendclient(p, print);
+	    }
+	    sendclient(p, "\n");
+	}
+    }else {
+        sendclient(p, "Incorrect syntax\r\n");
+    }
+    return 0;
+}
+
+void swap(int* i, int* j) {
+	  int temp = *i;
+	  *i = *j;
+	  *j = temp;
+}
+
+void sendclient(struct client *p, char *msg) {
+    write(p->fd, msg, strlen(msg));
+}
+
+
+/*
+ * Search the first inbuf characters of buf for a network newline.
+ * Return the location of the '\r' if the network newline is found, or -1 otherwise.
+ */
+int find_network_newline(char *buf, int inbuf) {
+	int i;
+	for (i = 0; i < inbuf - 1; i++)
+		if ((buf[i] == '\r') && (buf[i + 1] == '\n'))
+			return i;
+	return -1;
+}
+
+void remove_client(int fd) {
+/*
+    struct client *p = client_list;
+    struct client *t;
+    while(p != NULL){
+    	if(p->fd == fd){
+	    t = p->next;
+	    p->next = t->next;
+	    printf("Removing client %s\n", p->name);
+	    free(p);
+	    if(close(fd) != 0){
+	    	perror("closing fd when removing client");
+	    }
+		
+	}
+    }
+*/
+    struct client **p;
+    for (p = &client_list; *p && (*p)->fd != fd; p = &(*p)->next)
+        ;
+    if (*p) {
+        struct client *t = (*p)->next;
+        fprintf(stderr, "Removing client %s\n", (*p)->name);
+        fflush(stderr);
+        free(*p);
+        if (close(fd) != 0) {
+            perror("closing fd on exiting client");
+        }
+        *p = t;
+    } else {
+        fprintf(stderr, "Trying to remove fd %d, but I don't know about it\n", fd);
+        fflush(stderr);
+    }
+}
+

+ 113 - 0
boggle_server/game_server.h

@@ -0,0 +1,113 @@
+#ifndef GAME_SERVER_H
+#define GAME_SERVER_H
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/select.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#define _GNU_SOURCE
+
+#ifndef PORT
+  #define PORT 8888
+#endif
+
+
+#define NAME 0		//reflects 2 states of client connection: 0 - identification, 1 - identified, can enter commands
+#define COMMAND 1
+
+#define DELIM " \n"
+
+#define TIMER_TICK 120
+#define MAX_NAME 10     // Max playername length
+#define INPUT_ARG_MAX_NUM 10
+
+#define BUFFER_SIZE 1024
+
+//Node for the linked list which stores currently connected clients
+typedef struct client {
+    int fd;
+    int state; // either NAME or COMMAND
+    char name[MAX_NAME];
+    char buf[BUFFER_SIZE];  // each client has its own buffer
+    int inbuf;              // and a personal pointer to the current place in buffer
+    struct client *next;
+} Client;
+
+//Node for linked list which stores all players ever played - these are never removed
+typedef struct player {
+    char name[MAX_NAME]; 
+    int max_score;
+    int total_games;
+    int total_score; 
+    struct player *next;
+} Player;
+
+
+//=====================================
+// These are implemented in players.c
+//=====================================
+
+/*
+ * Create a new player with the given name.  Insert it at the tail of the list
+ * of players whose head is pointed to by *player_ptr_add.
+ *
+ * Return:
+ *   - 0 if successful
+ *   - 1 if a player by this name already exists in this list
+ *   - 2 if the given name cannot fit in the 'name' array 
+ *   - in this case truncate the name (don't forget about the null terminator), 
+ *		add to the list, and return 2 to send message to the client that name was truncated.
+ *       
+ */
+int create_player(char *name, Player **player_ptr_add);
+
+
+/*
+ * Return a pointer to the player with this name in
+ * the list starting with head. Return NULL if no such player exists.
+ *
+ * NOTE: You'll likely need to cast a (const Player *) to a (Player *)
+ * to satisfy the prototype without warnings.
+ */
+Player *find_player(const char *name, Player *head);
+
+
+//=====================================
+// These implemented in game_server.c
+//=====================================
+
+/*
+ * Sets up server socket: bind and listen
+ * Returns a file descriptor for listening and accepting new connections
+ */
+int setup (void);
+
+void timer_handler(int sig);
+
+/* accept new connection and calls add_client */
+void new_connection(int listenfd) ;
+
+/* Sets up Client fields, add to the list, and ask for the name */
+void add_client(int fd, struct in_addr addr);
+/* remove client from the list */
+void remove_client(int fd);
+
+void sendclient(Client *p, char *msg);
+void receiveclient(Client *p);
+
+
+void interpret_message(Client *p);
+
+int parse_line(Client *p);
+int do_command(struct client * p, int cmd_argc, char **cmd_argv);
+
+int find_network_newline(char *buf, int inbuf);
+
+void swap(int* i, int* j);
+
+#endif

+ 47 - 0
boggle_server/players.c

@@ -0,0 +1,47 @@
+#define _GNU_SOURCE 
+#include "game_server.h"
+
+
+//TODO - implement this
+/*
+ * Create a new player with the given name.  Insert it at the tail of the list
+ * of players whose head is pointed to by *player_ptr_add.
+ */
+int create_player(char name[MAX_NAME], Player **player_ptr_add) {
+    int r = 0;
+    Player *current = (*player_ptr_add);
+    while(current){
+	if(strcmp(current->name, name) == 0){
+	    return 1;
+	}
+    }
+    if(strlen(name) > MAX_NAME){
+	name[MAX_NAME] = '\0';
+	r = 2;
+    }
+    Player *next_player = malloc(sizeof(Player));
+    strcpy(next_player->name, name);
+    next_player->max_score = 0;
+    next_player->total_games = 0;
+    next_player->total_score = 0;
+    next_player->next = *(player_ptr_add);
+    *(player_ptr_add) = next_player;
+    return r;
+}
+
+//TODO - implement this
+/* 
+ * Return a pointer to the player with this name in
+ * the list starting with head. Return NULL if no such player exists. 
+ */
+Player *find_player(const char *name, Player *head) {    
+    const Player *current = head;
+    while(current){
+    	if(strcmp(name, current->name) == 0){
+	    return (Player *)current;
+	}
+    }
+    return NULL;
+}
+
+