/* jester.c */
/* an X enhanced game similar to the boardgame "Othello"(tm)
   Copyright (C) 1998 Matthew Grossman <mattg@oz.net> */
/*
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include"jester.h"

static int exit_flag = 0;
static int current_player = WHITE; /* white moves first */
static char *exit_text = "EXIT";
static int game_over = 0;
char *program_name = "jester";



int
main(int argc, char *argv[])
{
  char *display_name = NULL;
  int rv = 0;
  char *s = NULL;

  display = NULL;
  gc = NULL;
  jester_font = 0;
  legal_cursor = 0;

  /* random numbers for the ai */
  srand(time(NULL));

  if(get_bool_option(argc, argv, "-help") ||
     get_bool_option(argc, argv, "-h") ||
     get_bool_option(argc, argv, "--help")) {
    print_usage();
    goto DONE;
  }
  
  if(!(display_name = get_display_name(argc, argv))) {
    fprintf(stderr, "Cannot get display name\n");
    goto ERROR;
  }
  screen_number = get_screen_number(display_name);
  if(!(display = XOpenDisplay(display_name))) {
    fprintf(stderr, "cannot open display.\n");
    goto ERROR;
  }

  if(get_bool_option(argc, argv, "-2players"))
    ai = 0;
  else if((s = get_text_option(argc, argv, "-ai"))) {
    if(strcmp(s, "white") == 0)
      ai = WHITE;
    else if(strcmp(s, "black") == 0)
      ai = BLACK;
    else {
      fprintf(stderr, "No such color %s\n", s);
      goto ERROR;
    }
  }
  else
    ai = BLACK;
  
  if(set_up_stuff(argc, argv))
    goto ERROR;
  if(ai == WHITE)
    make_ai_move();
  if(handle_events())
    goto ERROR;


 DONE:
  if(display_name)
    free(display_name);
  clean_up_stuff();
  exit(rv);
 ERROR:
  rv = 1;
  goto DONE;
}

int
handle_events()
     /* the event loop */
{
  XEvent event;
  int rv = 0;
  struct jester_data *jd = NULL;
  int (*func)();

  while(!exit_flag) {
    XNextEvent(display, &event);
    if(!XFindContext(display, event.xany.window, jester_context,
		     (XPointer *)&jd)) {
      func = (int (*)())proper_function(jd, &event);
      if(!func)
	continue;
      (*func)(&event);
    }
  }

  return(rv);
}

caddr_t
proper_function(struct jester_data *jd, XEvent *event)
     /* return the proper function for the proper event type */
{
  caddr_t rv = 0;
  
  switch(event->type) {
  case Expose:
    if(event->xexpose.count == 0)
      rv = (caddr_t)jd->expose_function;
    break;
  case ButtonPress:
    rv = (caddr_t)jd->buttonpress_function;
    break;
  default:
  }

  return(rv);
}

int
set_up_stuff(int argc, char *argv[])
     /* create and display the windows, initialize gc, etc. */
{
  int rv = 0;

  set_up_colors();
  set_up_pieces();
  set_up_fonts();
  set_up_cursors();
  jester_context = XUniqueContext();
  
  create_board_window(argc, argv);

  gc = XCreateGC(display, board_window, 0, NULL);

  create_exit_button();
  create_score_window();
  create_player_id_window();
  create_square_windows();
  mark_legal_squares(current_player);

  return(rv);
}

void
set_up_fonts()
{
  jester_font = XLoadFont(display, "fixed");
}
  
void
set_up_colors()
     /* set up the colors */
{
  XColor exact_color, real_color;
  Colormap colormap;

  colormap = DefaultColormap(display, screen_number);
  XAllocNamedColor(display, colormap, "green", &exact_color, &real_color);
  green = real_color.pixel;
  XAllocNamedColor(display, colormap, "black", &exact_color, &real_color);
  black = real_color.pixel;
  XAllocNamedColor(display, colormap, "white", &exact_color, &real_color);
  white = real_color.pixel;

  XAllocNamedColor(display, colormap, "Gray35", &exact_color, &real_color);
  black_piece = real_color.pixel;
  XAllocNamedColor(display, colormap, "Gray50", &exact_color, &real_color);
  black_highlight = real_color.pixel;
  XAllocNamedColor(display, colormap, "Gray20", &exact_color, &real_color);
  black_shadow = real_color.pixel;


  XAllocNamedColor(display, colormap, "ivory2", &exact_color, &real_color);
  white_piece = real_color.pixel;
  XAllocNamedColor(display, colormap, "ivory", &exact_color, &real_color);
  white_highlight = real_color.pixel;
  XAllocNamedColor(display, colormap, "ivory3", &exact_color, &real_color);
  white_shadow = real_color.pixel;
  
  return;
}

