#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#ifdef SOLARIS
#include <string.h>
#else
#include <strings.h>
#endif

#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <syslog.h>
#ifdef _AIX
#include <sys/select.h>
#endif
#include <netdb.h>
#ifdef HESIOD
#include <hesiod.h>
#endif

#include "boggle.h"

#define MAXLINE 1000

extern char *worddir;

/* The C compiler ought to know this, but doesn't. */
#if defined(mips) && defined(ultrix)
char *getenv();
char *malloc(), *realloc();
#endif
#define my_realloc(ptr, num) ((ptr) ? realloc(ptr, num) : malloc(num))

void listen_loop(void);


/* A player_ext is used to store info about a player that can be seen
   externally, i.e. by a query_game.  A player_int is used to store other
   information about a player that other players wouldn't be interested in.
   Keeping this data divided this way makes it easy to do query_response and
   broadcast_state. */

struct player_int {
  int		socket;
  int		num_of_words;
  int		current_word;
  char	words[MAX_NUM_OF_WORDS][MAX_WORD_LENGTH];
};

struct player_ext players_ext[MAX_NUM_OF_PLAYERS];
static struct player_ext players_netbyte[MAX_NUM_OF_PLAYERS];
struct player_int players_int[MAX_NUM_OF_PLAYERS];

void send_scored_word();

int dictionary_started = 0;     
int num_of_players, player_in_question, num_of_active_players;
char current_tray[5][5];
int is_child = 0;
fd_set permfds;
int max_fd;
int mboggle_socket;
int time_is_up;

int word_values[MAX_WORD_LENGTH] = {0, 0, 0, 0, 1, 2, 3, 5, 8, 13, 21, 34,
				      55, 89, 144, 233, 477, 710, 1187, 1897, 3084, 4981, 8065, 13046, 21111,
				      34156};


#ifdef SOLARIS
#define srandom srand
#define random  rand
#endif


static unsigned long nlen;		/* for network byte order */

reap_children()
{
#ifdef SOLARIS
  pid_t status;
#else
  union wait status;
#endif

  while (wait3(&status, WNOHANG, 0) > 0) /* empty */;
}



int main(int argc, char **argv)
{
  int c;
  extern char *optarg;
  u_short port = 0;
  
  signal(SIGCHLD, (void(*)())reap_children);
  
  openlog("mbogglemaster", LOG_PID, LOG_DAEMON);
  
  while ((c = getopt(argc, argv, "w:p:")) != EOF) {
    switch (c) {
    case 'p':
      port = htons(atoi(optarg));
      break;
    case 'w':
      worddir = optarg;
      break;
    default:
      syslog(LOG_ERR, "invalid command-line option; exiting.");
      exit(1);
    }
  }
  
  mboggle_socket = get_mboggle_socket(port);
  listen_loop();
  return(0);
}


void listen_loop(void)
{
  int nfds, retval;
  fd_set readfds;
  int i;
  
  FD_ZERO(&permfds);
  FD_SET(mboggle_socket, &permfds);
  max_fd = mboggle_socket;
  num_of_players = 0;
  srandom(getpid());
  
  while (1) {
    readfds = permfds;
    nfds = select(max_fd + 1, &readfds, 0, 0, 0);
    if (nfds < 0) {
      if (errno == EINTR) {
	if (time_is_up) {
	  do_stuff();
	}
	continue;
      }
      syslog(LOG_ERR, "select: %m; exiting.");
      exit(1);
    }
    if (FD_ISSET(mboggle_socket, &readfds)) {
      int new;
      if (! new_player_is_ok()) {
	if ((retval = fork()) < 0) {
	  syslog(LOG_ERR, "forking for new daemon: %m");
	}
	else if (retval != 0) {
	  continue;
	}
	else {
	  clear_everything();
	  is_child++;
	}
      }
      new = accept(mboggle_socket, 0, 0);
      if (new < 0) {
	continue;
      }
      new_player(new);
      nfds--;
    }
    while (nfds > 0) {
      for (i = 0; i < num_of_players; i++) {
	if (FD_ISSET(players_int[i].socket, &readfds)) {
	  listen_to_player(i);
	}
	nfds--;
	continue;
      }
      break;
    }
  }
}

