/*LINTLIBRARY*/

/*  @(#)boardstuff.c 1.4 90/04/05
 *
 *  Various board routines used by the othello game.
 *
 *  Original SunView version by Ed Falk - Sun Microsystems Inc.
 *
 *  Rewritten for independent use by
 *          Rich Burridge, Sun Microsystems, Australia
 *
 *  Copyright (c) Ed Falk & Rich Burridge - Sun Microsystems.
 *                                          All rights reserved.
 *
 *  Permission is given to distribute these sources, as long as the
 *  introductory messages are not removed, and no monies are exchanged.
 *
 *  No responsibility is taken for any errors on inaccuracies inherent
 *  either to the comments or the code of this program, but if reported
 *  to me, then an attempt will be made to fix them.
 */

#include "color.h"
#include "othello.h"
#include "extern.h"


animate_move(move)
int move ;
{
  int x0, y0, x1, y1, x, y, dx, dy, ctr ;

  lock_screen(IS_ON) ;
  get_xy(move, &x1, &y1) ;
  dx = x1 ;
  dy = y1 ;
  if (x1 > y1)
    {
      ctr = dx / 2 ;
      x = BBORDER ;
      y = BBORDER ;
      draw_piece(WHITE, x, CY+y, RINV) ;
      while (x < x1)
        {
          set_timer() ;
          x0 = x ;
          y0 = y ;
          x += move_delta ;
          if ((ctr -= dy) < 0)
            {
              ctr += dx ;
              y += move_delta ;
            }
          draw_piece(WHITE, x, CY+y, RINV) ;
          draw_piece(WHITE, x0, CY+y0, RINV) ;
          nap_upto(1) ;
        }
      draw_piece(WHITE, x, CY+y, RINV) ;
    }
  else
    { 
      ctr = dy / 2 ;
      x = BBORDER ;
      y = BBORDER ;
      draw_piece(WHITE, x, CY+y, RINV) ;
      while (y < y1)
        {
          set_timer() ;
          x0 = x ;
          y0 = y ;
          y += move_delta ;
          if ((ctr -= dx) < 0)
            {
              ctr += dy ;
              x += move_delta ;
            }
          draw_piece(WHITE, x, CY+y, RINV) ;
          draw_piece(WHITE, x0, CY+y0, RINV) ;
          nap_upto(1) ;
        }
      draw_piece(WHITE, x, CY+y, RINV) ;
    }
  lock_screen(IS_OFF) ;
}


/*  This routine checks to see if a move can be made for this player.
 *  If not, various checks are made to see if the game is finished.
 *  Return value indicates if a move can be made.
 */

check(player, who, be)
int player ;
char *who[], *be[] ;
{
  if ((!count(&board, BLACK)) || (!count(&board, WHITE)) ||
      ((count(&board, BLACK) + count(&board, WHITE)) == 64))
    {
      who_wins() ;
      cmode = GAME_OVER ;
      message(PANEL_MES, "Game over") ;
      return(FALSE) ;
    }
  if ((move = makemove(&board, player, 0)) == NOMOVE)
    {
      SPRINTF(line, "%s %s forced to pass",
                     who[(int) play_mode], be[(int) play_mode]) ;
      message(PANEL_MES, line) ;
      if ((move = makemove(&board, OPPONENT(player), 0)) == NOMOVE)
        {
          who_wins() ;
          cmode = GAME_OVER ;
          message(PANEL_MES, "Game over") ;
        }
      return(FALSE) ;
    }
  return(TRUE) ;
}


count(board, player)    /* Count the number of player pieces on the board. */
int player ;
BOARD *board ;
{
  int i, n ;

  n = 0 ;
  FOR_BOARD(i)
    if (board->square[i] == player) n++ ;
  return(n) ;
}


do_move(player, who)
int player ;
char *who[] ;
{
  int taken ;                /* Number of pieces flipped this go. */

  taken = formfliplist(move, player) ;
  update_board_image() ;
  if (taken == 1)
    {
      SPRINTF(line, "%s took 1 piece", who[(int) play_mode]) ;
      message(PANEL_MES, line) ;
    }
  else
    {
      SPRINTF(line, "%s took %d pieces", who[(int) play_mode],taken) ;
      message(PANEL_MES, line) ;
    }
}


