/*
 * ai.c
 *
 * 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
 *
 * For more details see the file COPYING.
 */

#include <gnome.h>

#include "ai.h"

gint8 board0[SIZE][SIZE];
gint vsquares[MAX_VPOINTER][3];
guint move_count;
guint whose_turn;
guint bcount;
guint wcount;

static gint history[MAX_MOVE_COUNT][13];
static gint vpointer;
static GRand *rgen;


/* Initialization of data */
void
init (void)
{
  gint i, j;

  rgen = g_rand_new ();

  for (i = 0; i < SIZE; i++)
    for (j = 0; j < SIZE; j++)
      board0[i][j] = BLANK;

  board0[0][0] = WHITE;
  board0[0][SIZE - 1] = BLACK;
  board0[SIZE - 1][0] = BLACK;
  board0[SIZE - 1][SIZE - 1] = WHITE;

  vpointer = 0;
  move_count = 0;
  whose_turn = WHITE;
  bcount = 2;
  wcount = 2;
}

gboolean
is_valid (gint ox, gint oy, gint nx, gint ny)
{
  if (ox < 0 || ox > SIZE - 1 || nx < 0 || nx > SIZE - 1)
    return FALSE;
  if (board0[oy][ox] != (gint8) whose_turn)
    return FALSE;
  if (board0[ny][nx] != BLANK)
    return FALSE;
  if (ox - nx > 2 || ox - nx < -2 || oy - ny > 2 || oy - ny < -2)
    return FALSE;
  return TRUE;
}

void
move_board (gint ox, gint oy, gint nx, gint ny)
{
  gint i, j;
  gint count = 0, stat = 0, not_me = OTHER_PLAYER (whose_turn);

  if (!is_valid (ox, oy, nx, ny))
    return;

  for (i = ny - 1; i <= ny + 1; i++)
    for (j = nx - 1; j <= nx + 1; j++)
      if (i >= 0 && i <= SIZE - 1 && j >= 0 && j <= SIZE - 1)
	{
	  if (board0[i][j] == (gint8) whose_turn)
	    stat = 1;
	  else if (board0[i][j] == (gint8) not_me)
	    {
	      board0[i][j] = (gint8) whose_turn;
	      count++;
	      history[move_count][4 + count] = i * SIZE + j;
	    }
	}

  board0[ny][nx] = (gint8) whose_turn;
  if (!stat)
    board0[oy][ox] = BLANK;
  history[move_count][0] = whose_turn;
  history[move_count][1] = oy * SIZE + ox;
  history[move_count][2] = ny * SIZE + nx;
  history[move_count][3] = stat;
  history[move_count][4] = count;
  if (whose_turn == BLACK)
    {
      bcount += count + stat;
      wcount -= count;
    }
  else
    {
      wcount += count + stat;
      bcount -= count;
    }
  move_count++;
  whose_turn = OTHER_PLAYER (whose_turn);
}

/* Back to before board0 */
void
back (void)
{
  gint o_yx, n_yx;
  gint count, not_me, stat;

  move_count--;
  whose_turn = history[move_count][0];
  o_yx = history[move_count][1];
  n_yx = history[move_count][2];
  stat = history[move_count][3];
  count = history[move_count][4];

  if (whose_turn == WHITE)
    {
      wcount -= count + stat;
      bcount += count;
    }
  else
    {
      bcount -= count + stat;
      wcount += count;
    }

  board0[n_yx / SIZE][n_yx % SIZE] = BLANK;
  if (!stat)
    board0[o_yx / SIZE][o_yx % SIZE] = (gint8) whose_turn;
  not_me = OTHER_PLAYER (whose_turn);
  while (count > 0)
    {
      o_yx = history[move_count][4 + count];
      board0[o_yx / SIZE][o_yx % SIZE] = (gint8) not_me;
      count--;
    }
}

