/* options.h
 */
#ifndef _OPTIONS_H
#define _OPTIONS_H

#include "osl/game_playing/searchPlayer.h"
#include "osl/oslConfig.h"
#include <boost/program_options.hpp>
namespace po = boost::program_options;

struct SearchOptions
{
  int initial_limit, deepening_step, search_limit;
  int table_size, check_table_size;
  int table_record_limit;
  uint64_t node_limit, node_count_hard_limit;
  int draw_coef;
#ifdef OSL_SMP
  int num_cpus;
#endif
  double memory_use_percent;
  bool ignore_node_counts;
  po::options_description options;

  SearchOptions()
    : options("Search options")
  {
#ifdef OSL_SMP
#  ifdef OSL_NCPUS
    const int num_cpus_default = OSL_NCPUS;
#  else
    const int num_cpus_default = std::thread::hardware_concurrency();
#  endif
#endif
    options.add_options()
      ("initial-limit,i",
       po::value<int>(&initial_limit)->default_value(400),
       "initial limit of iterative deepening")
      ("deepening-step",	// 's' is reserved for sente in MatchOptions
       po::value<int>(&deepening_step)->default_value(200),
       "step of iterative deepening")
      ("search-limit,l",
       po::value<int>(&search_limit)->default_value(1400),
       "maximum limit of search")
      ("record-limit,L",
       po::value<int>(&table_record_limit)->default_value(200),
       "minimum depth left of storing record")
      ("node-limit,n",
       po::value<uint64_t>(&node_limit)->default_value(16000000),
       "maximum number of search nodes (tested at each iteration)")
      ("node-count-hard-limit",
       po::value<uint64_t>(&node_count_hard_limit)->default_value(std::numeric_limits<uint64_t>::max()),
       "maximum number of search nodes (tested at every nodes)")
      ("table-size,t",
       po::value<int>(&table_size)->default_value(4800000), // for 8GB
       "number of entries in table")
      ("check-table-size",
       po::value<int>(&check_table_size)->default_value(-1),
       "ignored.  just for backward compatibility.")
      ("ignore-node-counts",
       po::value<bool>(&ignore_node_counts)->default_value(osl::OslConfig::isMemoryLimitEffective()),
       "ignore {node,checkmate} counts and table size and continue search until memory exhausts")
      ("draw-coef",
       po::value<int>(&draw_coef)->default_value(-1),
       "preference for draw (prefer if positive, escape if negative), relative to pawn value")
#ifdef OSL_SMP
      ("num-cpus,N",
       po::value<int>(&num_cpus)->default_value(num_cpus_default),
       "num cpus for parallel search")
#endif
      ("memory-use-percent",
       po::value<double>(&memory_use_percent)->default_value(100.0),
       "percentage for memory use (normally 100)")
      ;
  }
  void setConfig(osl::game_playing::SearchPlayer& player)
  {
    player.setDepthLimit(search_limit, initial_limit, deepening_step);
    player.setNodeCountHardLimit(node_count_hard_limit);
    if (ignore_node_counts) 
    {
      player.setTableLimit(std::numeric_limits<size_t>::max(), 
			   table_record_limit);
      player.setNodeLimit(std::numeric_limits<size_t>::max());
    }
    else
    {
      player.setNodeLimit(node_limit);
      player.setTableLimit(table_size, table_record_limit);
    }
    player.setDrawCoef(draw_coef);
  }
};

struct EvalOptions
{
  std::string eval_type;
  std::string eval_filename;
  std::string progress_filename;
  std::string usi_proxy;
  unsigned int eval_random;

  po::options_description options;
  EvalOptions() : options("Evaluation options")
  {
    options.add_options()
      ("eval-type,e",
       po::value<std::string>(&eval_type)->default_value(std::string("test")),
       "evaluation function (test or progress")
      ("eval-data",
       po::value<std::string>(&eval_filename)->default_value(""),
       "filename for evaluation function")
      ("progress-file",
       po::value<std::string>(&progress_filename)->default_value(""),
       "filename for NewProgress data")
#ifndef MINIMAL
      ("eval-randomness",
       po::value<unsigned int>(&eval_random)->default_value(0),
       "add random value generated by normal distribution of a given standard deviation, to evaluation values")
#endif
      ("usi-proxy", po::value<std::string>(&usi_proxy)->default_value(""),
       "path to gpsfish")
      ;
  }
};

struct OpeningOptions
{
  bool use_opening_book;
  std::string kisen_filename;
  std::string csa_filename;
  std::string normal_book_filename;
  int kisen_id;
  unsigned int book_moves;
  unsigned int determinate_level;
  int weight_coef_for_the_initial_move;
  int weight_coef;

