
/*  @(#)othello.c 1.7 90/04/10
 *
 *  Main routine for the othello program.
 *
 *  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 "patchlevel.h"

/* Constants, typedefs, externals, globals, statics, macros. */

char *w_who[] = { "You", "Computer", "White" } ;
char *w_be[]  = { "are", "is", "is" } ;
char *b_who[] = { "Computer", "You", "Black" } ;
char *b_be[]  = { "is", "are", "is" } ;

/* Text values for the cyclic buttons. */
char *asp_values[]  = { "1", "2", "3", "4", "5", "6", NULL } ;
char *diff_values[] = { "1", "2", "3", "4", NULL } ;
char *comp_values[] = { "black", "white", NULL } ;
char *rem_values[]  = { "no", "yes", NULL } ;

enum cantype cmode ;
enum playtype play_mode ;
enum set_type direction ;   /* Incremental direction for cycle item. */

int piece_x ;           /* Current X position of moving piece. */
int piece_y ;           /* Current Y position of moving piece */

int aspire ;            /* Computers level of aspiration. */
int bfont_height ;      /* Height in pixels for bold font. */
int but_inverted ;      /* Value of panel item inverted. */
int color ;             /* Current color value. */
int cretin_flag ;
int cur_ch ;            /* Current character pressed. */
int curx ;              /* Current mouse X position. */
int cury ;              /* Current mouse Y position. */
int depth ;             /* Depth of search for computers move. */
int down ;              /* Indicates is a mouse button is down. */
int expected ;
int iconic ;            /* Set if window is currently iconic. */
int inv_video = 0 ;     /* Set if displaying in inverse video. */
int iscolor ;           /* Set if this is a color screen. */
int itemno ;            /* Current panel item being processed. */
int item_value ;        /* Value for current panel item. */
int ix ;                /* Initial X position of the icon. */
int iy ;                /* Initial Y position of the icon. */
int last_move ;         /* Last valid computer move. */
int last_remark ;
int margin ;
int max_bad = 0 ;
int max_good = 0 ;
int move ;              /* Current move being evaluated. */
int move_delta ;        /* Delta for piece animation. */
int nextc ;             /* Current event identifier. */
int next_player ;       /* Next player (BLACK or WHITE) to move. */
int nfont_height ;      /* Height in pixels for normal font. */
int posspec ;           /* Set if -Wp or -g option is present (for X11) */
int remark ;            /* Indicates if remarks are to be displayed. */
int suggestion = -1 ;   /* Positive if a suggested move. */
int suggest_x ;         /* X position of suggested move. */
int suggest_y ;         /* Y position of suggested move. */
int validkey ;          /* Set if half way though a valid multiple key. */
int wx ;                /* Initial X position of the window. */
int wy ;                /* Initial Y position of the window. */

/*  Globals for passing arguments to "sandwich";
 *  this is to save time putting arguments on and off the
 *  stack in a very heavily used piece of code
 */

int s_flip ;
int s_move ;
int s_opponent ;
int s_player ;
int s_row ;
int s_col ;

long ab_count ;
long bad_remarks[MAX_REMARKS] ;
long good_remarks[MAX_REMARKS] ;
long se_count ;

FILE *rem_fp ;

struct timeval tp ;        /* Used by the nap_upto routine. */

BOARD old_board ;          /* The previous othello board. */
BOARD board ;              /* The othello board. */
BOARD *s_pos ;

BOARD moves[64] ;          /* Complete array of board moves. */

int done(), last(), new_game(), quit(), suggest(), undo() ;
int computer_plays(), do_remark(), do_aspiration(), difficulty() ;