get_mboggle_socket(port)
  u_short port;
{
  struct servent *service;
  struct sockaddr_in addr;
  int mboggle_socket;
  int true = 1;
  
#ifdef HESIOD
  if (port == 0) {
    service = hes_getservbyname("mboggle", "tcp");
    if (service) {
      port = service->s_port;
    }
  }
#endif
  
  if (port == 0) {
    service = getservbyname("mboggle", "tcp");
    if (service) {
      port = service->s_port;
    }       
  }
  
#ifdef MBOGGLE_PORT
  if (port == 0) {
    port = htons(MBOGGLE_PORT);
  }
#endif
  
  if (port == 0) {
    syslog(LOG_ERR, "couldn't determine network port number; exiting.");
    exit(1);
  }
  
  memset((char *)&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = port;
  addr.sin_addr.s_addr = INADDR_ANY;
  
  mboggle_socket = socket(AF_INET, SOCK_STREAM, 0);
  if (mboggle_socket < 0) {
    syslog(LOG_ERR, "socket: %m; exiting.");
    exit(1);
  }
  if (setsockopt(mboggle_socket, SOL_SOCKET, SO_REUSEADDR, (char *)true,
		 sizeof(true)) < 0) {
    syslog(LOG_ERR, "setsockopt: %m");
  }
  if (bind(mboggle_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    syslog(LOG_ERR, "bind: %m; exiting.");
    exit(1);
  }
  if (listen(mboggle_socket, 5) < 0) {
    syslog(LOG_ERR, "listen: %m; exiting.");
    exit(1);
  }
  
  return(mboggle_socket);
}


clear_everything()
{
  int i;
  
  for (i = 0; i < num_of_players; i++) {
    FD_CLR(players_int[i].socket, &permfds);
    close(players_int[i].socket);
  }
  num_of_players = 0;
  max_fd = mboggle_socket;
}

do_zephyr()
{
#ifdef STARTZEPHYR
  int child_pid;
#endif
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering do_zephyr()\n");
#endif
#ifdef STARTZEPHYR
  if ((child_pid = fork()) == -1)
    syslog(LOG_ERR, "Couldn't fork to send zephyr message: %m");
  else if (! child_pid) {
    execl(STARTZEPHYR, "zwrite", "-q", "-d", "-i",
	  "boggle", "-m", "Someone is playing boggle!", 0);
    /* should never get here */
    syslog(LOG_ERR, "Exec of zwrite failed: %m");
    exit(0);
  }
#endif
}


int new_player_is_ok()
{
  extern int num_of_players;
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering new_player_is_ok()\n");
#endif
  return(num_of_players < MAX_NUM_OF_PLAYERS);
}


/* ARGSUSED */
new_player(sock)
  int sock;
{
  extern struct player_int players_int[];
  extern struct player_ext players_ext[];
  extern int num_of_players;
  extern char current_tray[5][5];
  
  int name_length;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering new_player()\n");
#endif
  if (safe_read(sock, (char *)&name_length, sizeof(name_length)) < 0) {
    return;
  }
  nlen = ntohl(name_length);
  if (safe_read(sock, (char *)players_ext[num_of_players].name,
		(int) nlen) < 0) {
    return;
  }
  FD_SET(sock, &permfds);
  if (sock > max_fd) {
    max_fd = sock;
  }
  players_int[num_of_players].socket		= sock;
  players_int[num_of_players].num_of_words	= 0;
  players_int[num_of_players].current_word	= 0;
  players_ext[num_of_players].state		= PLAYER_VIEWING;
  players_ext[num_of_players].tray_score	= 0;
  players_ext[num_of_players].total_score	= 0;
  players_ext[num_of_players].ease_rating	= 0;
  players_ext[num_of_players].num_of_games	= 0;
  num_of_players++;
  if (num_of_players == 1) {
    do_zephyr();
  }
  
  do_stuff();
}


/* ARGSUSED */
fix_to_net(players, num)
  struct player_ext players[];
  int num;
{
  extern struct player_ext players_ext[];
  extern int num_of_players;
  register int i;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering fix_to_net()\n");
#endif
  for (i = 0; i < num_of_players; i++) {
    players[i].ease_rating = htonl(players_ext[i].ease_rating);
    players[i].tray_score = htons(players_ext[i].tray_score);
    players[i].total_score = htons(players_ext[i].total_score);
  }
}


listen_to_player(player_num)
{
  extern struct player_ext players_ext[];
  
  char boggle_code;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering listen_to_player()\n");
#endif
  
  /* Find out what he wants. */
  if (safe_read(players_int[player_num].socket,
		&boggle_code, sizeof(boggle_code)) < 0) {
    get_rid_of_player(player_num);
    return;
  }
  
  if (players_ext[player_num].state != PLAYER_IN_QUESTION) {
    players_ext[player_num].state = boggle_code;
  }
  
  broadcast_state_update(player_num, boggle_code);
  
  do_stuff();
}


do_stuff()
{
  static int state_of_game = SOG_WAITING;
  
  if (num_of_players == 0) {
    state_of_game = SOG_WAITING;
    return;
  }
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering do_stuff()\n");
#endif
  switch (state_of_game) {
  case SOG_WAITING:
    goto waiting;
  case SOG_PLAYING:
    goto playing;
  case SOG_SCORING:
    goto scoring;
  default:
    syslog(LOG_ERR, "game in bad state");
    exit(1);
  }
  
  while (1) {
  waiting:
    invite_everyone();
    if (someone_is(PLAYER_INVITED)) break;
    start_game();
    state_of_game = SOG_PLAYING;
  playing:
    if (time_is_up) {
      int i;
      char boggle_code = YOURE_DONE;
      for (i = 0; i < num_of_players; i++) {
	if (players_ext[i].state == PLAYER_PLAYING) {
	  write(players_int[i].socket, (char *) &boggle_code,
		sizeof(boggle_code));
	}
      }
    }
    if (someone_is(PLAYER_PLAYING)) break;
    get_words_from_players();
    continue_scoring();
    state_of_game = SOG_SCORING;
  scoring:
    if (player_in_question >= 0) {
      if (someone_is(PLAYER_DECIDING)) break;
      tally_votes();
      if (player_in_question >= 0) break;
      continue_scoring();
      if (player_in_question >= 0) break;
    }
    accumulate_scores();
    state_of_game = SOG_WAITING;
  }
  
  broadcast_state();
}


invite_everyone()
{
  extern struct player_ext players_ext[];
  extern struct player_int players_int[];
  extern int num_of_players;
  
  register int i;
  char boggle_code;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering invite_everyone()\n");
#endif
  boggle_code = WANT_TO_PLAY;
  for (i = 0; i < num_of_players; i++) {
    switch (players_ext[i].state) {
    case PLAYER_READY:
    case PLAYER_PASSING:
    case PLAYER_INVITED:
      break;
    default:
      write(players_int[i].socket, &boggle_code, sizeof(boggle_code));
      players_ext[i].state = PLAYER_INVITED;
      break;
    }
  }
}


time_up()
{
  time_is_up = 1;
}

start_game()
{
  extern struct player_int players_int[];
  extern struct player_ext players_ext[];
  extern int num_of_players, num_of_active_players;
  extern char current_tray[5][5];
  
  char boggle_code;
  register int i, sock;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering start_game()\n");
#endif
  if (! dictionary_started) {
    initialize_hash_tables(4, 25);
    dictionary_started++;
  }
  shake_tray(current_tray);
  num_of_active_players = 0;
  for (i = 0; i < num_of_players; i++) {
    sock = players_int[i].socket;
    if (players_ext[i].state == PLAYER_PASSING) {
      boggle_code = CURRENT_TRAY;
      players_ext[i].state = PLAYER_VIEWING;
    } else {
      boggle_code = NEW_GAME;
      players_ext[i].state = PLAYER_PLAYING;
      num_of_active_players++;
    }
    write(sock, &boggle_code, sizeof(boggle_code));
    write(sock, (char *)current_tray, 25 * sizeof(char));
  }
  time_is_up = 0;
  signal(SIGALRM, (void(*)())time_up);
#ifdef SHORT_TIMER
  alarm(SHORT_TIMER+10);
#else
  alarm(190);
#endif
}


continue_scoring()
{
  extern int num_of_active_players, player_in_question;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering continue_scoring()\n");
#endif
  switch (num_of_active_players) {
  case 0:
    player_in_question = -1;
    break;
  case 1:
    score_one_player();
    break;
  default:
    score_multiple_players();
    break;
  }
}


score_one_player()
{
  extern struct player_ext players_ext[];
  extern struct player_int players_int[];
  extern int player_in_question;
  
  register struct player_int *pi;
  register int current_word, num_of_words;
  int player_num;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering score_one_player()\n");
#endif
  for (player_num= 0; player_num < num_of_players; player_num++) {
    if (players_ext[player_num].state != PLAYER_VIEWING) break;
  }
  
  pi = &players_int[player_num];
  current_word = pi->current_word;
  num_of_words = pi->num_of_words;
  
  while (current_word < num_of_words) {
    if (check_word(pi->words[current_word])) {
      give_player_word(player_num, pi->words[current_word]);
    }
    current_word++;
  }
  
  pi->current_word = current_word;
  player_in_question = -1;
}


score_multiple_players()
{
  extern struct player_ext players_ext[];
  extern struct player_int players_int[];
  extern int num_of_players, player_in_question;
  
  register int i, sock;
  struct player_int *piq_int;
  struct player_ext *piq_ext;
  char *word, boggle_code;
  int wordlen;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering score_multiple_players()\n");
#endif
  if ((player_in_question = next_disputed_word()) >= 0) {
    
    /* We need a vote.  Each person needs to be sent the name of the person
       who thought up the word and the word itself.  The viewers and the
       person involved get the message with the THEY_ARE_VOTING code; everyone
       else gets it with the WE_ARE_VOTING code. */
    
    piq_int = &players_int[player_in_question];
    piq_ext = &players_ext[player_in_question];
    
    piq_ext->state = PLAYER_IN_QUESTION;
    
    /* Add one to the word length so that we will include the trailing null. */
    
    word = piq_int->words[piq_int->current_word];
    wordlen = strlen(word) + 1;
    
    nlen = htonl(wordlen);
    for (i = 0; i < num_of_players; i++) {
      if ((i == player_in_question) || (players_ext[i].state == PLAYER_VIEWING)) {
	boggle_code = THEY_ARE_VOTING;
      }
      else {
	players_ext[i].state = PLAYER_DECIDING;
	boggle_code = WE_ARE_VOTING;
      }
      sock = players_int[i].socket;
      write(sock, &boggle_code, sizeof(boggle_code));
      write(sock, piq_ext->name, MAX_NAME_LENGTH);
      write(sock, &nlen, sizeof(nlen));
      write(sock, word, wordlen);
    }
  }
}


tally_votes()
{
  extern struct player_int players_int[];
  extern int player_in_question, num_of_players;
  
  register int i, yes_votes, no_votes;
  struct player_int *piq_int;
  char *word;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering tally_votes()\n");
#endif
  yes_votes = 0;
  no_votes = 0;
  for (i = 0; i < num_of_players; i++) {
    switch (players_ext[i].state) {
    case PLAYER_VOTES_YES:
      yes_votes++;
      break;
    case PLAYER_VOTES_NO:
      no_votes++;
      break;
    case PLAYER_DECIDING:
      return;
    }
  }
  
  if (no_votes == 0) {
    /* If no one objects to the word, let him have it, whether or not
       someone actively agreed. */
    piq_int = &players_int[player_in_question];
    word = piq_int->words[piq_int->current_word++];
    give_player_word(player_in_question, word);
    player_in_question = -1;
  }
  else if (yes_votes == 0) {
    /* Someone objected, and no one else seemed to like it either.
       Don't give the player any points, just go on to the next
       word. */
    players_int[player_in_question].current_word++;
    player_in_question = -1;
  }
}


int next_disputed_word()
{
  extern struct player_int players_int[];
  extern int num_of_players;
  
  register int i;
  register struct player_int *pi;
  int unique, player_in_question, cmp;
  char *word_in_question;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering next_disputed_word()\n");
#endif
  while (1) {
    
    player_in_question = -1;
    word_in_question = "~~~~~~~~~~~~~~~~~~~~~~~~~~";
    
    /* First, find the player with the first word in alphabetical order. */
    for (i = 0; i < num_of_players; i++) {
      pi = &players_int[i];
      if (pi->current_word < pi->num_of_words) {
	cmp = strcmp(pi->words[pi->current_word], word_in_question);
	if (cmp < 0) {
	  unique = 1;
	  player_in_question = i;
	  word_in_question = pi->words[pi->current_word];
	}
	else if (cmp == 0) {
	  unique = 0;
	}
      }
    }
    
    /* If no one had any word, we are done. */
    if (player_in_question < 0) return(-1);
    
    /* If we found a person, and his word was unique, return him. */
    if (unique) {
      if (check_word(word_in_question)) {
	give_player_word(player_in_question, word_in_question);
	players_int[player_in_question].current_word++;
	continue;
      }
      else return(player_in_question);
    }
    
    players_int[player_in_question].current_word++;
    for (i = player_in_question + 1; i < num_of_players; i++) {
      pi = &players_int[i];
      if (pi->current_word < pi->num_of_words) {
	if (strcmp(pi->words[pi->current_word], word_in_question) == 0) {
	  pi->current_word += 1;
	}
      }
    }
  }
}

give_player_word(player_num, word)
  int player_num;
  char *word;
{
  extern struct player_int players_int[];
  extern struct player_ext players_ext[];
  extern int word_values[], num_of_players, num_of_active_players;
  
  register int val, i, sock;
  register float ease;
  int wordlen;
  char boggle_code;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering give_player_word()\n");
#endif
  wordlen = strlen(word);
  val = word_values[wordlen];
  players_ext[player_num].tray_score += val;
  
  ease = ((float) val) / num_of_active_players;
  for (i = 0; i < num_of_players; i++) {
    if (players_ext[i].state != PLAYER_VIEWING) {
      players_ext[i].ease_rating += (int) (ease * 100);
    }
  }
  
  wordlen++;
  nlen = htonl(wordlen);
  
  boggle_code = WINNING_WORD;
  sock = players_int[player_num].socket;
  write(sock, &boggle_code, sizeof(boggle_code));
  write(sock, &nlen, sizeof(nlen));
  write(sock, word, wordlen);
  
  send_scored_word(word);
}


accumulate_scores()
{
  extern struct player_ext players_ext[];
  extern int num_of_players;
  
  register struct player_ext *pe;
  register int i;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering accumulate_scores()\n");
#endif
  for (i = 0; i < num_of_players; i++) {
    pe = &players_ext[i];
    if (pe->state != PLAYER_VIEWING) {
      pe->total_score += pe->tray_score;
      pe->num_of_games++;
    }
  }
}


get_words_from_players()
{
  extern struct player_int players_int[];
  extern struct player_ext players_ext[];
  extern int num_of_players;
  
  struct player_int *p;
  char boggle_code;
  int num_of_words;
  register int sock, i;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering get_words_from_players()\n");
#endif
  boggle_code = SEND_WORDS;
  for (i = 0; i < num_of_players; i++) {
    if (players_ext[i].state == PLAYER_DONE) {
      p = &players_int[i];
      sock = p->socket;
      write(sock, &boggle_code, sizeof(boggle_code));
      if (safe_read(sock, (char *)&nlen,
		    sizeof(nlen)) < 0) {
	get_rid_of_player(i);
	return;
      }
      num_of_words = ntohl(nlen);
      if (num_of_words > 0) {
	if (safe_read(sock, (char *)p->words,
		      num_of_words * MAX_WORD_LENGTH) < 0) {
	  get_rid_of_player(i);
	  return;
	}
      }
      p->num_of_words = num_of_words;
#ifdef DEBUG
      syslog(LOG_DEBUG, "Got %d words from player %d",
	     num_of_words, i);
#endif			
      p->current_word = 0;
      players_ext[i].tray_score = 0;
    }
  }
}


int find_player_by_socket(sock)
  int sock;
{
  extern struct player_int players_int[];
  extern int num_of_players;
  
  register int i;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering find_player_by_socket()\n");
#endif
  for (i = 0; i < num_of_players; i++) {
    if (players_int[i].socket == sock) return(i);
  }
  return(-1);
}


char cubes[25][6] = {
  {'p', 'r', 'y', 'f', 'i', 's'},
  {'c', 'l', 'p', 'e', 'i', 't'},
  {'a', 'e', 'e', 'e', 'e', 'm'},
  {'t', 'o', 'u', 'o', 'o', 't'},
  {'e', 'o', 't', 'm', 't', 't'},
  {'p', 'r', 'y', 'i', 'r', 'r'},
  {'h', 'n', 'o', 'd', 'l', 'r'},
  {'e', 'n', 's', 's', 's', 'u'},
  {'a', 'g', 'u', 'e', 'e', 'm'},
  {'a', 'f', 's', 'a', 'a', 'r'},
  {'h', 'l', 'o', 'd', 'h', 'r'},
  {'a', 'm', 'n', 'e', 'g', 'n'},
  {'j', 'q', 'x', 'b', 'k', 'z'},
  {'d', 'o', 't', 'd', 'h', 'n'},
  {'c', 'p', 't', 'e', 'i', 's'},
  {'c', 'c', 's', 'e', 'n', 't'},
  {'a', 'e', 'e', 'a', 'e', 'e'},
  {'d', 'n', 'r', 'h', 'l', 'o'},
  {'e', 'i', 't', 'i', 'i', 't'},
  {'a', 'f', 'r', 'i', 's', 'y'},
  {'i', 'i', 't', 'c', 'e', 'l'},
  {'a', 'e', 'n', 'd', 'n', 'n'},
  {'r', 'o', 'v', 'g', 'r', 'w'},
  {'a', 'f', 'r', 'a', 'i', 's'},
  {'n', 'o', 't', 'o', 'u', 'w'}};


shake_tray(tray)
  char tray[5][5];
{
  register int x, y, i, cubes_taken, cubes_remaining, cube_number;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering shake_tray()\n");
#endif
  cubes_taken = 0;
  cubes_remaining = 25;
  for (x = 0; x < 5; x++) {
    for (y = 0; y < 5; y++) {
      cube_number = (int) random() % cubes_remaining;
      for (i = 0; i <= cube_number; i++) {
	if ((1 << i) & cubes_taken) {
	  cube_number++;
	}
      }
      tray[x][y] = cubes[cube_number][((int)random()) % 6];
      cubes_taken |= (1 << cube_number);
      cubes_remaining--;
    }
  }
}


get_rid_of_player(player_num)
  int player_num;
{
  int i;
  extern struct player_int players_int[];
  extern struct player_ext players_ext[];
  extern int num_of_players, player_in_question, num_of_active_players;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering get_rid_of_player()\n");
#endif
  if (players_ext[player_num].state != PLAYER_VIEWING)
    num_of_active_players--;
  
  if (player_num == player_in_question)
    player_in_question = -1;
  
  --num_of_players;
  if (is_child && num_of_players == 0) {
    exit(0);
  }
  
  /* If we are about to die, kill the dictionary. */
  /*	if (num_of_players == 0) {
   *      kill_dictionary();
   * }
   */
  
  FD_CLR(players_int[player_num].socket, &permfds);
  (void) close(players_int[player_num].socket);
  
  /* Fill in the hole. */
  players_int[player_num] = players_int[num_of_players];
  players_ext[player_num] = players_ext[num_of_players];
  
  max_fd = mboggle_socket;
  for (i = 0; i < num_of_players; i++) {
    if (players_int[i].socket > max_fd) {
      max_fd = players_int[i].socket;
    }
  }
  
  if (num_of_players) {
    do_stuff();
  }
}


broadcast_state()
{
  extern struct player_ext players_ext[];
  extern struct player_int players_int[];
  extern int num_of_players;
  
  static char buf[4096];
  register int i, len;
  
  /* This routine is the main reason that player_ext and player_int
     are kept separately. */
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering broadcast_state()\n");
#endif
  memcpy(players_netbyte, players_ext, num_of_players * sizeof(struct player_ext));
  fix_to_net(players_netbyte, num_of_players);
  
  buf[0] = GAME_STATE;
  nlen = htonl(num_of_players);
  bcopy((char *)&nlen, buf + 1, sizeof(nlen));
  bcopy((char *)players_netbyte, buf + 1 + sizeof(num_of_players), num_of_players * sizeof(struct player_ext));
  
  len = 1 + sizeof(num_of_players) + (num_of_players * sizeof(struct player_ext));
  
  for (i = 0; i < num_of_players; i++) {
    write(players_int[i].socket, buf, len);
  }
}


broadcast_state_update(player_num, state)
  int player_num;
  int state;
{
  extern struct player_int players_int[];
  extern int num_of_players;
  
  static char buf[3] = {STATE_UPDATE, '\0', '\0'};
  register int i;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering broadcast_state_update()\n");
#endif
  buf[1] = (char) player_num;
  buf[2] = (char) state;
  for (i = 0; i < num_of_players; i++) {
    write(players_int[i].socket, buf, 3);
  }
}


broadcast_all_state_updates()
{
  extern struct player_ext players_ext[];
  extern struct player_int players_int[];
  extern int num_of_players;
  
  static char buf[3 * MAX_NUM_OF_PLAYERS];
  register int i, len;
  char *ptr;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering broadcast_all_state_updates()\n");
#endif
  ptr = buf;
  for (i = 0; i < num_of_players; i++) {
    ptr[0] = STATE_UPDATE;
    ptr[1] = i;
    ptr[2] = players_ext[i].state;
    ptr += 3;
  }
  
  len = 3 * num_of_players;
  for (i = 0; i < num_of_players; i++) {
    write(players_int[i].socket, buf, len);
  }
}


int someone_is(state)
  int state;
{
  extern int num_of_players;
  extern struct player_ext players_ext[];
  
  register int i;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering someone_is()\n");
#endif
  for (i = 0; i < num_of_players; i++) {
    if (players_ext[i].state == state) return(1);
  }
  return(0);
}


void send_scored_word(word)
  char *word;
{
  char message;
  int i;
  
#ifdef DEBUG
  syslog(LOG_DEBUG, "Entering send_scored_word(), sending %s\n", word);
#endif
  
  message = SCORED_WORD;
  
  for (i = 0; i < num_of_players; i++) {
    write(players_int[i].socket, &message, sizeof(char));
    write(players_int[i].socket, word, MAX_WORD_LENGTH);
  }
}