void
set_up_pieces()
     /* set up the board */
{
  int i = 0, j = 0;

  for(i = 0; i < 8; i++)
    for(j = 0; j < 8; j++)
      pieces[i][j] = EMPTY;

  /* place the initial four pieces */
  
  pieces[3][3] = WHITE;
  pieces[4][4] = WHITE;
  pieces[3][4] = BLACK;
  pieces[4][3] = BLACK;

  return;
}


char *
get_display_name(int argc, char *argv[])
     /* return a pointer to the display name, or the value of $DISPLAY,
	or NULL */
{
  char *s = NULL;
  char *rv = NULL;

  /* the "display" flags are "-d", "-display", "--display" */

  if((s = get_text_option(argc, argv, "-d")) ||
     (s = get_text_option(argc, argv, "-display")) ||
     (s = get_text_option(argc, argv, "--display")) ||
     (s = getenv("DISPLAY"))) {
    rv = (char *)malloc(strlen(s) + 1);
    strcpy(rv, s);
    goto DONE;
  }
  else {
    fprintf(stderr, "get_display_name: cannot get display name.\n");
    goto ERROR;
  }

 DONE:
#ifdef DEBUG
  if(rv)
    fprintf(stderr, "get_display_name: display = %s\n", rv);
#endif
  return(rv);
 ERROR:
  rv = NULL;
  goto DONE;
}


int
get_screen_number(char *display_name)
     /* get the number of the screen from the display name */
{
  char *s = NULL;
  int rv = 0;

  if((s = strrchr(display_name, '.')) == NULL)
    goto DONE;
  s += 1;
  rv = atoi(s);

 DONE:
#ifdef DEBUG
  fprintf(stderr, "get_screen_number: screen number is %d\n", rv);
#endif
  return(rv);
}

int
square_expose(XEvent *event)
     /* handle an expose event in a square */
{
  int rv = 0;
  struct jester_data *jd = NULL;

  XFindContext(display, event->xexpose.window, jester_context,
	       (XPointer *)&jd);

  if(pieces[jd->coords.x][jd->coords.y] == EMPTY)
    goto DONE;

  draw_piece(pieces[jd->coords.x][jd->coords.y], event->xexpose.window);
  
 DONE:
  return(rv);
}

void
draw_piece(int player, Window window)
     /* draw a color piece in window */
{
  unsigned long color = 0, shadow = 0, highlight = 0;
  XPoint points[3];
  int i = 0;

  if(player == WHITE) {
    color = white_piece;
    shadow = white_shadow;
    highlight = white_highlight;
  }
  else {
    color = black_piece;
    shadow = black_shadow;
    highlight = black_highlight;
  }

  XSetForeground(display, gc, color);
  XFillRectangle(display, window, gc, 0, 0, SQUARE_WIDTH,
		 SQUARE_WIDTH);
    
  XSetForeground(display, gc, highlight);
  for(i = 0; i < 8; i++) {
    points[0].x = i;
    points[0].y = SQUARE_WIDTH - (i + 1);
    points[1].x = i;
    points[1].y = i;
    points[2].x = SQUARE_WIDTH - (i + 1);
    points[2].y = i;
    XDrawLines(display, window, gc, points, 3, CoordModeOrigin);
  }

  
  XSetForeground(display, gc, shadow);
  for(i = 0; i < 8; i++) {
    points[0].x = SQUARE_WIDTH - (i + 1);
    points[0].y = i;
    points[1].x = SQUARE_WIDTH - (i + 1);
    points[1].y = SQUARE_WIDTH - (i + 1);
    points[2].x = i;
    points[2].y = SQUARE_WIDTH - (i + 1);
    XDrawLines(display, window, gc, points, 3, CoordModeOrigin);
  }

    
  XSetForeground(display, gc, highlight);
  for(i = 8; i < 12; i++) {
    points[0].x = SQUARE_WIDTH - (i + 1);
    points[0].y = i;
    points[1].x = SQUARE_WIDTH - (i + 1);
    points[1].y = SQUARE_WIDTH - (i + 1);
    points[2].x = i;
    points[2].y = SQUARE_WIDTH - (i + 1);
    XDrawLines(display, window, gc, points, 3, CoordModeOrigin);
  }

      
  XSetForeground(display, gc, shadow);
  for(i = 8; i < 12; i++) {
    points[0].x = i;
    points[0].y = SQUARE_WIDTH - (i + 1);
    points[1].x = i;
    points[1].y = i;
    points[2].x = SQUARE_WIDTH - (i + 1);
    points[2].y = i;
    XDrawLines(display, window, gc, points, 3, CoordModeOrigin);
  }


/*   XSetForeground(display, gc, color); */
/*   XFillArc(display, window, gc, 0, 0, SQUARE_WIDTH, SQUARE_WIDTH, 0, */
/* 	   360 * 64); */
  return;
}