struct iteminfo items[MAXITEMS] = {                     /* Panel items. */

/*   type   x   y   width   height   text   value   function */

{ P_BUTTON,  BBORDER + (0*(BWIDTH+BGAP)), BBORDER + (0*(BHEIGHT+BGAP)),
             BWIDTH, BHEIGHT, "done",     0, done },

{ P_BUTTON,  BBORDER + (1*(BWIDTH+BGAP)), BBORDER + (0*(BHEIGHT+BGAP)),
             BWIDTH, BHEIGHT, "last",     0, last },

{ P_BUTTON,  BBORDER + (2*(BWIDTH+BGAP)), BBORDER + (0*(BHEIGHT+BGAP)),
             BWIDTH, BHEIGHT, "new game", 0, new_game },

{ P_BUTTON,  BBORDER + (3*(BWIDTH+BGAP)), BBORDER + (0*(BHEIGHT+BGAP)),
             BWIDTH, BHEIGHT, "suggest",  0, suggest },

{ P_BUTTON,  BBORDER + (4*(BWIDTH+BGAP)), BBORDER + (0*(BHEIGHT+BGAP)),
             BWIDTH, BHEIGHT, "undo",     0, undo },

{ P_BUTTON,  BBORDER + (5*(BWIDTH+BGAP)), BBORDER + (0*(BHEIGHT+BGAP)),
             BWIDTH, BHEIGHT, "quit",     0, quit },

{ P_CYCLE,   BBORDER + (0*(BWIDTH+BGAP)), BBORDER + (2*(BHEIGHT+BGAP)),
             CWIDTH, CHEIGHT, "Computer plays:", 1, computer_plays },

{ P_CYCLE,   BBORDER + (3*(BWIDTH+BGAP)), BBORDER + (2*(BHEIGHT+BGAP)),
             CWIDTH, CHEIGHT, "Remarks:", 0, do_remark },

{ P_CYCLE,   BBORDER + (0*(BWIDTH+BGAP)), BBORDER + (1*(BHEIGHT+BGAP)),
             CWIDTH, CHEIGHT, "Aspiration:", 2, do_aspiration },

{ P_CYCLE,   BBORDER + (3*(BWIDTH+BGAP)), BBORDER + (1*(BHEIGHT+BGAP)),
             CWIDTH, CHEIGHT, "Difficulty:", 0, difficulty },

{ P_MESSAGE, BBORDER + (0*(BWIDTH+BGAP)), BBORDER + (3*(BHEIGHT+BGAP)),
             0, 0, "To move, use the left mouse button", 0, NULL },

{ P_MESSAGE, BBORDER + (0*(BWIDTH+BGAP)), BBORDER + (4*(BHEIGHT+BGAP)),
             0, 0, "",                                   0, NULL },

{ P_MESSAGE, BBORDER + (0*(BWIDTH+BGAP)), BBORDER + (5*(BHEIGHT+BGAP)),
             0, 0, "White:  2, Black:  2",               0, NULL },
} ;

char line[40] ;
char geometry[MAXLINE] ;       /* X11 geometry information. */
char progname[MAXLINE] ;       /* The name of this program. */
char rem_file[MAXLINE] ;       /* Location of remarks file. */
char x11_display[MAXLINE] ;    /* X11 display information. */


main(argc,argv)
int argc ;
char *argv[] ;

{
  STRCPY(progname, argv[0]) ;   /* Save program name for later use. */
  SPRINTF(line, " Othello.  V1.3.%1d", PATCHLEVEL) ;
  initialise() ;                /* Initialise variables used by Othello. */
  get_options(argc, argv) ;     /* Read command line options. */
  if (init_ws_type())           /* Determine window system type. */
    {
      FPRINTF(stderr,"Error initialising window system.\n") ;
      exit(1) ;
    }
  make_icon() ;
  make_frame(argc, argv) ;      /* Create othello window/icon. */
  make_canvas() ;               /* Create drawing canvas. */
  init_fonts() ;                /* Load normal and bold fonts. */
  create_menu(ASPIRATION, asp_values) ;     /* Create cycle popup menus. */
  create_menu(DIFFICULTY, diff_values) ;
  create_menu(COMPUTER_PLAYS, comp_values) ;
  create_menu(REMARK, rem_values) ;
  initboard() ;
  get_remarks() ;
  set_cursor(CANVASCUR) ;
  start_tool() ;                /* Event handling loop. */
  exit(0) ;
}