gint
entry_valid_movement0 (gint ox, gint oy, gint j)
{
  gint i, nx, ny;

  i = j;
  for (ny = oy - 2; ny <= oy + 2; ny++)
    if (ny >= 0 && ny <= SIZE - 1)
      for (nx = ox - 2; nx <= ox + 2; nx++)
	if (nx >= 0 && nx <= SIZE - 1)
	  {
	    if (is_valid (ox, oy, nx, ny))
	      {
		i++;
		vsquares[i][1] = oy * SIZE + ox;
		vsquares[i][2] = ny * SIZE + nx;
		move_board (ox, oy, nx, ny);
		if (whose_turn == WHITE)
		  vsquares[i][0] = wcount - bcount;
		else
		  vsquares[i][0] = bcount - wcount;
		back ();
	      }
	  }
  return i;
}

gint
entry_valid_movement (void)
{
  gint ox, oy;
  gint j;

  j = vpointer;
  for (oy = 0; oy < SIZE; oy++)
    for (ox = 0; ox < SIZE; ox++)
      j = entry_valid_movement0 (ox, oy, j);

  return j - vpointer;
}

/* Sort vsquares by small order */
static void
sort (guint l, guint r)
{
  guint i, j;
  gint u, v, w, x;

  i = l;
  j = r;
  x = vsquares[(l + r) / 2][0];
  do
    {
      while (vsquares[i][0] < x)
	i++;
      while (x < vsquares[j][0])
	j--;
      if (i < j)
	{
	  u = vsquares[i][0];
	  v = vsquares[i][1];
	  w = vsquares[i][2];
	  vsquares[i][0] = vsquares[j][0];
	  vsquares[i][1] = vsquares[j][1];
	  vsquares[i][2] = vsquares[j][2];
	  vsquares[j][0] = u;
	  vsquares[j][1] = v;
	  vsquares[j][2] = w;
	}
      if (i <= j)
	{
	  i++;
	  j--;
	}
    }
  while (i <= j);

  if (l < j)
    sort (l, j);
  if (i < r)
    sort (i, r);
}

/* alpha-beta search */
static gint
search (guint n, gint a, gint b)
{
  gint ox, oy, nx, ny;
  gint aa, bb;
  gint i, j;

  if (!n)			/* end ? */
    {
      if (whose_turn == WHITE)
	return wcount - bcount;
      return bcount - wcount;
    }

  aa = a;
  j = entry_valid_movement ();
  if (!j)			/* end ? */
    {
      aa = wcount - bcount;
      if (whose_turn == BLACK)
	aa = -aa;
      if (aa > 0)
	return aa + 1000;
      if (aa < 0)
	return aa - 1000;
      return 0;
    }

  i = vpointer;			/* save vpointer    */
  vpointer = i + j + 1;		/* new vpointer     */
  j = i + 1;
  sort (j, vpointer - 1);	/* sort vsquares    */

  while (aa < b && j < vpointer) /* evaluate squares */
    {
      oy = vsquares[j][1] / SIZE;
      ox = vsquares[j][1] % SIZE;
      ny = vsquares[j][2] / SIZE;
      nx = vsquares[j][2] % SIZE;
      move_board (ox, oy, nx, ny);
      bb = -search (n - 1, -b, -aa);
      back ();
      if (aa < bb)
	{
	  aa = bb;
	  vsquares[i][1] = vsquares[j][1];
	  vsquares[i][2] = vsquares[j][2];
	}
      j++;
    }
  vpointer = i;			/* pop vpointer      */
  return aa;
}

static gint
random_select (void)
{
  guint i, j;

  j = entry_valid_movement ();
  i = vpointer + 1;
  if (j > i)
    i = g_rand_int_range (rgen, i, j + 1);
  vsquares[0][1] = vsquares[i][1];
  vsquares[0][2] = vsquares[i][2];
  return 0;
}