unsigned long
color_from_player(int player)
{
  unsigned long rv = 0;
  
  if(player == WHITE)
    rv = white_piece;
  else if(player == BLACK)
    rv = black_piece;
  else
    fprintf(stderr, "unknown player %d\n", player);
    
  return(rv);
}
  
int
legal_move(int x, int y, int player)
     /* is current_player playing on x, y a legal move? and how many pieces
	will be flipped? */
{
  int rv = 0;
  
  if(pieces[x][y] == EMPTY) {
    if(y > 1)
      if((rv = flip_line(x, y, up, player, 1))) {
	goto SUCCESS;
      }
    if(x < 6 && y > 1)
      if((rv = flip_line(x, y, up_right, player, 1))) {
	goto SUCCESS;
      }
    if(x > 1 && y > 1)
      if((rv = flip_line(x, y, up_left, player, 1))) {
	goto SUCCESS;
      }
    if(x < 6)
      if((rv = flip_line(x, y, right, player, 1))) {
	goto SUCCESS;
      }
    if(x > 1)
      if((rv = flip_line(x, y, left, player, 1))) {
	goto SUCCESS;
      }
    if(x > 1 && y < 6)
      if((rv = flip_line(x, y, down_left, player, 1))) {
	goto SUCCESS;
      }
    if(y < 6)
      if((rv = flip_line(x, y, down, player, 1))) {
	goto SUCCESS;
      }
    if(y < 6 && x < 6)
      if((rv = flip_line(x, y, down_right, player, 1))) {
	goto SUCCESS;
      }
  }
  DONE:
    return(rv);
 SUCCESS:
    /*    rv = 1; */
    goto DONE;
}
  
int
square_buttonpress(XEvent *event)
     /* handle a buttonpress in a square */
{
  struct jester_data *jd = NULL;
  int rv = 0;
  int x = 0, y = 0;

  XFindContext(display, event->xany.window, jester_context,
	       (XPointer *)&jd);
  x = jd->coords.x;
  y = jd->coords.y;

  if(!legal_move(x, y, current_player))
    goto DONE;
  if(current_player == ai)
    goto DONE;
  
  if(current_player == WHITE) {
    pieces[x][y] = WHITE;
    current_player = BLACK;
  }
  else {
    pieces[x][y] = BLACK;
    current_player = WHITE;
  }
  draw_piece(pieces[x][y], event->xany.window);
  flip_pieces(jd->coords);
  update_score_window("White = %d Black = %d");
  show_player_id();
  mark_legal_squares(current_player);
  XFlush(display);

  if(!can_player_move(current_player)) {
    game_over = 1;
    update_score_window("GAME OVER White = %d Black = %d");
    show_restart();
    goto DONE;
  }

  if(ai == current_player)
    make_ai_move();
  else
    goto DONE;
  
  if(!can_player_move(current_player)) {
    game_over = 1;
    update_score_window("GAME OVER White = %d Black = %d");
    show_restart();
    goto DONE;
  }
    
  
 DONE:
  return(rv);
}

