/*
 * src/pente.c, part of Pente (game program)
 * Copyright (C) 1994-1995 William Shubert.
 * See "configure.h.in" for more copyright information.
 */

#include "pente.h"

static void  play_pente(int level[BD_MAXPLAYERS],
			uint init(int level[BD_MAXPLAYERS], uint winner,
				  pl_t *game),
			void draw(pl_t *game, bool forced),
			bd_loc_t selectmove(pl_t *game, int compfd),
			void  show_think(bd_loc_t pos, uint player),
			pl_t *game, bd_loc_t firstmove,
			int  from_c, int to_c);

static pl_t  game;

Clp  *pe_clp;
Rnd  *pe_rnd;

static struct  {
  char  *name;
  uint  (*init)(int level[BD_MAXPLAYERS], uint winner, pl_t *game);
  void  (*draw)(pl_t *game, bool forced);
  bd_loc_t  (*selectmove)(pl_t *game, int compfd);
  void  (*show_think)(bd_loc_t pos, uint player);
} disp[] = {
#if  W_DISP
  {"W1", w1_init,  w1_draw,  w1_selectmove, w1_think},
#endif
#if  CURSES_DISP
  {"curses", cio_init,  cio_draw,  cio_selectmove, cio_think},
#endif
  {"text", text_init, text_draw, text_selectmove, text_think},
  {NULL, NULL, NULL}};

static ClpSetup  clpVars[] = {
  CLPSETUP_MSG("Pente " VERSION " by William Shubert - " DATE),
  CLPSETUP_MSG(""),
  {"VersionNumber", VERSION, NULL, 0, NULL},
  {"version,-version", "f", "Print version information",
     CLPSETUP_BOOL|CLPSETUP_NOSAVE, NULL},
  {"showthink", "t", "Show computer thinking", CLPSETUP_BOOL, NULL},
  CLPSETUP_MSG(""),
  CLPSETUP_MSG("Display type options:"),
#if  W_DISP
  {"W1", "f", "W1R2", CLPSETUP_BOOL|CLPSETUP_SHOWBOOL|CLPSETUP_NOSAVE,
     NULL},
#endif
#if  CURSES_DISP
  {"curses", "f", "Curses", CLPSETUP_BOOL|CLPSETUP_SHOWBOOL|CLPSETUP_NOSAVE,
     NULL},
#endif
  {"text", "f", "Plain text", CLPSETUP_BOOL|CLPSETUP_SHOWBOOL|CLPSETUP_NOSAVE,
     NULL},
  {"perf", "f", NULL, CLPSETUP_BOOL, NULL},

  {"player1", "0", NULL, 0, NULL},
  {"player2", "3", NULL, 0, NULL},
  {"board", NULL, NULL, 0, NULL},

  CLPSETUP_END};

static int  play_level[BD_MAXPLAYERS] = {3, 0};