void
computer_move (gint level, gint * ox, gint * oy, gint * nx, gint * ny)
{
  if (move_count < RANDOM)
    vsquares[0][0] = random_select ();
  else if (move_count < RANDOM * 3)
    vsquares[0][0] = search (level + 1, -32767, 32767);
  else if (SIZE * SIZE - bcount - wcount < level * 3)
    vsquares[0][0] = search (level + 1, -32767, 32767);
  else
    vsquares[0][0] = search (level, -32767, 32767);

  *ox = vsquares[0][1] % SIZE;
  *oy = vsquares[0][1] / SIZE;
  *nx = vsquares[0][2] % SIZE;
  *ny = vsquares[0][2] / SIZE;
}


-------------------------------------------------------------------


/*
 * ai.h
 *
 * 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
 *
 * For more details see the file COPYING.
 */

#define SIZE            7
#define RANDOM          4
#define MAX_DEPTH	5
#define MAX_MOVE_COUNT	105
#define MAX_VPOINTER	MAX_DEPTH * 120

#define BLACK           49
#define WHITE           79
#define BLANK           1

#define OUTSIDE           (BLACK + WHITE - 1)
#define OTHER_PLAYER(who) (OUTSIDE - who + 1)

void init (void);

gboolean is_valid (gint ox, gint oy, gint nx, gint ny);

void move_board (gint ox, gint oy, gint nx, gint ny);

void back (void);

gint entry_valid_movement0 (gint ox, gint oy, gint j);

gint entry_valid_movement (void);

void computer_move (gint level, gint *ox, gint *oy, gint *nx, gint *ny);


-------------------------------------------------------------------


/*
 * gataxx.c
 *
 * 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
 *
 * For more details see the file COPYING.
 */


#include "config.h"
#include <gnome.h>
#include <glib.h>
#include <gconf/gconf-client.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>

#include "games-stock.h"

#include "ai.h"
#include "properties.h"
#include "gataxx.h"

static const gchar *strs[] = {
  N_("Dark's move"),			/* 0 */
  N_("Light's move"),			/* 1 */
  N_("Light player wins!"),		/* 2 */
  N_("Dark player wins!"),		/* 3 */
  N_("The game was a draw."),		/* 4 */
  N_("Now thinking."),		        /* 5 */
  N_("Count overflow!"),	        /* 6 */
  N_("not gataxx's file"),		/* 7 */
  N_("Invalid move.")		        /* 8 */
};

static const gchar *keys[MAX_KEYS] = {
  "/apps/gataxx/black_level",		/* 0 */
  "/apps/gataxx/white_level",		/* 1 */
  "/apps/gataxx/none_animation",	/* 2 */
  "/apps/gataxx/use_quick_moves",	/* 3 */
  "/apps/gataxx/hide_valid_squares",	/* 4 */
  "/apps/gataxx/hide_last_move",	/* 5 */
  "/apps/gataxx/hide_final_results",	/* 6 */
  "/apps/gataxx/tileset",		/* 7 */
  "/apps/gataxx/bg_color_name",		/* 8 */
  "/apps/gataxx/board_width",		/* 9 */
  "/apps/gataxx/board_height",		/* 10 */
  "gataxx",				/* 11 Window title */
  "Gataxx Preferences"			/* 12 Properties dialog title */
};

static guint8    kifu[MAX_MOVE_COUNT][3];
static gint      old_x = -1;
static gint      old_y = -1;
static guint     t_black_computer_level;
static guint     t_white_computer_level;
static gboolean  t_hide_last_move;
static guint     computer_id = 0;
static guint     computer_id1 = 0;


extern gint8     pixmaps[MAX_SIZE][MAX_SIZE];
extern gint8     board[MAX_SIZE][MAX_SIZE];

extern GtkWidget *window;
extern GtkWidget *file_open_dialog;
extern GtkWidget *file_save_dialog;
extern GtkWidget *counter;
extern GtkWidget *score[4];
extern GtkWidget *entry;
extern GTimer    *b_timer;
extern GTimer    *w_timer;

