/*****************************************************************************
 *
 * gosh.c
 * 
 * gosh is a set of Go functions.
 *
 ****************************************************************************/

/*#define DEBUGFLAG*/
#define MODULE "gosh"

#include <stdio.h>
#include "gosh.h"

/*****************************************************************************
 * other(color)
 *   int color;
 * other will return the opposite color of 'color'.
 * if color is black, then other(c) will be white.
 * if color is white, then other(c) will be black.
 *****************************************************************************/

int other(color)
     int color;
{
  return( (color==BLACK) ? WHITE : BLACK);
}

/*****************************************************************************
 * IsInBoard(board, x, y)
 *  - returns TRUE is x,y is in the bounds of the board. 
 *  - returns FALSE if not.
 ****************************************************************************/

int IsInBoard(board, x, y)
     Board *board;
     int x,y;
{
  if(x>=0 && y>=0 && x<board->sizex && y<board->sizey)
    return(TRUE);
  else
    return(FALSE);
}

/*****************************************************************************
 * AddStone(board, x, y, color)
 *  - Adds a stone to board at position x,y of color.
 ****************************************************************************/

int AddStone(board, x, y, color)
     Board *board;
     int x,y;
     int color;
{
  if(IsInBoard(board, x, y))
    board->grid[x][y] = color;
  else
    printf("Error in AddStone: x,y coordinates out of bounds\n");
}

/*****************************************************************************
 * GetStone(board, x, y)
 *  - returns the stone at x,y.
 ****************************************************************************/

int GetStone(board, x, y)
     Board *board;
     int x,y;
{
  if(IsInBoard(board, x, y))
    return(board->grid[x][y]);
  else 
    printf("Error in GetStone: x,y coordinates out of bounds\n");
  return(FALSE);
}

/*****************************************************************************
 * CountStones(board, color)
 *  - CountStones examines the playing board and counts up the number of 'color'ed
 *    stones.
 *****************************************************************************/

int CountStones(board, color)
     Board *board;
     int color;
{
  register int i,j;
  register int count=0;

  for(i=0; i<board->sizex; i++)
    for(j=0; j<board->sizey; j++)
      if(board->grid[i][j]==color)
	count++;
  return(count);
}

/*****************************************************************************
 * CountStones(board)
 * CountStones examines the playing board and counts all of the stones played.
 *****************************************************************************/

int CountAllStones(board)
     Board *board;
{
  register int i,j;
  register int count=0;
  for(i=0; i<board->sizex; i++)
    for(j=0; j<board->sizey; j++)
      if(board->grid[i][j]!=EMPTY)
	count++;
  return(count);
}

CountCapturedStones(board, cbs, cws)
     Board *board;
     int *cbs, *cws;
{
  MoveTree *head, *node;


  *cbs = *cws = 0;
  head = board->movetree->next;

  if(!IsEmpty(head)) {
    while(!IsEmpty(head)) {
      node = head->kill;
      if(!IsEmpty(node)) {
	node = node->next;
	while(!IsEmpty(node)) {
	  if(node->color == BLACK)
	    (*cbs)++;
	  else if(node->color == WHITE)
	    (*cws)++;
	  node = node->next;
	}
      }
      head = head->next;
    }
  }
}

/*****************************************************************************
 * InitBoard(board)
 *  - clear the board of stones.
 *****************************************************************************/

InitBoard(board)
     Board *board;
{
  register int i,j;
  for(i=0; i<board->sizex; i++)
    for(j=0; j<board->sizey; j++)
      board->grid[i][j] = EMPTY;
}

/*****************************************************************************
 * InitPlayBoard(board, handicap)
 *  - clear board and set the handicap stones.
 ****************************************************************************/