int
board_expose(XEvent *event)
     /* handle an expose event on the board */
{
  return(0);
}

struct jester_data *
new_jd()
     /* return a new struct jester_data */
{
  struct jester_data *jd = NULL;

  jd = (struct jester_data *)calloc(1, sizeof(struct jester_data));
  return(jd);
}

void
delete_jd(struct jester_data *jd)
     /* duh */
{
  if(jd)
    free(jd);
  return;
}

void
clean_up_stuff()
     /* dispose of globals, etc. */
{
  struct jester_data *jd = NULL;
  int i = 0, j = 0;

  if(jester_font)
    XUnloadFont(display, jester_font);
  if(legal_cursor)
    XFreeCursor(display, legal_cursor);
  if(gc)
    XFreeGC(display, gc);

  if(display) {
    for(i = 0; i < 8; i++) {
      for(j = 0; j < 8; j++) {
	XFindContext(display, square_windows[i][j], jester_context,
		     (XPointer *)&jd);
	delete_jd(jd);
      }
    }

    XFindContext(display, exit_button_window, jester_context, (XPointer *)&jd);
    delete_jd(jd);

    XFindContext(display, score_window, jester_context, (XPointer *)&jd);
    delete_jd(jd);

    XFindContext(display, player_id_window, jester_context, (XPointer *)&jd);
    delete_jd(jd);
    
    XFindContext(display, board_window, jester_context, (XPointer *)&jd);
    delete_jd(jd);
    XDestroyWindow(display, board_window);

    XCloseDisplay(display);
  }

  return;
}

int
flip_pieces(struct coords coords)
     /* flip over pieces that should be flipped, unless we are just testing */
{
  int new_x = 0, new_y = 0;
  int new_color = 0;
  int rv = 0;

  new_x = coords.x;
  new_y = coords.y;

  new_color = pieces[new_x][new_y];
  if(new_y > 1) {
    if(other_color(pieces[new_x][new_y - 1], new_color))
      rv = flip_line(new_x, new_y, up, new_color, 0);
  }
  if(new_x < 6 && new_y > 1) {
    if(other_color(pieces[new_x + 1][new_y - 1], new_color))
      rv = flip_line(new_x, new_y, up_right, new_color, 0);
  }
  if(new_x > 1 && new_y > 1) {
    if(other_color(pieces[new_x - 1][new_y - 1], new_color))
      rv = flip_line(new_x, new_y, up_left, new_color, 0);
  }
  if(new_x < 6) {
    if(other_color(pieces[new_x + 1][new_y], new_color))
      rv = flip_line(new_x, new_y, right, new_color, 0);
  }
  if(new_x > 1) {
    if(other_color(pieces[new_x - 1][new_y], new_color))
      rv = flip_line(new_x, new_y, left, new_color, 0);
  }
  if(new_x > 1 && new_y < 6) {
    if(other_color(pieces[new_x - 1][new_y + 1], new_color))
      rv = flip_line(new_x, new_y, down_left, new_color, 0);
  }
  if(new_y < 6) {
    if(other_color(pieces[new_x][new_y + 1], new_color))
      rv = flip_line(new_x, new_y, down, new_color, 0);
  }
  if(new_y < 6 && new_x < 6) {
    if(other_color(pieces[new_x + 1][new_y + 1], new_color))
      rv = flip_line(new_x, new_y, down_right, new_color, 0);
  }

  return(rv);
}


int
check_piece(int x, int y, int color)
     /* if pieces[x][y] is color, return 1, if EMPTY return -1, else 0 */
{
  int rv = 0;

  if(pieces[x][y] == color)
    rv = 1;
  else if(pieces[x][y] == EMPTY)
    rv = -1;
  return(rv);
}