extern guint     tile_width;
extern guint     tile_height;
extern guint     black_computer_level;
extern guint     white_computer_level;
extern gboolean  hide_valid_squares;
extern gboolean  hide_last_move;
extern gboolean  hide_final_results;
extern guint     computer_speed;
extern gboolean  game_in_progress;
extern gboolean  gameover;
extern gboolean  flip_tiles;
extern guint     flip_final_id;
extern gboolean  play_sound;
extern guint     t_move_count;
extern guint     t_whose_turn;
extern guint     t_bcount;
extern guint     t_wcount;
extern gboolean  ggz_network_mode;


extern gint8     board0[SIZE][SIZE];
extern gint      vsquares[MAX_VPOINTER][3];
extern guint     move_count;
extern guint     whose_turn;
extern guint     bcount;
extern guint     wcount;


static void
copy_board (void)
{
  guint i, j;

  for (i = 0; i < SIZE; i++)
    for (j = 0; j < SIZE; j++)
      board[i][j] = board0[i][j];

  t_move_count = move_count;
  t_whose_turn = whose_turn;
  t_bcount = bcount;
  t_wcount = wcount;
}

static void
gui_hint (gint oyx, gint nyx, gint score)
{
  gchar message[16];

  message[0] = (gchar) (oyx % SIZE + 0x41);
  message[1] = (gchar) (oyx / SIZE + 0x31);
  message[2] = 0x2D;
  message[3] = (gchar) (nyx % SIZE + 0x41);
  message[4] = (gchar) (nyx / SIZE + 0x31);
  message[5] = 0x20;
  sprintf (&message[6], _("%d"), score);

  gui_entry (message);
}

static gboolean
check_game_over (void)
{
  gint i;

  if (!hide_last_move && move_count > 0)
    board[kifu[move_count - 1][2] / SIZE]
	  [kifu[move_count - 1][2] % SIZE] = -kifu[move_count - 1][0];

  i = entry_valid_movement ();
  if (i)
    {
      if (whose_turn == BLACK)
	{
	  gui_message ((gchar *)strs[0]);
	  if (game_in_progress)
	    g_timer_continue (b_timer);
	}
      else if (whose_turn == WHITE)
	{
	  gui_message ((gchar *)strs[1]);
	  if (game_in_progress)
	    g_timer_continue (w_timer);
	}
      return FALSE;
    }

  if (wcount > bcount)
    gui_message ((gchar *)strs[2]);
  if (bcount > wcount)
    gui_message ((gchar *)strs[3]);
  if (wcount == bcount)
    gui_message ((gchar *)strs[4]);

  t_whose_turn = whose_turn = 0;
  game_in_progress = FALSE;
  gameover = TRUE;
  if (!hide_final_results)
    flip_final_id = g_timeout_add (computer_speed, flip_final_results, NULL);

  return TRUE;
}

static gboolean
t_computer_move (gpointer level)
{
  gint ox, oy, nx, ny;

  computer_move (GPOINTER_TO_INT (level), &ox, &oy, &nx, &ny);
  if (game_in_progress)
    move (whose_turn, ox, oy, nx, ny);
  computer_id = 0;
  return FALSE;
}

static gboolean
check_computer_players (void)
{
  guint level = 0;

  if (computer_id)
    {
      g_source_remove (computer_id);
      computer_id = 0;
    }

  if (black_computer_level && t_whose_turn == BLACK)
    level = black_computer_level;
  else if (white_computer_level && t_whose_turn == WHITE)
    level = white_computer_level;
  if (level)
    {
      gui_entry ((gchar *)strs[5]);
      computer_id = g_timeout_add (computer_speed,
                      (GSourceFunc) t_computer_move, GINT_TO_POINTER (level));
    }
  return FALSE;
}