int  main(int argc, char *argv[])  {
  int  tnum, defnum;
  bd_loc_t  resp;
  int  from_c, to_c;
    
  pe_clp = clp_create(clpVars);

  if ((argc = clp_rCmdline(pe_clp, argv)) == CLP_ARGS_NOGOOD)  {
    exit(1);
  }
  play_level[0] = clp_getInt(pe_clp, "player1");
  play_level[1] = clp_getInt(pe_clp, "player2");
  
  if (clp_getBool(pe_clp, "version"))  {
    printf("Pente " VERSION " by William Shubert (wms@ssd.intel.com) "
	   DATE "\n");
    exit(0);
  }

  if (clp_getBool(pe_clp, "perf"))  {
    /* Analyze the performance of pente...have the computer play itself
     *   at level 5, with a known random seed, to get the same game
     *   every time.  Time this on different machines to determine
     *   how fast that machine is.
     */
    bd_loc_t  loc;
    bool  game_over;

    pe_rnd = rnd_create(0);
    pl_init(&game);
    do  {
      loc = comp_noSpawnGetMove(&game, 5);
      game_over = pl_move(&game, loc);
    } while (!game_over);
    if (pl_nmoves(&game) != 34)  {
      fprintf(stderr,
	      "pente: Timing invalid.  Game play was not deterministic.  "
	      "%d moves.\n", pl_nmoves(&game));
      exit(1);
    }
    printf("pente: Timed game was %d moves long.\n", pl_nmoves(&game));
    exit(0);
  }
  
  for (tnum = 0, defnum = 0;  disp[tnum].init;  ++tnum)  {
    if (clp_getBool(pe_clp, disp[tnum].name))
      ++defnum;
  }
  
  pe_rnd = rnd_create(time(NULL) ^ getpid());

  comp_spawn(&from_c, &to_c);
  pl_init(&game);
  
  switch(defnum)  {
  case 0:
    for (tnum = 0;  disp[tnum].init;  ++tnum)  {
      resp = disp[tnum].init(play_level, BD_MAXPLAYERS, &game);
      if (resp != PE_CANTPLAY)  {
	play_pente(play_level, disp[tnum].init, disp[tnum].draw,
		   disp[tnum].selectmove, disp[tnum].show_think, &game, resp,
		   from_c, to_c);
	break;
      } else  {
	fprintf(stderr, "Pente: Could not start up %s display.\n"
		"       Other displays are available with these commands:\n",
		disp[tnum].name);
	for (++tnum;  disp[tnum].name != NULL;  ++tnum)  {
	  fprintf(stderr, "         pente -%s\n",
		  disp[tnum].name);
	}
	exit(1);
      }
    }
    if (!disp[tnum].init)  {
      fprintf(stderr, "pente: Error - Init failed.\n");
      exit(1);
    }
    break;
  case 1:
    for (tnum = 0;  !clp_getBool(pe_clp, disp[tnum].name);
	 ++tnum);
    resp = disp[tnum].init(play_level, BD_MAXPLAYERS, &game);
    if (resp != PE_CANTPLAY)  {
      play_pente(play_level, disp[tnum].init, disp[tnum].draw,
		 disp[tnum].selectmove, disp[tnum].show_think, &game, resp,
		 from_c, to_c);
    } else  {
      fprintf(stderr, "pente: Display type \"%s\" failed.\n",
	      disp[tnum].name);
      exit(1);
    }
    break;
  default:
    fprintf(stderr, "pente: At most one display type can be specified.\n");
    exit(1);
  }
  
  exit(0);
}


static void  play_pente(int level[BD_MAXPLAYERS],
			uint init(int level[BD_MAXPLAYERS], uint winner,
				  pl_t *game),
			void draw(pl_t *game, bool forced),
			bd_loc_t selectmove(pl_t *game, int compfd),
			void show_think(bd_loc_t pos, uint player),
			pl_t *game, bd_loc_t firstmove,
			int  from_c, int to_c)  {
  bd_loc_t  loc = firstmove;
  bool  game_over = FALSE, paused = FALSE;
  uint  turn_num, old_cap, player = pl_player(game);
  static char  gameStr[4*(19*19+20)];
  
  for (;;)  {
    switch(loc)  {
    case PE_CONTINUE:
      paused = FALSE;
      break;
    case PE_UNDO:
      player ^= 1;
      paused = TRUE;
      game_over = FALSE;
      pl_undo(game);
      draw(game, TRUE);
      break;
    case PE_REDO:
      paused = TRUE;
      old_cap = pl_board(game)->captures[player];
      game_over = pl_redo(game);
      draw(game, TRUE);
      while (old_cap < pl_board(game)->captures[player])  {
	++old_cap;
      }
      player ^= 1;
      break;
    case PE_PAUSE:
      paused = TRUE;
      break;
    case PE_RESTART:
      pl_init(game);
      player = pl_player(game);
      turn_num = pl_turn(game);
      paused = FALSE;
      game_over = FALSE;
      break;
    default:
      if (paused)
	paused = FALSE;
      assert(pl_movep(game, loc));
      if (!pl_movep(game, loc))  {
	fprintf(stderr, "pente: Illegal move reported.\n");
	exit(1);
      }
      old_cap = pl_board(game)->captures[player];
      game_over = pl_move(game, loc);
      pl_game_str(game, gameStr);
      clp_setStr(pe_clp, "board", gameStr);
      while (old_cap < pl_board(game)->captures[player])  {
	++old_cap;
      }
      player ^= 1;
      break;
    }
    draw(game, (game_over || paused ||
		((level[player^1] == 0) || (level[player] != 0))));
    if (game_over || paused)
      loc = init(level, player^1, game);
    else  {
      if (level[player] < 1)  {
	loc = selectmove(game, from_c);
      } else  {
	comp_start(game, level[player], to_c);
	do  {
	  loc = selectmove(game, from_c);
	  if (loc == PE_COMP)  {
	    loc = comp_getmove(from_c, show_think, player);
	  } else  {
	    comp_reset(&from_c, &to_c);
	  }
	} while (loc == PE_COMP);
      }
    }
  }
}


void  pe_output_vars(void)  {
  clp_wFile(pe_clp, "~/.pente.ad", "pente");
}