int
flip_line(int x, int y, enum directions direction, int new_color, int testp)
     /* follow a line of pieces, if it is terminated by a piece of
	the same color, flip all intervening pieces, unless we are just
	testing */
{
  int i = 0, j = 0;
  int rv = 0;
  int counter = 0;
  int flipp = 0; /* a flag: if it is 1, we want to flip */
  
  switch(direction) {
  case up:
    for(i = x, j = y - 1, counter = 0; j >= 0; j--, counter++) {
      if((flipp = check_piece(i, j, new_color)) != 0 )
	break;
    }
    break;
  case up_right:
    for(counter = 0, i = x + 1, j = y - 1; i < 8 && j >= 0;
	i++, j--, counter++) {
      if((flipp = check_piece(i, j, new_color)) != 0 )
	break;
    }
    break;
  case right:
    for(counter = 0, i = x + 1, j = y; i < 8; i++, counter++) {
      if((flipp = check_piece(i, j, new_color)) != 0 )
	break;
    }
    break;
  case down_right:
    for(counter = 0, i = x + 1, j = y + 1; i < 8 && j < 8;
	i++, j++, counter++) {
      if((flipp = check_piece(i, j, new_color)) != 0 )
	break;
    }
    break;
  case down:
    for(counter = 0, i = x, j = y + 1; j < 8; j++, counter++) {
      if((flipp = check_piece(i, j, new_color)) != 0 )
	break;
    }
    break;
  case down_left:
    for(counter = 0, i = x - 1, j = y + 1; i >= 0 && j < 8;
	i--, j++, counter++) {
      if((flipp = check_piece(i, j, new_color)) != 0 )
	break;
    }
    break;
  case left:
    for(counter = 0, i = x - 1, j = y; i >= 0; i--, counter++) {
      if((flipp = check_piece(i, j, new_color)) != 0 )
	break;
    }
    break;
  case up_left:
    for(counter = 0, i = x - 1, j = y - 1; i >= 0 && j >= 0;
	i--, j--, counter++) {
      if((flipp = check_piece(i, j, new_color)) != 0 )
	break;
    }
    break;
  }
  
  if(!testp && flipp == 1 && counter)
    really_flip_line(x, y, i, j, new_color);
  if(flipp == 1 && counter)
    rv = counter;
  
  return(rv);
}

void
really_flip_line(int start_x, int start_y, int end_x, int end_y, int new_color)
     /* actually do the work of flipping the line of pieces */
{
  int i = 0, j = 0;
  int x_increment = 0, y_increment = 0;

  if(start_x < end_x)
    x_increment = 1;
  else if(start_x == end_x)
    x_increment = 0;
  else
    x_increment = -1;

  if(start_y < end_y)
    y_increment = 1;
  else if(start_y == end_y)
    y_increment = 0;
  else
    y_increment = -1;

  for(i = start_x, j = start_y; i != end_x || j != end_y;
      i += x_increment, j += y_increment) {
    pieces[i][j] = new_color;
    draw_piece(new_color, square_windows[i][j]);
  }

  return;
}

int
other_color(int s, int c)
{
  int rv = 0;
  if(s != EMPTY && s != c)
    rv = 1;
  return(rv);
}

int
count_pieces(int player)
     /* how many pieces does player have? */
{
  int rv = 0;
  int i = 0, j = 0;

  for(i = 0; i < 8; i++) {
    for(j = 0; j < 8; j++) {
      if(pieces[i][j] == player)
	rv++;
    }
  }

  return(rv);
}

int
can_player_move(int player)
     /* can player make a move, or must he forfeit the turn? */
{
  int rv = 0;
  int i = 0, j = 0;

  for(i = 0; i < 8; i++) {
    for(j = 0; j < 8; j++) {
      if(legal_move(i, j, player)) {
	rv = 1;
	goto DONE;
      }
    }
  }

 DONE:
  return(rv);
}

int
other_player(int player)
{
  int rv = 0;
  if(player == WHITE)
    rv = BLACK;
  else
    rv = WHITE;
  return(rv);
}

int
exit_button_expose(XEvent *event)
{
  int rv = 0;
  int x = 0, y = 0;

  x = center_text_x(display, EXIT_BUTTON_WIDTH, exit_text, jester_font);
  y = center_text_y(display, BUTTON_HEIGHT, jester_font);

  XSetFont(display, gc, jester_font);
  XSetForeground(display, gc, black);
  XSetBackground(display, gc, green);
  XDrawString(display, event->xexpose.window, gc, x, y, exit_text,
	      strlen(exit_text));
  

  return(rv);
}

int
center_text_x(Display *display, int width, char *text, Font font)
{
  return((width - get_string_width(display, text, font)) / 2);
}