static gboolean
check_computer_level_changed (gpointer user_data)
{
   gint yx;
  
  if (t_hide_last_move != hide_last_move && move_count > 0)
    {
      t_hide_last_move = hide_last_move;
      yx = kifu[move_count - 1][2];
      if (!hide_last_move)
	board[yx / SIZE][yx % SIZE] = -kifu[move_count - 1][0];
      else
	board[yx / SIZE][yx % SIZE] = kifu[move_count - 1][0];
      redraw_board ();
    };

  if (!game_in_progress)
    return TRUE;
  if (t_black_computer_level == black_computer_level
      && t_white_computer_level == white_computer_level)
    return TRUE;

  t_black_computer_level = black_computer_level;
  t_white_computer_level = white_computer_level;
  check_computer_players ();

  return TRUE;
};

static void
set_computerid1 (void)
{
  t_black_computer_level = black_computer_level;
  t_white_computer_level = white_computer_level;
  t_hide_last_move = hide_last_move;
  computer_id1 = g_timeout_add (500,
                          (GSourceFunc) check_computer_level_changed , NULL);
};

static gboolean
check_valid_move (gint who, gint ox, gint oy, gint nx, gint ny)
{
  if (!game_in_progress)
    return FALSE;
  if (who != whose_turn)
    return FALSE;
  if (ox < 0 || ox > SIZE - 1 || oy < 0 || oy > SIZE - 1
      || nx < 0 || nx > SIZE - 1 || ny < 0 || ny > SIZE - 1)
    return FALSE;

  return is_valid (ox, oy, nx, ny);
}

gboolean
move (gint who, gint ox, gint oy, gint nx, gint ny)
{
  if (!check_valid_move (who, ox, oy, nx, ny))
    {
      gui_message ((gchar *)strs[8]);
      return FALSE;
    }
  if (game_in_progress)
    {
      kifu[move_count][0] = whose_turn;
      kifu[move_count][1] = oy * SIZE + ox;
      kifu[move_count][2] = ny * SIZE + nx;
      kifu[move_count + 1][0] = 0;
      if (whose_turn == BLACK)
	g_timer_stop (b_timer);
      else
	g_timer_stop (w_timer);
    }

  move_board (ox, oy, nx, ny);

  if (move_count >= MAX_MOVE_COUNT - MAX_DEPTH)
    {
      gui_message ((gchar *)strs[6]);
      game_in_progress = FALSE;
      return FALSE;
    }

  old_x = -1;
  old_y = -1;
  gui_entry (" ");
  gtk_entry_set_position ((GtkEntry *) entry, 0);
  copy_board ();
  gui_status ();
  check_game_over ();
  flip_tiles = TRUE;
  play_sound = TRUE;
  if (game_in_progress)
    return check_computer_players ();

  return FALSE;
}

void
init_new_game (void)
{
  if (flip_final_id)
    {
      g_source_remove (flip_final_id);
      flip_final_id = 0;
    }
  if (computer_id)
    {
      g_source_remove (computer_id);
      computer_id = 0;
    }

  kifu[0][0] = 0;
  init ();
  copy_board ();
  game_in_progress = TRUE;
  g_timer_stop (b_timer);
  g_timer_reset (b_timer);
  g_timer_stop (w_timer);
  g_timer_reset (w_timer);
  gui_timer ();
  timer_start ();
  check_game_over ();
  redraw_board ();
  check_computer_players ();
}

static void
on_new_game_activate (GtkMenuItem *menuitem, gpointer user_data)
{
  init_new_game ();
}

static void
on_redo_activate (GtkMenuItem *menuitem, gpointer user_data)
{
  if (game_in_progress)
    return;
  if (move_count < 0)
    return;
  if (!entry_valid_movement ())
    return;

  game_in_progress = TRUE;
  kifu[move_count][0] = 0;
  check_game_over ();
  check_computer_players ();
}