get_options(argc, argv)   /* Read and process command line options. */
int argc ;
char *argv[] ;
{
  char next[MAXLINE] ;    /* The next command line parameter. */

  INC ;
  while (argc > 0)
    {
      if (argv[0][0] == '-')
        switch (argv[0][1])
          {
            case 'd' : INC ;                 /* X11 display information. */
                       getparam(x11_display, argv, "-d needs display information") ;                       break ;
            case 'g' : INC ;                 /* X11 geometry information. */
                       getparam(geometry, argv, "-g needs geometry information") ;
                       break ;
            case 'i' : inv_video = 1 ;       /* Display in inverse video. */
                       break ;
            case 'r' : INC ;
                       getparam(rem_file, argv, "-r needs a remarks file") ;
                       break ;
            case 'v' : FPRINTF(stderr, "%s version 1.3.%1d\n", progname, PATCHLEVEL) ;
                       exit(1) ;

/*  SunView windowing arguments. -Wp, -WP and -Wi are used in the X11
 *  implementation to initially position the window and icon.
 */          
             
            case 'W' : switch (argv[0][2])
                         {
                           case 'H' : break ;   /* -WH, no sub-args follow */
                           case 'i' : iconic = 1 ;
                                      break ;   /* -Wi, start as an icon. */
                           case 'g' :           /* -Wg, set default color. */
                           case 'n' : break ;   /* -Wn, no label at all */
                           case 'h' :           /* -Wh, height */
                           case 'I' :           /* -WI "icon filename" */
                           case 'l' :           /* -Wl "some window label" */
                           case 'L' :           /* -Wl "some icon label" */
                           case 't' :           /* Font filename */
                           case 'T' :           /* Icon font filename */
                           case 'w' : INC ;     /* Width, in columns. */
                                      break ;
                           case 'p' : INC ;     /* -Wp xnum ynum */
                                      getparam(next, argv,
                                               "-Wp needs x coordinate") ;
                                      wx = atoi(next) ;
                                      INC ;
                                      getparam(next, argv,
                                               "-Wp needs y coordinate") ;
                                      wy = atoi(next) ;
                                      posspec = 1 ;
                                      break ;
                           case 'P' : INC ;      /* -WP xnum ynum */
                                      getparam(next, argv,
                                               "-WP needs x coordinate") ;
                                      ix = atoi(next) ;
                                      INC ;
                                      getparam(next, argv,
                                               "-WP needs y coordinate") ;
                                      iy = atoi(next) ;
                                      break ;
                           case 's' : INC ; INC ;  /* -Ws xnum ynum */
                                      break ;
                           case 'b' :              /* -Wb r g b (bg color spec)
*/
                           case 'f' : INC ; INC ; INC ;  /* Same, fg color */
                                      break ;
                           default :  FPRINTF(stderr,"%s: -W%c unknown argument\n",
                                                      progname, argv[0][2]) ;
                                      break ;
                         }
                       break ;
            default  : usage() ;
          }
      INC ;
    }
}


getparam(s, argv, errmes)
char *s, *argv[], *errmes ;
{
  if (*argv != NULL && argv[0][0] != '-') STRCPY(s, *argv) ;
  else
    { 
      FPRINTF(stderr,"%s: %s as next argument.\n", progname, errmes) ;
      exit(1) ;
    }
}