int
center_text_y(Display *display, int height, Font font)
{
  return(height - get_font_height(display, font) / 2);
}

int
exit_button_buttonpress(XEvent *event)
{
  int rv = 0;

  exit_flag = 1;
  return(rv);
}


int
get_font_height(Display *display, Font font)
     /* returns the ascent + descent of font */
{
  int rv = 0;
  XFontStruct *fstruct = NULL;

  fstruct = XQueryFont(display, font);
  if(!fstruct) {
    fprintf(stderr, "font_height: font %ld does not exist.\n", font);
    goto ERROR;
  }

  rv = fstruct->ascent + fstruct->descent;

 DONE:
  if(fstruct)
    XFreeFontInfo(NULL, fstruct, 1);
  return(rv);

 ERROR:
  rv = 1;
  goto DONE;
}

int
get_string_width(Display *display, char *s, Font font)
     /* width of s in font */
{
  XFontStruct *fstruct = NULL;
  int rv = 0;

  fstruct = XQueryFont(display, font);
  if(!fstruct) {
    fprintf(stderr, "string_width: cannot find font %d.\n", (int)font);
    goto ERROR;
  }
  rv = XTextWidth(fstruct, s, strlen(s));
  XFreeFontInfo(NULL, fstruct, 1);

 DONE:
  return(rv);
 ERROR:
  goto DONE;
}

int
score_window_expose(XEvent *event)
{
  if(game_over)
    return(update_score_window("GAME OVER White = %d Black = %d"));
  else
    return(update_score_window("White = %d Black = %d"));
}

int
update_score_window(char *format)
     /* update the score window after each move */
{
  int black_pieces = 0, white_pieces = 0;
  int rv = 0;
  char text[256];
  int x = 0, y = 0;

  XClearWindow(display, score_window);
  black_pieces = count_pieces(BLACK);
  white_pieces = count_pieces(WHITE);
  /*  sprintf(text, "White = %d Black = %d", white_pieces, black_pieces); */
  sprintf(text, format, white_pieces, black_pieces);
  x = center_text_x(display, SCORE_WINDOW_WIDTH, text, jester_font);
  y = center_text_y(display, BUTTON_HEIGHT, jester_font);
  XSetForeground(display, gc, black);
  XSetFont(display, gc, jester_font);

  XDrawString(display, score_window, gc, x, y, text, strlen(text));

  return(rv);
}

int
player_id_window_expose(XEvent *event)
     /* if the game is ongoing, show the color of the current player;
	if it's over, offer to restart */
{
  int rv = 0;
  
  if(game_over == 0)
    show_player_id();
  else
    show_restart();

  return(rv);
}

void
show_player_id()
{
  unsigned long color = 0;

  if(current_player == WHITE)
    color = white;
  else
    color = black;
  
  XSetForeground(display, gc, color);
  XFillRectangle(display, player_id_window, gc, 0, 0, PLAYER_ID_WINDOW_WIDTH,
		 BUTTON_HEIGHT);
  return;
}
    
void
show_restart()
     /* show "RESTART" */
{
  char text[] = "RESTART";
  int x = 0, y = 0;

  XClearWindow(display, player_id_window);
  x = center_text_x(display, PLAYER_ID_WINDOW_WIDTH, text, jester_font);
  y = center_text_y(display, BUTTON_HEIGHT, jester_font);
  XSetForeground(display, gc, black);
  XSetFont(display, gc, jester_font);

  XDrawString(display, player_id_window, gc, x, y, text, strlen(text));

}

int
player_id_window_buttonpress(XEvent *event)
     /* if the game is over, restart; else do nothing */
{
  int rv = 0;
  int i = 0, j = 0;
  
  if(game_over) {
    game_over = 0;
    current_player = WHITE;
    show_player_id();
    set_up_pieces();
    update_score_window("White = %d Black = %d");
    for(i = 0; i < 8; i++) {
      for(j = 0; j < 8; j++) {
	if(pieces[i][j] == EMPTY)
	  XClearWindow(display, square_windows[i][j]);
	else
	  draw_piece(pieces[i][j], square_windows[i][j]);
      }
    }
    mark_legal_squares(current_player);
    if(ai == current_player)
      make_ai_move();
  }

  return(rv);
}
	