formfliplist(move, player)
int move, player ;
{        
  int cnt, i, old_cnt ;

  old_cnt = count(&board, player) ;
  FOR_BOARD(i) old_board.square[i] = board.square[i] ;
  old_board.moves_left = board.moves_left ;
  domove(&old_board, move, &board, player) ;
  FOR_BOARD(i) moves[63 - board.moves_left].square[i] = board.square[i] ;
  moves[63 - board.moves_left].moves_left = board.moves_left ;
  cnt = count(&board, player) ;
  return(cnt - old_cnt - 1) ;
}


initboard()    /* Initialise the othello board. */
{
  static int ivals[4] = { 27, 28, 35, 36 } ;
  static int icolors[4] = { WHITE, BLACK, BLACK, WHITE } ;
  int i, j , n ;

  for (n = 0; n < 4; n++)
    {
      FOR_BOARD(i) moves[n].square[i] = FREE ;
      for (j = 0; j <= n; j++) moves[n].square[ivals[j]] = icolors[j] ;
      moves[n].moves_left = 63 - n ;
    }

  FOR_BOARD(i) old_board.square[i] = board.square[i] = FREE ;
  board.square[27] = WHITE ;
  board.square[28] = BLACK ;
  board.square[35] = BLACK ;
  board.square[36] = WHITE ;
  board.moves_left = 60 ;
}


move_and_check(player, who, be, opp_who, opp_be)
int player ;
char *who[], *be[], *opp_who[], *opp_be[] ;
{
  do_move(player, who) ;
  for (;;)
    if (check(OPPONENT(player), opp_who, opp_be) == TRUE)
      {
        think(OPPONENT(player), opp_who) ;
        if (check(player, who,be) == TRUE) break ;
        if (cmode == GAME_OVER) break ;
      }
    else
      {
        if (cmode != GAME_OVER) cmode = (enum cantype) ((int) cmode - 1) ;
        return ;
      }
}


nap_upto(n)          /* Sleep upto n microseconds from start of timer. */
int n ;
{
  struct timeval ctp ;
  struct timezone ctzp ;
  long elapsed ;     /* Number of microseconds since timer started. */

  GETTIMEOFDAY(&ctp, &ctzp) ;
  elapsed = ((ctp.tv_usec - tp.tv_usec) +
            (ctp.tv_sec - tp.tv_sec) * 1000000L) / 1000 ;
  if (elapsed > n) return ;
  usleep((unsigned) (n - elapsed)) ;
}


set_timer()
{
  struct timezone tzp ;

  GETTIMEOFDAY(&tp, &tzp) ;
}


show_suggestion()
{
  enum optype rop ;

  if (suggestion != -1)
    {
      rop = RCLR ;
      color = (iscolor) ? C_LBROWN : C_WHITE ;
      if (iscolor) rop = RSRC ;
      draw_line(suggest_x-5, CY+suggest_y-5,
                suggest_x+5, CY+suggest_y+5, rop, color) ;
      draw_line(suggest_x-5, CY+suggest_y+5,
                suggest_x+5, CY+suggest_y-5, rop, color) ;
      suggestion = -1 ;
    }
}


update_board_image()
{
  int flips, i, piece, x, y ;
 
  show_suggestion() ;
  for (flips = 0; flips < 4; flips++)
    {
      batch(IS_ON) ;
      FOR_BOARD(i)
        {
          if (board.square[i] != old_board.square[i])
            {
              get_xy(i, &x, &y) ;
              if (i == move) piece = board.square[i] ;
              else
                piece = (flips % 2) ? board.square[i] : board.square[i] * -1 ;
              draw_piece(piece, x, CY+y, RSRC) ;
            } 
        }
      batch(IS_OFF) ;
      PAUSE ;
    }
  SPRINTF(line, "White: %2d, Black: %2d",
                 count(&board, WHITE), count(&board, BLACK)) ;
  message(SCORE_MES, line) ;
}


who_wins()
{
  int cs, ps ;

  ps = count(&board, WHITE) ;
  cs = count(&board, BLACK) ;
  if (ps > cs)
    {
      SPRINTF(line, "White wins %d-%d", ps, cs) ;
      message(SCORE_MES, line) ;
    }
  else if (ps == cs)
    {
      SPRINTF(line,"A tie %d-%d", ps, cs) ;
      message(SCORE_MES, line) ;
    }
  else
    {
      SPRINTF(line, "Black wins %d-%d", cs, ps) ;
      message(SCORE_MES, line) ;
    }
  if (cretin_flag) message(REMARK_MES, "*** CRETIN! ***") ;
  else message(REMARK_MES, "") ;
}