static void
on_hint_activate (GtkMenuItem *menuitem, gpointer user_data)
{
  gint ox, oy, nx, ny;

  if (move_count < 0)
    return;
  if (!entry_valid_movement ())
    return;

  computer_move (MAX_DEPTH, &ox, &oy, &nx, &ny);
  gui_hint (oy * SIZE + ox, ny * SIZE + nx, vsquares[0][0]);
}

static gboolean
check_back (void)
{
  if (flip_final_id)
    {
      g_source_remove (flip_final_id);
      flip_final_id = 0;
    }
  if (game_in_progress)
    {
      game_in_progress = FALSE;
      if (whose_turn == BLACK)
	g_timer_stop (b_timer);
      else
	g_timer_stop (w_timer);
    }
  return TRUE;
}

static void
on_head_button_clicked (GtkToolButton *toolbutton, gpointer user_data)
{
  if (!check_back ())
    return;

  init ();
  copy_board ();
  check_game_over ();
  redraw_board ();
}

static void
read_kifu (int fd)
{
  gchar buf[12];
  gint i = 0;
  ssize_t err;

  err = read (fd, buf, 7);
  buf[7] = 0;
  if (!g_str_equal (buf, _("gataxx\n")))
    {
      gui_message ((gchar *)strs[7]);
      return;
    }
  err = read (fd, buf, 11);
  while (err > 0)
    {
      i = (gint) ((buf[0] - 0x30) * 10 + buf[1] - 0x30 - 1);
      kifu[i][0] = (buf[3] == 0x42) ? BLACK : WHITE;
      kifu[i][1] = (gint) ((buf[6] - 0x31) * 7 + buf[5] - 0x41);
      kifu[i][2] = (gint) ((buf[9] - 0x31) * 7 + buf[8] - 0x41);
      err = read (fd, buf, 11);
    }
  kifu[i + 1][0] = 0;
}

static void
on_open_clicked (GtkButton *button, gpointer user_data)
{
  gchar *filename;
  int fd;
  
  filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_open_dialog));
  fd = open (filename, 0, 0644);
  if (fd)
    {
        read_kifu (fd);
        close (fd);
    }
  gtk_widget_destroy (file_open_dialog);
  g_free (filename);
  on_head_button_clicked (NULL, NULL);
}

static void
on_open_activate (GtkMenuItem *menuitem, gpointer user_data)
{
  game_in_progress = FALSE;
  g_timer_stop (b_timer);
  g_timer_stop (w_timer);
  g_timer_reset (b_timer);
  g_timer_reset (w_timer);
  gui_timer ();
  kifu[0][0] = 0;
  file_open_dialog = create_file_open_dialog ((GtkSignalFunc *)on_open_clicked);
  gtk_widget_show (file_open_dialog);
}

static void
write_kifu (int fd)
{
  gchar buf[12] = { 0, 0, 0x20, 0, 0x20, 0, 0, 0x2D, 0, 0, 0x0A, 0 };
  gint8 i = 0;
  ssize_t err;

  err = write (fd, _("gataxx\n"), 7);
  while (kifu[i][0] && err > 0)
    {
      buf[0] = (i + 1) / 10 + 0x30;
      buf[1] = (i + 1) % 10 + 0x30;
      buf[3] = (kifu[i][0] == BLACK) ? 0x42 : 0x57;
      buf[5] = (gchar) (kifu[i][1] % 7 + 0x41);
      buf[6] = (gchar) (kifu[i][1] / 7 + 0x31);
      buf[8] = (gchar) (kifu[i][2] % 7 + 0x41);
      buf[9] = (gchar) (kifu[i][2] / 7 + 0x31);

      err = write (fd, buf, 11);
      i++;
    }
}

static void
on_save_clicked (GtkButton *button, gpointer user_data)
{
  gchar *filename;
  int fd;

  filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_save_dialog));
  fd = creat (filename, 0644);
  if (fd)
    {
      write_kifu (fd);
      close (fd);
    }
  gtk_widget_destroy (file_save_dialog);
  g_free (filename);
}

static void
on_save_activate (GtkMenuItem *menuitem, gpointer user_data)
{
  GDate *date;
  gchar filename[17];

  date = g_date_new ();
  g_date_set_time_t (date, time (NULL));
  g_date_strftime (filename, 17, _("%y%m%d%H"), date);

  file_save_dialog = create_file_save_dialog ((GtkSignalFunc *)on_save_clicked);
  gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER
				     (file_save_dialog), filename);
  gtk_widget_show (file_save_dialog);
  g_date_free (date);
}

static void
on_help_activate (GtkMenuItem *menuitem, gpointer user_data)
{
  gnome_help_display ("gataxx.xml", NULL, NULL);
}

static void
on_about_activate (GtkMenuItem *menuitem, gpointer user_data)
{
  const gchar *authors[] = {
    "Chris Rogers", "Sjoerd Langkemper",
    N_("Based on code from Iagno by Ian Peters"), NULL
  };

  const gchar *documenters[] = {
    "Aaron Weber",
    NULL
  };

  gtk_show_about_dialog (GTK_WINDOW (window),
			 "name", _("Ataxx"),
			 "version", VERSION,
			 "copyright",
			 "Copyright \xc2\xa9 1999-2003 Chris Rogers\n"
			 "Copyright \xc2\xa9 2004-2005 Sjoerd Langkemper\n"
			 "Copyright \xc2\xa9 2005-2006 Callum McKenzie",
			 "license", "GPL 2+", "comments",
			 _
			 ("A disk-flipping game where you attempt to dominate the board."),
			 "authors", authors, "documenters", documenters,
			 "translator_credits", _("translator-credits"),
			 "logo-icon-name", "gnome-ataxx", "website",
			 "http://www.gnome.org/projects/gnome-games/",
			 "wrap-license", TRUE, NULL);
}

static void
on_back_button_clicked (GtkToolButton *toolbutton, gpointer user_data)
{
  if (move_count < 1)
    return;
  if (!check_back ())
    return;

  back ();
  copy_board ();
  check_game_over ();
  gui_status ();
  flip_tiles = TRUE;
  play_sound = TRUE;
}

static void
on_forward_button_clicked (GtkToolButton *toolbutton, gpointer user_data)
{
  game_in_progress = FALSE;

  if (!kifu[move_count][0])
    {
      if (!check_game_over ())
	{
	  game_in_progress = TRUE;
	  check_computer_players ();
	}
      return;
    }

  whose_turn = kifu[move_count][0];
  move_board (kifu[move_count][1] % SIZE, kifu[move_count][1] / SIZE,
	      kifu[move_count][2] % SIZE, kifu[move_count][2] / SIZE);
  copy_board ();
  check_game_over ();
  gui_status ();
  flip_tiles = TRUE;
  play_sound = TRUE;
}

static void
on_last_button_clicked (GtkToolButton *toolbutton, gpointer user_data)
{
  game_in_progress = FALSE;

  while (kifu[move_count][0])
    {
      whose_turn = kifu[move_count][0];
      move_board (kifu[move_count][1] % SIZE, kifu[move_count][1] / SIZE,
		  kifu[move_count][2] % SIZE, kifu[move_count][2] / SIZE);
    }
  copy_board ();
  if (!check_game_over ())
    {
      game_in_progress = TRUE;
      check_computer_players ();
    }
  redraw_board ();
}

static gboolean
check_event_move (gint ox, gint oy, gint nx, gint ny)
{
  if (whose_turn == WHITE && white_computer_level)
    return FALSE;
  if (whose_turn == BLACK && black_computer_level)
    return FALSE;
  return move (whose_turn, ox, oy, nx, ny);
}

static void
set_valid_squares (void)
{
  gint i, yx;
  
  if (hide_valid_squares)
    return;

  copy_board ();
  i = entry_valid_movement0 (old_x, old_y, 0);
  while (i > 0)
    {
      yx = vsquares[i--][2];
      board[yx / SIZE][yx % SIZE] = -BLANK;
    }
  redraw_board ();
};

static gboolean
on_drawing_area_button_press_event (GtkWidget *widget,
				    GdkEventButton *event,
				    gpointer user_data)
{
  guint x, y;

  if (event->button != 1)
    return FALSE;

  x = event->x / (tile_width + GRIDWIDTH);
  y = event->y / (tile_height + GRIDWIDTH);
  if (board[y][x] == whose_turn)
    {
      old_x = x;
      old_y = y;
      set_valid_squares ();
      return FALSE;
    }
  return check_event_move (old_x, old_y, x, y);
}

static gboolean
on_entry_key_press_event (GtkWidget *widget,
			  GdkEventKey *event, gpointer user_data)
{
  gchar *ch;
  gint ox, oy, nx, ny;

  ch = (gchar *) gtk_entry_get_text (GTK_ENTRY (entry));
  ox = (gint) ((*ch++ & 0xdf) - 0x41);
  if (!*ch)
    return FALSE;
  oy = (gint) (*ch++ - 0x31);
  if (!*ch)
    return FALSE;
  if (*ch == 0x2D)
    ch++;
  nx = (gint) ((*ch++ & 0xdf) - 0x41);
  if (!*ch)
    return FALSE;
  ny = (gint) (*ch - 0x31);

  return check_event_move (ox, oy, nx, ny);
}

const GCallback callbacks[MAX_ACTIONS] = {
  NULL,
  (GCallback) on_new_game_activate,
  (GCallback) on_redo_activate,
  (GCallback) on_hint_activate,
  NULL,
  NULL,
  (GCallback) on_open_activate,
  (GCallback) on_save_activate,
  (GCallback) on_quit_activate,
  NULL,
  (GCallback) on_properties_activate,
  (GCallback) on_minimum_board_activate,
  NULL,
  (GCallback) on_help_activate,
  (GCallback) on_about_activate,
  (GCallback) on_head_button_clicked,
  (GCallback) on_back_button_clicked,
  (GCallback) on_forward_button_clicked,
  (GCallback) on_last_button_clicked,
  (GCallback) on_hint_activate,
  (GCallback) on_redo_activate,
  (GCallback) on_new_game_activate,
  (GCallback) on_quit_activate
};

const GtkSignalFunc signalfuncs[2] = {
  (GtkSignalFunc) on_drawing_area_button_press_event,
  (GtkSignalFunc) on_entry_key_press_event,
};

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

  gnome_score_init ("gataxx");

  bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
/*	bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); */
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  textdomain (GETTEXT_PACKAGE);

  program =
    gnome_program_init ("iagno", VERSION, LIBGNOMEUI_MODULE, argc, argv,
			GNOME_PARAM_APP_DATADIR, PACKAGE_DATA_DIR, NULL);
/*			    GNOME_PARAM_APP_DATADIR, DATADIR, NULL); */

  gconf_init (argc, argv, NULL);
  games_stock_init ();
  gtk_window_set_default_icon_name ("gnome-ataxx");

  init_interface (SIZE, MAX_DEPTH, (gchar **) keys,
		  (GCallback *) callbacks, (GtkSignalFunc *) signalfuncs);
  load_properties ();
  window = create_window ();
  reset_properties ();
  apply_changes ();
  set_computerid1 ();

  init_new_game ();
  gtk_main ();

  g_object_unref (program);
  return 0;
}


-------------------------------------------------------------------


/*
 * gataxx.h
 *
 * 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
 *
 * For more details see the file COPYING.
 */


gboolean move (gint who, gint ox, gint oy, gint nx, gint ny);

void init_new_game (void);