void
set_up_cursors()
     /* set up the cursors */
{
  legal_cursor = XCreateFontCursor(display, XC_hand2);

  return;
}

void
mark_legal_squares(int player)
     /* set the cursor properly for player */
{
  int i = 0, j = 0;

  for(i = 0; i < 8; i++) {
    for(j = 0; j < 8; j++) {
      if(legal_move(i, j, player))
	XDefineCursor(display, square_windows[i][j], legal_cursor);
      else
	XUndefineCursor(display, square_windows[i][j]);
    }
  }

  return;
}
	
void
create_board_window(int argc, char *argv[])
{
  struct jester_data *jd = NULL;
  XSizeHints *xsh = NULL;
  XClassHint *xch = NULL;
  XWMHints *xwmh = NULL;
  XTextProperty window_name;
  XTextProperty icon_name;
  int x = 0, y = 0; /* location of the toplevel window */

  xsh = XAllocSizeHints();
  xsh->flags = (PMinSize | PMaxSize);
  xsh->min_height = BOARD_HEIGHT;
  xsh->max_height = BOARD_HEIGHT;
  xsh->min_width = BOARD_WIDTH;
  xsh->max_width = BOARD_WIDTH;
  if(get_us_position(argc, argv, &x, &y)) {
    xsh->flags |= USPosition;
    xsh->x = x;
    xsh->y = y;
  }

  xch = XAllocClassHint();
  xch->res_name = program_name;
  xch->res_class = program_name;

  xwmh = XAllocWMHints();
  xwmh->flags = StateHint;
  xwmh->initial_state = NormalState;

  XStringListToTextProperty(argv, argc, &window_name);
  XStringListToTextProperty(&program_name, 1, &icon_name);

  board_window = XCreateSimpleWindow(display,
				     RootWindow(display, screen_number),
				     x, y, BOARD_WIDTH, BOARD_HEIGHT, 0, 0,
				     green);
  jd = new_jd();
  jd->expose_function = board_expose;
  XSaveContext(display, board_window, jester_context, (XPointer)jd);
  XSetWMProperties(display, board_window, &window_name, &icon_name, argv,
		   argc, xsh, xwmh, xch);
  XFree(xsh);
  XFree(xch);
  XFree(xwmh);
  XMapWindow(display, board_window);

  return;
}

void
create_exit_button()
{
  struct jester_data *jd = NULL;
  
  exit_button_window = XCreateSimpleWindow(display, board_window,
					     BORDER_WIDTH,
					     BORDER_WIDTH, EXIT_BUTTON_WIDTH,
					     BUTTON_HEIGHT, 1, black, green);
  jd = new_jd();
  jd->expose_function = exit_button_expose;
  jd->buttonpress_function = exit_button_buttonpress;
  XSaveContext(display, exit_button_window, jester_context, (XPointer)jd);
  XSelectInput(display, exit_button_window, ExposureMask | ButtonPressMask);
  XMapRaised(display, exit_button_window);

  return;
}

void
create_score_window()
{
  struct jester_data *jd = NULL;
  
  score_window = XCreateSimpleWindow(display, board_window,
				     EXIT_BUTTON_WIDTH + (BORDER_WIDTH * 2),
				     BORDER_WIDTH, SCORE_WINDOW_WIDTH,
				     BUTTON_HEIGHT, 1, black, green);
  jd = new_jd();
  jd->expose_function = score_window_expose;
  XSaveContext(display, score_window, jester_context, (XPointer)jd);
  XSelectInput(display, score_window, ExposureMask);
  XMapRaised(display, score_window);

  return;
}

void
create_player_id_window()
{
  struct jester_data *jd = NULL;
  
  player_id_window =
    XCreateSimpleWindow(display, board_window,
			EXIT_BUTTON_WIDTH + SCORE_WINDOW_WIDTH + (BORDER_WIDTH
								  * 3),
			BORDER_WIDTH, PLAYER_ID_WINDOW_WIDTH, BUTTON_HEIGHT,
			1, black, green);
  jd = new_jd();
  jd->expose_function = player_id_window_expose;
  jd->buttonpress_function = player_id_window_buttonpress;
  XSaveContext(display, player_id_window, jester_context, (XPointer)jd);
  XSelectInput(display, player_id_window, ExposureMask | ButtonPressMask);
  XMapRaised(display, player_id_window);

  return;
}

void
create_square_windows()
{
  struct jester_data *jd = NULL;
  int i = 0, j = 0;
    
  for(i = 0; i < 8; i++) {
    for(j = 0; j < 8; j++) {
      square_windows[i][j] = XCreateSimpleWindow(display, board_window,
					  (i * SQUARE_WIDTH) + BORDER_WIDTH,
					  (j * SQUARE_WIDTH) +
						 (BORDER_WIDTH * 2) +
						 BUTTON_HEIGHT,
					  SQUARE_WIDTH, SQUARE_WIDTH, 1, black,
					  green);
      jd = new_jd();
      jd->expose_function = square_expose;
      jd->buttonpress_function = square_buttonpress;
      jd->coords.x = i;
      jd->coords.y = j;
      XSaveContext(display, square_windows[i][j], jester_context,
		   (XPointer)jd);
      XSelectInput(display, square_windows[i][j],
		   ExposureMask | ButtonPressMask);
      XMapRaised(display, square_windows[i][j]);
    }
  }
  return;
}

char *get_text_option(int argc, char *argv[], char *option)
     /* return a pointer to the option's value, or NULL */
{
  char *rv = NULL;
  int i = 0;

  for(i = 0; i < argc; i++) {
    if(strcmp(argv[i], option) == 0) {
      rv = argv[i + 1];
      goto DONE;
    }
  }

 DONE:
  return(rv);
}
    
  
void
make_ai_move()
     /* make an ai move, ai is current player */
{
  struct coords move;
  
  sleep(1);

  get_best_ai_move(&move);
  pieces[move.x][move.y] = current_player;
  if(current_player == WHITE) {
    current_player = BLACK;
  }
  else {
    current_player = WHITE;
  }
  draw_piece(pieces[move.x][move.y], square_windows[move.x][move.y]);
  flip_pieces(move);
  update_score_window("White = %d Black = %d");
  show_player_id();
  mark_legal_squares(current_player);

  return;
}

void
get_best_ai_move(struct coords *coords)
     /* put the best move in coords, ai is current_player */
{
  int i = 0, j = 0, counter = 0, tmp = 0;

  for(i = 0; i < 8; i++) {
    for(j = 0; j < 8; j++) {
      tmp = legal_move(i, j, current_player);
      if(tmp > counter) {
	coords->x = i;
	coords->y = j;
	counter = tmp;
      }
      else if(tmp == counter) {
	if(rand() % 100 < 50) {
	  coords->x = i;
	  coords->y = j;
	}
      }
    }
  }

  return;
}

int
get_bool_option(int argc, char *argv[], char *option)
{
  int i = 0, rv = 0;

  for(i = 0; i < argc; i++) {
    if(strcmp(argv[i], option) == 0) {
      rv = 1;
      goto DONE;
    }
  }

 DONE:
  return(rv);
}

void
print_usage()
{
  printf("jester [{-display,-d,--display} display-name]\n[{-h,-help,--help}] [-2players] [-ai {white | black}]\n");
  return;
}

int
get_us_position(int argc, char *argv[], int *x, int *y)
     /* user specifies position through "-geometry +x+y" */
{
  char *s = NULL, *copy = NULL, *p1 = NULL, *p2 = NULL;
  int rv = 0; /* 1 if success */

  if((s = get_text_option(argc, argv, "-geometry")) ||
     (s = get_text_option(argc, argv, "-g")) ||
     (s = get_text_option(argc, argv, "--geometry"))) {
    copy = (char *)malloc(strlen(s) + 1);
    strcpy(copy, s);
    p1 = copy;
    while(*p1 != '+') /* in case a regular geometry string has been passed */
      p1++;
    p1++; /* skip the initial '+' */
    p2 = p1;
    while(*p1 && isdigit(*p1))
      p1++;
    *p1 = '\0';
    *x = atoi(p2);
    p1++;
    p2 = p1;
    while(*p1 && isdigit(*p1))
      p1++;
    *p1 = '\0';
    *y = atoi(p2);
    rv = 1;
  }

  return(rv);
}
    