init_canvas()
{
  char s1[2], sa[2] ;
  int adjust, i, n, x, y ;
 
  color_area(0, 0, TOTAL_WIDTH, TOTAL_HEIGHT, C_WHITE) ;
  if (iscolor)
    {
      color_area(0, 0, TOTAL_WIDTH, CY, C_BEIGE) ;
      color_area(0, CY, TOTAL_WIDTH, TOTAL_WIDTH, C_DBROWN) ;
      color_area(0 + BBORDER, CY + BBORDER, BOARD_SIZE * CELL_SIZE,
                 BOARD_SIZE * CELL_SIZE, C_LBROWN) ;
    }
  make_panel() ;                /* Create panel and panel items. */
  batch(IS_ON) ;
  for (n = 0; n <= BOARD_SIZE; n++)
    {
      color = (iscolor) ? C_LGREY : C_BLACK ;
      if (n == 0 || n == BOARD_SIZE) color = C_BLACK ;
      if (color == C_LGREY) adjust = 1 ;
      else adjust = 0 ;
      draw_line((n*CELL_SIZE)+BBORDER, CY+BBORDER+adjust,   /* Vertical. */
                (n*CELL_SIZE)+BBORDER,
                 CY+BBORDER+(BOARD_SIZE*CELL_SIZE)-adjust, RSRC, color) ;
      draw_line(BBORDER+adjust, CY+BBORDER+(n*CELL_SIZE),   /* Horizontal. */
                BBORDER+(BOARD_SIZE*CELL_SIZE)-adjust,
                CY+BBORDER+(n*CELL_SIZE), RSRC, color) ;
    }
  batch(IS_OFF) ;
  batch (IS_ON) ;
  for (i = 0; i < BOARD_SIZE; i++)
    {
      color = (iscolor) ? C_WHITE : C_BLACK ;
      SPRINTF(s1, "%c", 'a' + i) ;
      draw_text(BBORDER + (i * CELL_SIZE) + (CELL_SIZE / 2) - 2,
                CY + BBORDER - 4, BFONT, color, s1) ;
      draw_text(BBORDER + (i * CELL_SIZE) + (CELL_SIZE / 2) - 2,
                CY + (2 * BBORDER) + (BOARD_SIZE * CELL_SIZE) - 4,
                BFONT, color, s1) ;
      SPRINTF(sa, "%c", '1' + i) ;
      draw_text(BBORDER / 2 - 2, CY + BBORDER + (i * CELL_SIZE) +
                (CELL_SIZE / 2) + 2, BFONT, color, sa) ;
      draw_text(BBORDER + (BOARD_SIZE * CELL_SIZE) + (BBORDER / 2) - 2,
                CY + BBORDER + (i * CELL_SIZE) + (CELL_SIZE / 2) + 2,
                BFONT, color, sa) ;
    }
  batch(IS_OFF) ;
  show_suggestion() ;
  batch(IS_ON) ;
  FOR_BOARD(i)
    if (board.square[i])
      {
        get_xy(i, &x, &y) ;
        draw_piece(board.square[i], x, CY+y, RSRC) ;
      }
  batch(IS_OFF) ;
  message(SCORE_MES, sprintf(line, "White: %2d, Black: %2d",
                          count(&board, WHITE), count(&board, BLACK))) ;
}


initialise()        /* Initialise various variable used by Othello. */
{
  remark = items[(int) REMARK].value ;
  iconic = 0 ;                        /* Initially an open window. */
  last_move = -1 ;
  but_inverted = -1 ;
  cury = wx = wy = ix = iy = 0 ;
  play_mode = PLAY_WHITE ;
  validkey = 0 ;
  cmode = BLACK_START ;
  aspire = INIT_ASPIRE ;
  depth = INIT_DEPTH ;
  cretin_flag = FALSE ;
  last_remark = FIRST_REMARK ;        /* Force a remark the first time. */
  STRCPY(x11_display, "") ;           /* X11 display type. */
  STRCPY(geometry, "") ;              /* X11 geometry information. */
  STRCPY(rem_file, REMNAME) ;
}


think(player, who)
int player ;
char *who[] ;
{
  set_cursor(HOURGLASS) ;
  move = makemove(&board, player, depth) ;
  set_cursor(CANVASCUR) ;
  animate_move(move) ;
  do_move(player, who) ;
  last_move = move ;
  cmode = (enum cantype) (OPPONENT(player) + 1) ;
}


usage()
{
  FPRINTF(stderr, "Usage: %s: [-d display] [-g geometry] ", progname) ;
  FPRINTF(stderr, "[-r remarkfile] [-v] [-Wi] [-Wp x y] [-WP x y]\n") ;
}


#ifdef NO_USLEEP
usleep(x)        /* Suspend execution for interval in microseconds. */
unsigned x ;
{
  struct timeval st_delay ;

  st_delay.tv_usec = x ;
  st_delay.tv_sec = 0 ;
  (void) select(32, NULL, NULL, NULL, &st_delay) ;
}	
#endif /*NO_USLEEP*/