  po::options_description options;
  OpeningOptions() : options("Opening options", 78)
  {
    options.add_options()
      ("kisen-file,K", po::value<std::string>(&kisen_filename)->default_value(osl::OslConfig::home() + "/data/kisen/01.kif"),
       "kisen file for opening book (unused unless kisen-id is specified)")
      ("kisen-id,k", po::value<int>(&kisen_id)->default_value(-1),
       "id of kisen record for opening book (enabled if positive)")
      ("csa-file", po::value<std::string>(&csa_filename)->default_value(""),
       "csa file for opening book")
      ("opening-file", po::value<std::string>(&normal_book_filename)->default_value(""),
       "file for opening book (used for normal games)")
      ("no-opening-book", "do not use opening book")
      ("book-moves,m", po::value<unsigned int>(&book_moves)->default_value(30),
       "maximum depth traced in book")
      ("enable-determinate-book", po::value<unsigned int>(&determinate_level)->default_value(0),
       "if a specified value (N) > 0, a move is selected among the top N weighted moves with a probability propotional to each move's weight (default 0 [disabled])")
      ("weight-initial-coef", po::value<int>(&weight_coef_for_the_initial_move)->default_value(16),
       "weight coefficient to the top moves, which is used to filter moves only for the initial one.")
      ("weight-coef", po::value<int>(&weight_coef)->default_value(10),
       "weight coefficient to the top moves, which is used to filter moves except the initial one.")
      ;
  }
  void update(const po::variables_map& vm)
  {
    use_opening_book = (! vm.count("no-opening-book"));
  }
};

struct MatchOptions
{
  std::string black_name, white_name;
  unsigned int time_left, byoyomi;
  std::string output_filename, initial_csa_file;
  bool csa_mode, use_alpha_beta, random_player, sente, think_opponent_time;
  bool send_move_with_comment, save_pv;

  po::options_description options;
  MatchOptions(const std::string& program_name) : options("Match options")
  {
    options.add_options()
      ("black,b", po::value<std::string>(&black_name)->default_value("sente"),
       "name of black player")
      ("white,w", po::value<std::string>(&white_name)->default_value("gote"),
       "name of white player")
      ("time-left,T", po::value<unsigned int>(&time_left)->default_value(1500),
       "total time left")
      ("byoyomi,B", po::value<unsigned int>(&byoyomi)->default_value(0),
       "seconds for each move (byoyomi)")
      ("output-filename,o", po::value<std::string>(&output_filename)->default_value(program_name + ".csa"),
       "filename of game record")
      ("initial-position,f", po::value<std::string>(&initial_csa_file)->default_value(""),
       "csa file of initial position")
      ("no-table-reuse,R", "no effect (preserved only for backward compatibility)")
      ("save-pv", po::value<bool>(&save_pv)->default_value(true), "use pv of previous search results")
      ("csa,c", "use csa protocol, instead of gnushogi compatible mode")
      ("alphabeta,a", "use alphabeta, instead of mtd(f)")
      ("random,r", "random playing")
      ("sente,s", "computer play first (second if not specified)")
      ("opponent-time,P", "search while the opponent is thinking")
      ("move-with-comment,x", "send move with comment (this is a private extension to CSA protocol)")
      ;
  }
  void update(const po::variables_map& vm)
  {
    csa_mode = vm.count("csa");
    use_alpha_beta = vm.count("alphabeta");
    random_player = vm.count("random");
    sente = vm.count("sente");
    think_opponent_time = vm.count("opponent-time");
    send_move_with_comment = vm.count("move-with-comment");
  }
};

struct AnalyzeOptions
{
  int num_print_bestmoves;
  int log_margin;
  std::string log_filename;
  int dot_limit;
  std::string dot_filename;
  po::options_description options;

  AnalyzeOptions(const std::string& program_name)
    : dot_filename(program_name + ".dot"), options("Analyze options")
  {
    options.add_options()
      ("num-analyze-moves,a", 
       po::value<int>(&num_print_bestmoves)->default_value(3),
       "show other candidates after search")
      ("log-margin,d", 
       po::value<int>(&log_margin)->default_value(0),
       "maximum depth of logging")
      ("dot-limit,D", 
       po::value<int>(&dot_limit)->default_value(0),
       "maximum depth of dot output")
      ("log-filename,o",
       po::value<std::string>(&log_filename)->default_value(program_name + ".log"),
       "logfilename")
      ;
  }
};

#endif /* _OPTIONS_H */
// ;;; Local Variables:
// ;;; mode:c++
// ;;; c-basic-offset:2
// ;;; End:
