/*
 * File: acm98.cpp
 * Author: Chris Alfeld (calfeld@cs.utah.edu)
 *
 * Description: This file is the main driver for the player.  It
 * defines main(), handles io, maintains the GAME structure, etc.
 * It will call the routines NewGame, NewRound, TakeCard, PlayCard,
 * EndTake, and EndRound appropriately.  These routines are defined 
 * in shell.cpp and should be filled in by the contestent.
 *
 * Do NOT modify this file. 
 */

#include "acm98.h"
#include <stdio.h>

acm_gameinfo GAME;

/*
 * There is a routine for each element of the protocol.  These routines
 * take a character string that has the first two characters removed
 * (the type character and the following space).
 * These routines are:
 */
void round_start();
void pool_take();
void take_result();
void play();
void trick_result();
void round_end();

/*
 * These routines send a client_take or client_play back
 */
void client_take(acm_card card);
void client_play(acm_card card);

/*
 * IO Functions
 */
int get_string(char s[32][20]);

/*
 * Help functions
 * trim_pool - Trim cards takes a acm_play structure and removes those cards
 *             from the pool.
 */
void trim_pool(acm_play play);
void parse_card(acm_card *card,char suit,char face);
void parse_play(acm_play *play);
int card_equals(acm_card a,acm_card b);
void zero_card(acm_card *card);
void zero_play(acm_play *play);

char input[32][20];
int input_ind;

/*
 * main()
 * The main procedure.  This sets up everything, reads input, does
 * prilinary parsing, and passes it on to the appropriate function.
 */
int
main()
{
  /*
   * Call NewGame
   */
  NewGame();

  /*
   * input loop
   */
  while (get_string(input) != 0) {
    input_ind=1;
    switch (input[0][0]) {
    case 's' :
      round_start();
      break;
    case 't':
      pool_take();
      break;
    case 'a':
      take_result();
      break;
    case 'p':
      play();
      break;
    case 'r':
      trick_result();
      break;
    case 'e':
      round_end();
      break;
    default:
      /* just ignore output*/
      break;
    }
    fflush(stdout);
  }
  return 0;
}
  
/*
 * round_start()
 * This function parses a round_start line.  It sets up GAME and
 * calls NewRound.
 */
void round_start()
{
  register loop_var;
  char card_string[3];
  
  /*
   * Initialize GAME.
   */
  zero_play(&(GAME.last_trick));
  zero_play(&(GAME.cur_trick));
  zero_play(&(GAME.last_take));
  zero_play(&(GAME.cur_take));
  GAME.pool_size=12;
  GAME.hand_size=10;
  for (loop_var=10;loop_var<13;++loop_var)
    zero_card(&(GAME.hand[loop_var]));
  GAME.trick_number=-3;
  GAME.scores.north=GAME.scores.south=GAME.scores.east=GAME.scores.west=0;

  card_string[2]='\0';
  
  /*
   * Parse string
   */
  GAME.player = input[input_ind++][0];
  for (loop_var=0;loop_var<10;++loop_var) { 
    card_string[0] = input[input_ind][0];
    card_string[1] = input[input_ind++][1];
    parse_card(&(GAME.hand[loop_var]),card_string[0],card_string[1]);
  }
  for (loop_var=0;loop_var<12;++loop_var) {
    card_string[0] = input[input_ind][0];
    card_string[1] = input[input_ind++][1];
    parse_card(&(GAME.pool[loop_var]),card_string[0],card_string[1]);
  }

  /*
   * Call NewRound
   */
  NewRound();
}

/*
 * trim_pool(acm_play play)
 * Removes cards in play from pool.
 */
void trim_pool(acm_play play)
{
  int card_loc;
  int last_card;
  
  for (card_loc=0;card_loc<GAME.pool_size;++card_loc)
    if (card_equals(GAME.pool[card_loc],play.north) ||
	card_equals(GAME.pool[card_loc],play.east) ||
	card_equals(GAME.pool[card_loc],play.south) ||
	card_equals(GAME.pool[card_loc],play.west)) {
      zero_card(&(GAME.pool[card_loc]));
    }
  for (last_card=GAME.pool_size-1;GAME.pool[last_card].suit=='\0';--last_card);
  for (card_loc=0;card_loc<last_card;++card_loc)
    if (GAME.pool[card_loc].suit=='\0') {
      GAME.pool[card_loc]=GAME.pool[last_card];
      zero_card(&(GAME.pool[last_card]));
      for (last_card--;last_card>card_loc && GAME.pool[last_card].suit=='\0';--last_card);
    }
  for (GAME.pool_size=0;GAME.pool[GAME.pool_size].suit != '\0' && GAME.pool_size < 12;++GAME.pool_size);
}
  
/*
 * pool_take(char* line)
 * This sets up cur_take, calls TakeCard, and sends the result
 * as a responce.
 */
void pool_take()
{
  GAME.trick_number++;
  
  parse_play(&(GAME.cur_take));
  trim_pool(GAME.cur_take);
  
  client_take(TakeCard());
}

/*
 * take_result(char* line)
 * Simply sets up GAME.last_take
 */
void take_result()
{
  parse_play(&(GAME.last_take));
  trim_pool(GAME.last_take);
  
  if (GAME.trick_number == 0)
    EndTakes();
} 
  
/*
 * play(char* line)
 * Parses a play request, sets up current trick, and handles PlayCard.
 */
void play()
{
  GAME.trick_number++;

  parse_play(&(GAME.cur_trick));

  client_play(PlayCard());
}

/*
  card_cmp(char lead_suit,acm_card A,acm_card B)
  Compares two cards.  -1 if A < B, 0 if A == B, 1 if A > B
  */
int card_cmp(char lead_suit,acm_card A,acm_card B)
{
  int A_val = 0, B_val = 0;
  const char card_values[14] = {'\0','2','3','4','5','6','7','8','9',
			  't','j','q','k','a'};
    
  if (A.suit == lead_suit)
    for (A_val=0;A.face != card_values[A_val] && A_val<14;++A_val);
  if (B.suit == lead_suit)
    for (B_val=0;B.face != card_values[B_val] && B_val<14;++B_val);
  if (A_val == B_val) return 0;
  if (A_val < B_val) return -1;
  if (A_val > B_val) return 1;

  return 0;
}

/*
 * trick_result(char* line)
 * Just fills in GAME.last_trick.
 */
void trick_result()
{
  char took_trick='n';
  acm_card* cards[4];
  acm_card* highest=&(GAME.last_trick.north);
  const char play_table[4] = {'n','e','s','w'};
  register loop_var;
  int score=0;

  parse_play(&(GAME.last_trick));
 
  cards[0]=&(GAME.last_trick.north);
  cards[1]=&(GAME.last_trick.east);
  cards[2]=&(GAME.last_trick.south);
  cards[3]=&(GAME.last_trick.west);

  for (loop_var=1;loop_var<4;++loop_var)
    if (card_cmp(GAME.last_trick.lead->suit,
		   *(cards[loop_var]),
		   *highest) == 1) {
      highest=cards[loop_var];
      took_trick=play_table[loop_var];
    }

  if (GAME.trick_number == 1 || GAME.trick_number == 13)
    score = 3;
  else
    score = 1;
  for (loop_var=0;loop_var<4;++loop_var)
    if (cards[loop_var]->face == '8')
      score += 2;

  switch (took_trick) {
  case 'n':
    GAME.scores.north += score;
    break;
  case 'e':
    GAME.scores.east += score;
    break;
  case 's':
    GAME.scores.south += score;
    break;
  case 'w':
    GAME.scores.west += score;
    break;
  }
}

/*
 * round_end()
 * Parse the round_end tag, set up scores, call EndRound.
 */
void round_end()
{
  EndRound();
}

/*
 * client_take(acm_card card)
 * Sends a take responce.
 */
void client_take(acm_card card)
{
  GAME.hand[GAME.hand_size++]=card;

  printf("%c%c\n",card.suit,card.face);
}

/*
 * client_play(acm_card card)
 * Sneds a play responce.
 */
void client_play(acm_card card)
{
  int card_loc;
  for (card_loc=0;card_loc < GAME.hand_size && ! card_equals(GAME.hand[card_loc],card);++card_loc);
  for (;card_loc < GAME.hand_size;++card_loc)
    GAME.hand[card_loc]=GAME.hand[card_loc+1];
  GAME.hand_size--;

  printf("%c%c\n",card.suit,card.face);
}

/*
 * card_equals(acm_card a,acm_card b)
 * Simply returns 1 if two card are identical.
 */
int card_equals(acm_card a,acm_card b)
{
  return a.suit == b.suit && a.face == b.face;
}

/*
 * parse_card(acm_card *card,char suit,char face)
 * Given a pointer to a card and a suit and face
 * will fill in the card structure.
 */
void parse_card(acm_card *card,char suit,char face)
{
  card->suit=suit;
  card->face=face;
}

/*
 * parse_play(acm_play *play)
 * A frequently used procedure, will read from input and set up
 * an acm_play structure.
 */
void parse_play(acm_play *play)
{
  char who;
  char card[3];
  
  zero_play(play);
  who = input[input_ind++][0];
  play->leader=who;
  for (;;) {
    card[0]=input[input_ind][0];
    card[1]=input[input_ind++][1];
    if (card[0] == '\0') break;
    switch (who) {
    case 'n' :
      parse_card(&(play->north),card[0],card[1]);
      break;
    case 'e' :
      parse_card(&(play->east),card[0],card[1]);
      break;
    case 's' :
      parse_card(&(play->south),card[0],card[1]);
      break;
    case 'w' :
      parse_card(&(play->west),card[0],card[1]);
      break;
    }
    who = input[input_ind++][0];
  }
  switch (play->leader) {
  case 'n':
    play->lead=&(play->north);
    break;
  case 'e':
    play->lead=&(play->east);
    break;
  case 's':
    play->lead=&(play->south);
    break;
  case 'w':
    play->lead=&(play->west);
    break;
  }
}

/*
 * zero_card(acm_card *card)
 * Sets a card to blank.
 */
void zero_card(acm_card *card)
{
  card->suit=card->face='\0';
}

/*
 * zero_play(acm_play *play)
 * Resets an acm_play struct.
 */
void zero_play(acm_play *play)
{
  play->lead=NULL;
  play->leader='\0';
  play->north.suit=play->north.face='\0';
  play->south.suit=play->south.face='\0';
  play->east.suit=play->east.face='\0';
  play->west.suit=play->west.face='\0';
}
  
int get_string(char s[32][20])
{
  char message[20*32];
  register i,j;

  /* slow but very portable */
  for (i=0;i<32;++i)
    for (j=0;j<20;++j)
      s[i][j]='\0';
  
  if (fgets(message,sizeof(message),stdin) == NULL)
    return 0;

  return sscanf(message,"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
	s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],
	s[8],s[9],s[10],s[11],s[12],s[13],s[14],
	s[15],s[16],s[17],s[18],s[19],s[20],s[21],s[22],s[23],
	s[24],s[25],s[26],s[27],s[28],s[29],s[30],s[31]);
}