InitPlayBoard(board)
     Board *board;
{
  InitBoard(board);

  switch(board->handicap) {
    case(17): AddStone(board,6,board->sizey-7,BLACK);
    case(16): AddStone(board,board->sizex-7,6,BLACK);
    case(15): AddStone(board,board->sizex-7,board->sizey-7,BLACK);
    case(14): AddStone(board,6,6,BLACK);
    case(13): AddStone(board,board->sizex-3,2,BLACK);
    case(12): AddStone(board,2,board->sizey-3,BLACK);
    case(11): AddStone(board,board->sizex-3,board->sizey-3,BLACK);
    case(10): AddStone(board,2,2,BLACK);
    case( 9): AddStone(board,board->sizex/2,board->sizey-4,BLACK);
    case( 8): AddStone(board,3,board->sizey/2,BLACK);
    case( 7): AddStone(board,board->sizex/2,3,BLACK);
    case( 6): AddStone(board,board->sizex-4,board->sizey/2,BLACK);
    case( 5): AddStone(board,board->sizex/2,board->sizey/2,BLACK);
    case( 4): AddStone(board,board->sizex-4,board->sizey-4,BLACK);
    case( 3): AddStone(board,3,board->sizey-4,BLACK);
    case( 2): AddStone(board,board->sizex-4,3,BLACK);
    case( 1): AddStone(board,3,3,BLACK);
  }
}

/*****************************************************************************
 * CheckLibs(board, workboard, x, y, color)
 *  - returns the number of liberties if a stone of color is placed
 *    at position x,y.
 ****************************************************************************/

int CheckLibs(board, workboard, x,y,color)
     Board *board;
     Board *workboard;
     int x,y,color;
{
  DEBUG("CheckLibs", IN);

  /* is the stone is the bounds of the board? */
  if(IsInBoard(board, x, y)==FALSE)
    return(0);

  /* is the position already worked on? */
  if(GetStone(workboard, x, y) != EMPTY)
    return(0);

  /* is the position a liberty? */
  if(GetStone(board, x, y) == EMPTY) {
    AddStone(workboard, x, y, LIB);
    return(1);
  }

  /* is the position the other players stone? */
  if(GetStone(board, x, y) == other(color)) {
    AddStone(workboard, x, y, other(color));
    return(0);
  }

  /* Mark this position as your stone. */
  AddStone(workboard,x,y,color);
  
  /* Check liberties surrounding this position. */
  return(CheckLibs(board, workboard, x+1, y, color) +
	 CheckLibs(board, workboard, x-1, y, color) +
	 CheckLibs(board, workboard, x, y-1, color) +
	 CheckLibs(board, workboard, x, y+1, color));
}

/*****************************************************************************
 * PlaceStoneOnBoard(board, x, y, color)
 *   Places a stone on board at position x,y of color.  It will remove
 *   dead groups in the process.
 *   the number of liberties are returned. 
 ****************************************************************************/

int PlaceStoneOnBoard(board, x, y, color)
     Board *board;
     int x,y,color;
{
  Board workboard;

  DEBUG("PlaceStoneOnBoard", IN);
  workboard.sizex = board->sizex;
  workboard.sizey = board->sizey;

  InitBoard(&workboard);
  if(CheckLibs(board, &workboard, x+1, y, other(color)) == 0)
    KillStones(board, x+1, y, other(color));

  InitBoard(&workboard);
  if(CheckLibs(board, &workboard, x-1, y, other(color)) == 0)
    KillStones(board, x-1, y, other(color));

  InitBoard(&workboard);
  if(CheckLibs(board, &workboard, x, y+1, other(color)) == 0)
    KillStones(board, x, y+1, other(color));

  InitBoard(&workboard);
  if(CheckLibs(board, &workboard, x, y-1, other(color)) == 0)
    KillStones(board, x, y-1, other(color));

  InitBoard(&workboard);
  DEBUG("PlaceStoneOnBoard", OUT);
  return(CheckLibs(board, &workboard, x+1, y, color) +
         CheckLibs(board, &workboard, x-1, y, color) +
         CheckLibs(board, &workboard, x, y+1, color) +
	 CheckLibs(board, &workboard, x, y-1, color));
}

/*****************************************************************************
 * KillStone(board, x, y, color)
 *  - removes the dead stones starting from x,y of color.
 *****************************************************************************/

KillStones(board, x, y, color)
     Board *board;
     int x,y,color;
{
  DEBUG("KillStones", IN);
  if(IsInBoard(board, x, y)==FALSE)
    return;

  if(GetStone(board, x, y) == color) {
    AddStone(board, x, y, EMPTY);
    if(board->movetree->next->kill == NULL) {
      board->movetree->next->kill = InitMoveTree();
    } 
    AddNewNode(board->movetree->next->kill, x, y, color);
  }
  else
    return;

  KillStones(board, x+1, y, color);
  KillStones(board, x-1, y, color);
  KillStones(board, x, y-1, color);
  KillStones(board, x, y+1, color);
}

/*****************************************************************************
 * InitMoveTree()
 ****************************************************************************/

MoveTree *InitMoveTree()
{
  MoveTree *tree;
  tree = (MoveTree *)malloc(sizeof(MoveTree));
  tree->next = NULL;
  return(tree);
}
/*****************************************************************************
 * DestroyMoveTree()
 ****************************************************************************/

DestroyMoveTree(head)
     MoveTree *head;
{
  MoveTree *end;
  if(head != NULL) {
    end = head->next;
    while(end != NULL) {
      free((char *)head);
      head = end;
      end = head->next;
    }
  }
}

/*****************************************************************************
 * EndOfList(head)
 ****************************************************************************/

MoveTree *EndOfList(head)
     MoveTree *head;
{
  if(head != NULL)     
    while(head->next != NULL)
      head = head->next;
  return(head);
}

/*****************************************************************************
 * IsEmpty(node)
 ****************************************************************************/

int IsEmpty(node)
     MoveTree *node;
{
  if(node == NULL)
    return(TRUE);
  else
    return(FALSE);
}

/*****************************************************************************
 * NextNode(node)
 ****************************************************************************/

MoveTree *NextNode(node)
     MoveTree *node;
{
  if(!IsEmpty(node))
    node = node->next;
  return(node);
}

/*****************************************************************************
 * PrevNode(head,node)
 ****************************************************************************/

MoveTree *PrevNode(head, node)
     MoveTree *head, *node;
{
  if(!IsEmpty(head) && !(IsEmpty(head->next)))
    while(head->next != NULL && head->next != node) 
      head = NextNode(head);
  return(head);
}

/*****************************************************************************
 * InsertNode(head, node)
 ****************************************************************************/

InsertNode(head, node)
     MoveTree *head, *node;
{
  node->next = head->next;
  head->next = node;
}

/*****************************************************************************
 * AddNewNode(head, x, y, color)
 ****************************************************************************/
MoveTree *AddNewNode(head, x, y, color)
     MoveTree *head;
     int x,y, color;
{
  MoveTree *node;
  node = InitMoveTree();
  InsertNode(head, node);
  node->x = x;
  node->y = y;
  node->color = color;
  node->kill = NULL;
  return(node);
}

DeleteNode(head)
     MoveTree *head;
{
  MoveTree *node;

  if(!IsEmpty(head->next)) {
    node = NextNode(head);
    if(!IsEmpty(node) && !IsEmpty(node->kill))
      DestroyMoveTree(node->kill);
    head->next = NextNode(node);
    free(node);
  }
}

/*****************************************************************************
 * PrintList(head)
 ****************************************************************************/
PrintList(head)
     MoveTree *head;
{
  int i;
  i=1;
  if(!IsEmpty(head)) {
    head = NextNode(head);
    while(!IsEmpty(head)) {
      if(head->color == WHITE)
	printf("%d: %dx%d WHITE\n", i, head->x, head->y);
      else
	printf("%d: %dx%d BLACK\n", i, head->x, head->y);
      i++;
      if(!IsEmpty(head->kill)) {
	printf("Start of Kill list:\n");
	PrintList(head->kill);
	printf("end of Kill list.\n");
      }
      head = NextNode(head);
    }
  }
}

/*****************************************************************************
 * Length(head) - length of the list
 ****************************************************************************/
Length(head)
     MoveTree *head;
{
  int i;
  i=0;
  if(!IsEmpty(head)) {
    head = NextNode(head);
    while(!IsEmpty(head)) {
      i++;
      head = NextNode(head);
    }
  }
  return(i);
}

/*****************************************************************************
 * UndoLastMove(board)
 ****************************************************************************/

int UndoLastMove(board)
     Board *board;
{
  int kills;
  MoveTree *head;

  head = board->movetree->next;
  kills = 0;

  if(!IsEmpty(head)) {
    if(head->color != PASS && head->color != KILL)
      AddStone(board, head->x, head->y, EMPTY);
    head = head->kill;
    if(!IsEmpty(head)) 
      head = head->next;
      while(head != NULL) {
	AddStone(board, head->x, head->y, head->color);
	head = head->next;
	kills++;
      }
  }
  DeleteNode(board->movetree);
  return(kills);
}

/*****************************************************************************
 * LoadMoves(board)
 ****************************************************************************/

MoveTree *LoadMoves(board, filename)
     Board *board;
{
  FILE *fp;
  char s[81];
  int x,y,color;
  MoveTree *lastmove;
  lastmove = NULL;
  
  board->movetree = InitMoveTree();
  if((fp = fopen(filename, "r"))!=NULL) {
    fscanf(fp, "%s %d %d", s, &(board->sizex), &(board->sizey));
    fscanf(fp, "%s %d", s, &(board->handicap));
    InitPlayBoard(board);
    while(!feof(fp)) {
      fscanf(fp, "%d %d %d", &x, &y, &color);
      if(color != PASS) {
	AddStone(board, x, y, color);
	lastmove = AddNewNode(board->movetree, x, y, color);
	PlaceStoneOnBoard(board, x, y, color);
      } else
	AddNewNode(board->movetree, x, y, color);
    }
    fclose(fp);
  }
  return(lastmove);
}

/*****************************************************************************
 * SaveMoves(board)
 ****************************************************************************/

SaveMoves(board, filename)
     Board *board;
{
  FILE *fp;
  MoveTree *node;
  node = board->movetree;
  if(!IsEmpty(node)) {
    node = EndOfList(node);
    if((fp = fopen(filename, "w"))!=0L) {
      fprintf(fp, "size: %d %d\n",board->sizex, board->sizey);
      fprintf(fp, "handicap: %d\n",board->handicap);
      while(!IsEmpty(node) && board->movetree != node) {
	fprintf(fp,"%d %d %d\n",node->x, node->y, node->color);
	node = PrevNode(board->movetree, node);
      }
      fclose(fp);
    }
  }
}

/*****************************************************************************
 * PrintMoves(board)
 ****************************************************************************/

PrintMoves(board, filename)
     Board *board;
{
  FILE *fp;
  MoveTree *node;
  int i,j;

  if((fp = fopen(filename, "w")) != 0L) {
    fprintf(fp, "%d %d BoardSize \n", board->sizex, board->sizey);
    for(i=0; i<board->sizex; i++) {
      for(j=0; j<board->sizey; j++) {
	if(board->grid[i][j]==BLACK)
	  fprintf(fp, "%d %d () Black \n",i,j);
	else if(board->grid[i][j]==WHITE)
	  fprintf(fp, "%d %d () White \n",i,j);
      }
    }
    fclose(fp);
  }
}

/*****************************************************************************
 * NeighborColor(board,x,y)
 ****************************************************************************/
/* The followings are calulation */
NeighborColor(board,x,y)
     Board *board;
     int x,y;
{
  int color = EMPTY;
  int i,j;
  for(i=x+1; i < board->sizex; i++)
    if(board->grid[i][y] != EMPTY) {
      color = board->grid[i][y];
      break;
    }
  for(i=x-1;i>=0;i--) {
    if(board->grid[i][y] != EMPTY) {
      if(color != EMPTY && board->grid[i][y]!=color) 
	return EMPTY;
      else {
	color = board->grid[i][y];
	break;
      }
    }
  }

  for(j=y+1;j<board->sizey;j++) {
    if(board->grid[x][j] != EMPTY) {
      if(color != EMPTY && board->grid[x][j]!=color) return EMPTY;
      else {
	color = board->grid[x][j];
	break;
      }
    }
  }
  for(j=y-1; j>=0; j--) {
    if(board->grid[x][j] != EMPTY) {
      if(color != EMPTY && board->grid[x][j]!=color) 
	return EMPTY;
      else {
	color = board->grid[x][j];
	break;
      }
    }
  }
  return color;
}

CalcuBoard(board, white, black, undet)
     Board *board;
     int *white, *black, *undet;
{
  int i,j,k;
  *white=0;
  *black=0;
  *undet=0;
  for(i=0; i<board->sizex; i++)
    for(j=0; j<board->sizey; j++)
      if (board->grid[i][j] == EMPTY) {
	if(NeighborColor(board,i,j)==BLACK) 
	  (*black)++;
	else if(NeighborColor(board,i,j)==WHITE) 
	  (*white)++;
	else 
	  (*undet)++;
      }
}
