/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2006  by Diether Knof and Borg Enders
 *
 *   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 can find this license in the file 'gpl.txt'.
 *
 *   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
 *
 *  Contact:
 *    Diether Knof dknof@gmx.de
 *    Borg Enders  borg@borgsoft.de
 *
 *********************************************************************/ 

#include "constants.h"

#include "aiconfig.h"

#include "../party/party.h"
#include "../party/rule.h"
#include "../misc/setting.h"


unsigned const AITYPE::NUMBER = (AITYPE::AITYPE_FIRST
				 - AITYPE::AITYPE_LAST + 1);

unsigned const Aiconfig::HEURISTIC_NUMBER = (Aiconfig::HEURISTIC_LAST
					     - Aiconfig::HEURISTIC_FIRST + 1);
unsigned const Aiconfig::BOOL_NUMBER = (Aiconfig::BOOL_LAST
					- Aiconfig::BOOL_FIRST + 1);
unsigned const Aiconfig::UNSIGNED_NUMBER = (Aiconfig::UNSIGNED_LAST
					    - Aiconfig::UNSIGNED_FIRST + 1);
unsigned const Aiconfig::CARD_NUMBER = (Aiconfig::CARD_LAST
					- Aiconfig::CARD_FIRST + 1);

/**
 ** -> result
 **
 ** @param     difficulty   difficulty of the returned ai
 **
 ** @return    the preset aiconfig of the given difficulty
 **
 ** @author    Diether Knof
 **
 ** @version   0.7.2
 **/
Aiconfig const&
Aiconfig::preset(Difficulty const difficulty)
{
  DEBUG_ASSERTION( (difficulty != CUSTOM),
		  "Aiconfig::preset(difficulty)\n"
		  "  difficulty is 'CUSTOM'");

  // just to get a warning when a new difficulty has been added
  switch (difficulty) {
  case CUSTOM:
  case NOVICE:
  case STANDARD_DEFENSIVE:
  case STANDARD_OFFENSIVE:
  case PROFI:
  case PROFI_UNFAIR:
    break;
  } // switch (difficulty)

  static vector<Aiconfig const*> aiconfigs;
  if (aiconfigs.empty()) {
    { // CUSTOM
      aiconfigs.push_back(NULL);
    } // CUSTOM
    { // NOVICE
#include "aiconfigs.preset/novice"
      istringstream istr(novice);
      aiconfigs.push_back(new Aiconfig(istr));
    } // NOVICE
    { // STANDARD_DEFENSIVE
#include "aiconfigs.preset/standard.defensive"
      istringstream istr(standard_defensive);
      aiconfigs.push_back(new Aiconfig(istr));
    } // STANDARD_DEFENSIVE
    { // STANDARD_OFFENSIVE
#include "aiconfigs.preset/standard.offensive"
      istringstream istr(standard_offensive);
      aiconfigs.push_back(new Aiconfig(istr));
    } // STANDARD_OFFENSIVE
    { // PROFI
#include "aiconfigs.preset/profi"
      istringstream istr(profi);
      aiconfigs.push_back(new Aiconfig(istr));
    } // PROFI
    { // PROFI_UNFAIR
#include "aiconfigs.preset/profi.unfair"
      istringstream istr(profi_unfair);
      aiconfigs.push_back(new Aiconfig(istr));
    } // PROFI_UNFAIR
  } // if (aiconfigs.empty())

  return *aiconfigs[difficulty];
} // static Aiconfig const& Aiconfig::preset(Difficulty difficulty);

/**
 ** -> return
 **
 ** @param	h	the heuristic type
 **
 ** @return	whether 'h' is a general heuristic
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
Aiconfig::is_general(Aiconfig::Heuristic const h)
{
  return (   !Aiconfig::is_solo(h)
	  && !Aiconfig::is_poverty(h)
	  && (h != Aiconfig::NO_HEURISTIC)
	  && (h != Aiconfig::BUG_REPORT)
	  && (h != Aiconfig::ONLY_VALID_CARD)
	  && (h != Aiconfig::CHOOSE_BEST_CARD));
} // static bool Aiconfig::is_general(Aiconfig::Heuristic const h)

/**
 ** -> return
 **
 ** @param	h	the heuristic type
 **
 ** @return	whether 'h' was made for a poverty
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
Aiconfig::is_poverty(Aiconfig::Heuristic const h)
{
  return (   (h == Aiconfig::CHOOSE_PFUND_POVERTY)
	  || (h == Aiconfig::POVERTY_PLAYER_PLAY_PFUND) );
} // static bool Aiconfig::is_poverty(Aiconfig::Heuristic const h)

/**
 ** -> return
 **
 ** @param	h	the heuristic type
 **
 ** @return	whether 'h' was made for a solo
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
Aiconfig::is_solo(Aiconfig::Heuristic const h)
{
  return (Aiconfig::is_solo_color(h)
	  || Aiconfig::is_solo_picture(h)
	  || Aiconfig::is_solo_meatless(h));
} // static bool Aiconfig::is_solo(Aiconfig::Heuristic const h)

/**
 ** -> return
 **
 ** @param	h	the heuristic type
 **
 ** @return	whether 'h' was made for a color solo
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
Aiconfig::is_solo_color(Aiconfig::Heuristic const h)
{
  switch (h) {
  case COLOR_CHOOSE_ACE:
  case COLOR_BEST_WINNING:
  case COLOR_JAB_FOR_ACE:
  case COLOR_LOW_HIGH:
    return true;
  default:
    return false;
  }; // switch(h)
#ifdef WORKAROUND
  // g++ 4.0 -O3
  return false;
#endif
} // static bool Aiconfig::is_solo_color(Aiconfig::Heuristic const h)

/**
 ** -> return
 **
 ** @param	h	the heuristic type
 **
 ** @return	whether 'h' was made for a picture solo
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
Aiconfig::is_solo_picture(Aiconfig::Heuristic const h)
{
  switch (h) {
  case PICTURE_SECONDBESTTRUMP:
    return true;
  default:
    return false;
  }; // switch(h)
#ifdef WORKAROUND
  // g++ 4.0 -O3
  return false;
#endif
} // static bool Aiconfig::is_solo_picture(Aiconfig::Heuristic const h)

/**
 ** -> return
 **
 ** @param	h	the heuristic type
 **
 ** @return	whether 'h' was made for a meatless solo
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
Aiconfig::is_solo_meatless(Aiconfig::Heuristic const h)
{
  switch (h) {
  case MEATLESS_PLAYHIGHESTCOLOR:
    return true;
  default:
    return false;
  }; // switch(h)
#ifdef WORKAROUND
  // g++ 4.0 -O3
  return false;
#endif
} // static bool Aiconfig::is_solo_meatless(Aiconfig::Heuristic const h)


/**
 ** constructor
 **
 ** @param	-
 **
 ** @result	-
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.5.4
 **/
Aiconfig::Aiconfig() :
  aitype_p(::party.rule()(Rule::MAX_NUMBER_OF_TRICKS_IN_GAME)),
  future_limit_p(::party.rule()(Rule::MAX_NUMBER_OF_TRICKS_IN_GAME)),
  heuristic_p(HEURISTIC_NUMBER),
  bool_p(BOOL_NUMBER),
  unsigned_p(UNSIGNED_NUMBER),
  card_p(CARD_NUMBER)
{
  this->reset_to_hardcoded();

  return ;
} // Aiconfig::Aiconfig()

/**
 ** constructor
 **
 ** @param	istr	stream with the infos
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.2
 **/
Aiconfig::Aiconfig(istream& istr) :
  aitype_p(::party.rule()(Rule::MAX_NUMBER_OF_TRICKS_IN_GAME)),
  future_limit_p(::party.rule()(Rule::MAX_NUMBER_OF_TRICKS_IN_GAME)),
  heuristic_p(HEURISTIC_NUMBER),
  bool_p(BOOL_NUMBER),
  unsigned_p(UNSIGNED_NUMBER),
  card_p(CARD_NUMBER)
{
  this->load(istr);

  return ;
} // Aiconfig::Aiconfig(istream& istr)


/**
 ** copy constructor
 **
 ** @param	aiconfig   aiconfig to copy
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.4.2
 **/
Aiconfig::Aiconfig(Aiconfig const& aiconfig) :
  aitype_p(aiconfig.aitype_p),
  future_limit_p(aiconfig.future_limit_p),
  heuristic_p(aiconfig.heuristic_p),
  bool_p(aiconfig.bool_p),
  unsigned_p(aiconfig.unsigned_p),
  card_p(aiconfig.card_p)
{
  return ;
} // Aiconfig::Aiconfig(Aiconfig const& aiconfig) const

/**
 ** copy operator
 **
 ** @param	aiconfig   aiconfig to copy
 **
 ** @return	aiconfig
 **
 ** @author	Diether Knof
 **
 ** @version	0.4.2
 **/
Aiconfig&
Aiconfig::operator=(Aiconfig const& aiconfig)
{
  if (this == &aiconfig)
    return *this;

  for (unsigned t = 0; t < ::party.rule()(Rule::MAX_NUMBER_OF_TRICKS_IN_GAME);
       ++t) {
    this->set_aitype(t, aiconfig.aitype(t));
    this->set_future_limit(t, aiconfig.future_limit(t));
  }
  for (unsigned i = HEURISTIC_FIRST; i <= HEURISTIC_LAST; i++)
    this->set(Heuristic(i), aiconfig.value(Heuristic(i)));
  for (unsigned i = BOOL_FIRST; i <= BOOL_LAST; i++)
    this->set(TypeBool(i), aiconfig.value(TypeBool(i)));
  for (unsigned i = UNSIGNED_FIRST; i <= UNSIGNED_LAST; i++)
    this->set(TypeUnsigned(i), aiconfig.value(TypeUnsigned(i)));
  for (unsigned i = CARD_FIRST; i <= CARD_LAST; i++)
    this->set(TypeCard(i), aiconfig.value(TypeCard(i)));

  return *this;
} // Aiconfig& Aiconfig::operator=(Aiconfig const& aiconfig) const

/**
 ** destructor
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.4.2
 **/
Aiconfig::~Aiconfig()
{
  return ;
} // Aiconfig::~Aiconfig() const

#ifndef OUTDATED
// 0.7.2
/**
 ** resets the aiconfig to the hardcoded
 **
 ** @param	-
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **/
void
Aiconfig::reset_to_hardcoded()
{
  this->set_to_difficulty(STANDARD_DEFENSIVE);
  return ;

#ifndef OUTDATED
  this->set(TRUSTING,           	true);
  this->set(FAIRPLAYHANDS,      	true);
  this->set(FAIRPLAYTEAMS,		true);

  this->set(PLAY_TO_MARRY,		true);     
  this->set(CHOOSE_ACE,			true);     
  this->set(CHOOSE_FOR_COLOR_TRICK,	true);
  this->set(RETRY_COLOR,		true);
  this->set(SERVE_COLOR_TRICK,		true);
  this->set(SERVE_TRUMP_TRICK,		true);
  this->set(CHOOSE_PFUND,		true);
  this->set(CHOOSE_PFUND_POVERTY,	true);
  this->set(JAB_FOR_ACE,		true);
  this->set(CREATE_FEHL,		true);
  this->set(BEST_WINNING,		true);
  this->set(LOW_HIGH,			true);
  this->set(PLAY_FOR_TEAM,		true);
  this->set(JAB_FOX,			true);
  this->set(TRY_FOR_DOPPELKOPF,		true);
  this->set(DRAW_TRUMP,			true);
  this->set(GET_TRICK_FOR_ANNOUNCEMENT,			true);


  this->set(POVERTY_PLAYER_PLAY_PFUND,	true);
  this->set(MEATLESS_PLAYHIGHESTCOLOR,	true);
  this->set(PICTURE_SECONDBESTTRUMP,	true);

  this->set(COLOR_CHOOSE_ACE,		true);
  this->set(COLOR_BEST_WINNING,		true);
  this->set(COLOR_JAB_FOR_ACE,		true);
  this->set(COLOR_LOW_HIGH,		true);
  this->set(FEHLCREATIONONFIRSTCARD,	true);

  this->set(REMEMBERTRICKS,     ::party.rule()(Rule::MAX_NUMBER_OF_TRICKS_IN_GAME));
  this->set(LIMIT_THROW_FEHL,    8);
  this->set(LIMITQUEEN,         10);
  this->set(LIMITDOLLE,         19);

  this->set(LASTFEHLCREATION,    4);
  if (::party.rule()(Rule::WITH_NINES))
    this->set(LASTHEURISTICTRICK,  9);
  else
    this->set(LASTHEURISTICTRICK,  7);
  this->set(FIRST_TRICK_FOR_TRUMP_POINTS_OPTIMIZATION,  5);

  this->set(ANNOUNCELIMIT,      10);
  this->set(ANNOUNCELIMITDEC,    1);
  this->set(ANNOUNCECONFIG,      1);

  this->set(ANNOUNCELIMITREPLY,       9);
  this->set(ANNOUNCECONFIGREPLY,      1);


  this->set(TAKEPOVERTY,        13);

  this->set(SINGLESOLO,         18);
  this->set(DOUBLESOLO,         50);
  this->set(TRIPLESOLO,         100);
  this->set(COLORSOLO,          28);
  this->set(MEATLESS,           26);

  this->set(FUTURE_LIMIT,       10000);

  this->set(LIMITTHROWING,      Card(Card::DIAMOND,Card::QUEEN));
  this->set(LIMITHIGH,          Card(Card::HEART,Card::QUEEN));

  this->set(TRUMPLIMIT_SOLOCOLOR,     Card(Card::DIAMOND,Card::QUEEN));
  this->set(TRUMPLIMIT_SOLOJACK,      Card(Card::DIAMOND,Card::JACK));
  this->set(TRUMPLIMIT_SOLOQUEEN,     Card(Card::DIAMOND,Card::QUEEN));
  this->set(TRUMPLIMIT_SOLOKING,      Card(Card::DIAMOND,Card::KING));
  this->set(TRUMPLIMIT_SOLOJACKKING,  Card(Card::CLUB,Card::JACK));
  this->set(TRUMPLIMIT_SOLOJACKQUEEN, Card(Card::CLUB,Card::JACK));
  this->set(TRUMPLIMIT_SOLOQUEENKING, Card(Card::CLUB,Card::QUEEN));
  this->set(TRUMPLIMIT_SOLOKOEHLER,   Card(Card::CLUB,Card::QUEEN));
  this->set(TRUMPLIMIT_MEATLESS,      Card(Card::DIAMOND,Card::TEN));
  this->set(TRUMPLIMIT_NORMAL,        Card(Card::HEART,Card::QUEEN));

  this->set(LOWEST_TRUMPLIMIT_SOLOCOLOR,     Card(Card::HEART,Card::JACK));
  this->set(LOWEST_TRUMPLIMIT_SOLOJACK,      Card(Card::HEART,Card::JACK));
  this->set(LOWEST_TRUMPLIMIT_SOLOQUEEN,     Card(Card::HEART,Card::QUEEN));
  this->set(LOWEST_TRUMPLIMIT_SOLOKING,      Card(Card::HEART,Card::KING));
  this->set(LOWEST_TRUMPLIMIT_SOLOJACKKING,  Card(Card::SPADE,Card::JACK));
  this->set(LOWEST_TRUMPLIMIT_SOLOJACKQUEEN, Card(Card::SPADE,Card::JACK));
  this->set(LOWEST_TRUMPLIMIT_SOLOQUEENKING, Card(Card::SPADE,Card::QUEEN));
  this->set(LOWEST_TRUMPLIMIT_SOLOKOEHLER,   Card(Card::SPADE,Card::QUEEN));
  this->set(LOWEST_TRUMPLIMIT_MEATLESS,      Card(Card::DIAMOND,Card::QUEEN));
  this->set(LOWEST_TRUMPLIMIT_NORMAL,        Card(Card::DIAMOND,Card::JACK));

  DEBUG_ASSERTION((::party.rule()(Rule::MAX_NUMBER_OF_TRICKS_IN_GAME)
		   == 12),
		  "Aiconfig::reset_to_hardcoded():\n"
		  "  maximal number of tricks is not 12 but "
		  << ::party.rule()(Rule::MAX_NUMBER_OF_TRICKS_IN_GAME));

  this->set_aitype      ( 0, AITYPE::VIRTUAL_GAMES);
  this->set_future_limit( 0, 10000);
  this->set_aitype      ( 1, this->aitype( 0));
  this->set_future_limit( 1, this->future_limit( 0));
  this->set_aitype      ( 2, this->aitype( 1));
  this->set_future_limit( 2, this->future_limit( 1));
  this->set_aitype      ( 3, this->aitype( 2));
  this->set_future_limit( 3, this->future_limit( 2));
  //this->set_aitype      ( 4, AITYPE::MONTE_CARLO);
  //this->set_future_limit( 4, 2500);
  this->set_aitype      ( 4, this->aitype( 3));
  this->set_future_limit( 4, this->future_limit( 3));
  this->set_aitype      ( 5, this->aitype( 4));
  this->set_future_limit( 5, this->future_limit( 4));
  this->set_aitype      ( 6, this->aitype( 4));
  this->set_future_limit( 6, this->future_limit( 5));
  this->set_aitype      ( 7, this->aitype( 6));
  this->set_future_limit( 7, this->future_limit( 6));
  if (!::party.rule()(Rule::WITH_NINES)) {
    this->set_aitype      ( 7, AITYPE::GAMETREE);
    this->set_future_limit( 7, 10000);
  }
  this->set_aitype      ( 8, AITYPE::GAMETREE);
  this->set_future_limit( 8, 10000);
  this->set_aitype      ( 9, this->aitype( 8));
  this->set_future_limit( 9, this->future_limit( 8));
  this->set_aitype      (10, this->aitype( 9));
  this->set_future_limit(10, this->future_limit(10));
  this->set_aitype      (11, this->aitype(10));
  this->set_future_limit(11, this->future_limit(11));
#endif // #ifdef OUTDATED

  return ;
} // void Aiconfig::reset_to_hardcoded()
#endif // #ifdef OUTDATED

/**
 ** sets the aiconfig to the preset difficulty
 **
 ** @param	difficulty   difficulty to set to
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
void
Aiconfig::set_to_difficulty(Difficulty const difficulty)
{
  if (difficulty == CUSTOM)
    return ;

  Aiconfig const& preset = Aiconfig::preset(difficulty);

  this->aitype_p       = preset.aitype_p;
  this->future_limit_p = preset.future_limit_p;
  this->heuristic_p    = preset.heuristic_p;
  this->bool_p         = preset.bool_p;
  this->unsigned_p     = preset.unsigned_p;
  this->card_p         = preset.card_p;

  return ;
} // void Aiconfig::set_to_difficulty(Difficulty const difficulty)


/**
 ** comparison with the aiconfig
 **
 ** @param	aiconfig   aiconfig to compare with
 **
 ** @return	whether aiconfig has the same values as the argument
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
bool
Aiconfig::equal(Aiconfig const& aiconfig) const
{
  return (   (this->aitype_p       == aiconfig.aitype_p)
	  && (this->future_limit_p == aiconfig.future_limit_p)
	  && (this->heuristic_p    == aiconfig.heuristic_p)
	  && (this->bool_p         == aiconfig.bool_p)
	  && (this->unsigned_p     == aiconfig.unsigned_p)
	  && (this->card_p         == aiconfig.card_p) );
} // bool Aiconfig::equal(Aiconfig aiconfig) const

/**
 ** -> result
 ** the result is taken by comparison, so this is slow!
 **
 ** @param	-
 **
 ** @return	the difficulty of the aiconfig
 **             'CUSTOM' if it does not match a predefined difficulty
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
Aiconfig::Difficulty
Aiconfig::difficulty() const
{
  for (int d = CUSTOM + 1; d <= DIFFICULTY_LAST; ++d)
    if (this->equal(Aiconfig::preset(static_cast<Difficulty>(d))))
      return static_cast<Difficulty>(d);

  return CUSTOM;
} // Aiconfig::Difficulty Aiconfig::difficulty() const

/**
 ** -> result
 **
 ** @param	trickno		trick number
 **
 ** @return	the aitype for trick 'trickno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **/
AiType const&
Aiconfig::aitype(unsigned const trickno) const
{
  return this->aitype_p[trickno];
} // AiType const& Aiconfig::aitye(unsigned const trickno) const;

/**
 ** sets the aitype for trick 'trickno'
 **
 ** @param	trickno		trick number
 ** @param	aitype		new aitype
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **/
void
Aiconfig::set_aitype(unsigned const trickno, AiType const& aitype)
{
  this->aitype_p[trickno] = aitype;

  return ;
} // void Aiconfig::set_aitype(unsigned const trickno, AiType const& aitype) const

/**
 ** sets the aitype for all tricks
 **
 ** @param	aitype		new aitype
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **/
void
Aiconfig::set_aitype_for_all_tricks(AiType const& aitype)
{
  for (unsigned i = 0; i < this->aitype_p.size(); ++i)
    this->set_aitype(i, aitype);

  return ;
} // void Aiconfig::set_aitype_for_all_tricks(AiType const& aitype) const

/**
 ** -> result
 **
 ** @param	trickno		trick number
 **
 ** @return	the future limit for trick 'trickno'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **/
unsigned const 
Aiconfig::future_limit(unsigned const trickno) const
{
  return this->future_limit_p[trickno];
} // unsigned const Aiconfig::aitye(unsigned const trickno) const;

/**
 ** sets the future limit for trick 'trickno'
 **
 ** @param	trickno		trick number
 ** @param	future_limit	new future limit
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **/
void
Aiconfig::set_future_limit(unsigned const trickno, unsigned const future_limit)
{
  this->future_limit_p[trickno] = future_limit;

  return ;
} // void Aiconfig::set_future_limit(unsigned const trickno, unsigned const future_limit) const

/**
 ** sets the future limit for all tricks
 **
 ** @param	future_limit		new future limit
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.7
 **/
void
Aiconfig::set_future_limit_for_all_tricks(unsigned const future_limit)
{
  for (unsigned i = 0; i < this->future_limit_p.size(); ++i)
    this->set_future_limit(i, future_limit);

  return ;
} // void Aiconfig::set_future_limit_for_all_tricks(unsigned const future_limit) const

/**
 ** -> result
 **
 ** @param	type	the heuristic type
 **
 ** @return	the value of the aiconfig 'type'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
Aiconfig::value(Heuristic const type) const
{
  return this->heuristic_p[type - HEURISTIC_FIRST];
} // bool Aiconfig::value(Heuristic const type) const

/**
 ** -> result
 **
 ** @param	type	the bool type
 **
 ** @return	the value of the aiconfig 'type'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
Aiconfig::value(TypeBool const type) const
{
  return this->bool_p[type - BOOL_FIRST];
} // bool Aiconfig::value(TypeBool) const;

/**
 ** -> result
 **
 ** @param	type	the unsigned type
 **
 ** @return	the value of the aiconfig 'type'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
unsigned
Aiconfig::value(TypeUnsigned const type) const
{
  return this->unsigned_p[type - UNSIGNED_FIRST];
} // unsigned Aiconfig::value(TypeUnsigned) const;

/**
 ** -> result
 **
 ** @param	type	the card type
 **
 ** @return	the value of the aiconfig 'type'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
Card
Aiconfig::value(TypeCard const type) const
{
  return this->card_p[type - CARD_FIRST];
} // Card Aiconfig::value(TypeCard const type) const

/**
 ** -> result
 **
 ** @param     type   config type
 **
 ** @return    minimal value for the type
 **
 ** @author    Diether Knof
 **
 ** @version   0.7.2
 **/
unsigned
Aiconfig::min(TypeUnsigned const& type) const
{
  switch(type) {
  case REMEMBERTRICKS:
  case LASTFEHLCREATION:
  case LASTHEURISTICTRICK:
  case FIRST_TRICK_FOR_TRUMP_POINTS_OPTIMIZATION:
    return 0;

  case LIMIT_THROW_FEHL:
  case LIMITQUEEN:
  case LIMITDOLLE:
    return 0;

  case ANNOUNCELIMIT:
  case ANNOUNCELIMITDEC:
  case ANNOUNCECONFIG:
  case ANNOUNCELIMITREPLY:
  case ANNOUNCECONFIGREPLY:
    return 0;

  case TAKEPOVERTY:
    return 0;

  case SINGLESOLO:
  case DOUBLESOLO:
  case TRIPLESOLO:
  case COLORSOLO:
  case MEATLESS:
    return 0;

  case FUTURE_LIMIT:
    return 1;
  } // switch(type)

  return UINT_MAX;
} // unsigned Aiconfig::min(TypeUnsigned type) const

/**
 ** -> result
 **
 ** @param     type   config type
 **
 ** @return    maximum value for the type
 **
 ** @author    Diether Knof
 **
 ** @version   0.7.2
 **/
unsigned
Aiconfig::max(TypeUnsigned const& type) const
{
  switch(type) {
  case REMEMBERTRICKS:
  case LASTFEHLCREATION:
  case LASTHEURISTICTRICK:
  case FIRST_TRICK_FOR_TRUMP_POINTS_OPTIMIZATION:
    // when changing 'with nines' -> 'without nines' -> 'with nines'
    // it should keep '12'
    //return ::party.rule()(Rule::NUMBER_OF_TRICKS_IN_GAME);
    return ::party.rule()(Rule::MAX_NUMBER_OF_TRICKS_IN_GAME);

  case LIMIT_THROW_FEHL:
  case LIMITQUEEN:
  case LIMITDOLLE:
    return 33;

  case ANNOUNCELIMIT:
  case ANNOUNCELIMITDEC:
  case ANNOUNCECONFIG:
  case ANNOUNCELIMITREPLY:
  case ANNOUNCECONFIGREPLY:

    return 1000;

  case TAKEPOVERTY:
    return 1000;

  case SINGLESOLO:
  case DOUBLESOLO:
  case TRIPLESOLO:
  case COLORSOLO:
  case MEATLESS:
    return 1000;

  case FUTURE_LIMIT:
    return static_cast<unsigned const>(1e9);
  } // switch(type)

  return UINT_MAX;
} // unsigned Aiconfig::max(TypeUnsigned type) const


/**
 ** -> result
 **
 ** @param     type   config type
 **
 ** @return    the valid cards
 **
 ** @author    Diether Knof
 **
 ** @version   0.7.2
 **/
list<Card> const&
Aiconfig::valid_cards(TypeCard const type) const
{
  switch(type) {
  case LIMITTHROWING:
  case LIMITHIGH:

  case TRUMPLIMIT_NORMAL:
  case LOWEST_TRUMPLIMIT_NORMAL:
  case TRUMPLIMIT_SOLOCOLOR:
  case LOWEST_TRUMPLIMIT_SOLOCOLOR:
    {
      static list<Card> valid_cards;
      if (valid_cards.empty()) {
	valid_cards.push_back(Card(Card::CLUB,    Card::QUEEN));
	valid_cards.push_back(Card(Card::SPADE,   Card::QUEEN));
	valid_cards.push_back(Card(Card::HEART,   Card::QUEEN));
	valid_cards.push_back(Card(Card::DIAMOND, Card::QUEEN));
	valid_cards.push_back(Card(Card::CLUB,    Card::JACK));
	valid_cards.push_back(Card(Card::SPADE,   Card::JACK));
	valid_cards.push_back(Card(Card::HEART,   Card::JACK));
	valid_cards.push_back(Card(Card::DIAMOND, Card::JACK));
      } // if (valid.cards.empty())

      return valid_cards;
    }
  case TRUMPLIMIT_SOLOJACK:
  case LOWEST_TRUMPLIMIT_SOLOJACK:
    {
      static list<Card> valid_cards;
      if (valid_cards.empty()) {
	valid_cards.push_back(Card(Card::CLUB,    Card::JACK));
	valid_cards.push_back(Card(Card::SPADE,   Card::JACK));
	valid_cards.push_back(Card(Card::HEART,   Card::JACK));
	valid_cards.push_back(Card(Card::DIAMOND, Card::JACK));
      } // if (valid.cards.empty())

      return valid_cards;
    }
  case TRUMPLIMIT_SOLOQUEEN:
  case LOWEST_TRUMPLIMIT_SOLOQUEEN:
    {
      static list<Card> valid_cards;
      if (valid_cards.empty()) {
	valid_cards.push_back(Card(Card::CLUB,    Card::QUEEN));
	valid_cards.push_back(Card(Card::SPADE,   Card::QUEEN));
	valid_cards.push_back(Card(Card::HEART,   Card::QUEEN));
	valid_cards.push_back(Card(Card::DIAMOND, Card::QUEEN));
      } // if (valid.cards.empty())

      return valid_cards;
    }
  case TRUMPLIMIT_SOLOKING:
  case LOWEST_TRUMPLIMIT_SOLOKING:
    {
      static list<Card> valid_cards;
      if (valid_cards.empty()) {
	valid_cards.push_back(Card(Card::CLUB,    Card::KING));
	valid_cards.push_back(Card(Card::SPADE,   Card::KING));
	valid_cards.push_back(Card(Card::HEART,   Card::KING));
	valid_cards.push_back(Card(Card::DIAMOND, Card::KING));
      } // if (valid.cards.empty())

      return valid_cards;
    }
  case TRUMPLIMIT_SOLOJACKKING:
  case LOWEST_TRUMPLIMIT_SOLOJACKKING:
    {
      static list<Card> valid_cards;
      if (valid_cards.empty()) {
	valid_cards.push_back(Card(Card::CLUB,    Card::KING));
	valid_cards.push_back(Card(Card::SPADE,   Card::KING));
	valid_cards.push_back(Card(Card::HEART,   Card::KING));
	valid_cards.push_back(Card(Card::DIAMOND, Card::KING));
	valid_cards.push_back(Card(Card::CLUB,    Card::JACK));
	valid_cards.push_back(Card(Card::SPADE,   Card::JACK));
	valid_cards.push_back(Card(Card::HEART,   Card::JACK));
	valid_cards.push_back(Card(Card::DIAMOND, Card::JACK));
      } // if (valid.cards.empty())

      return valid_cards;
    }
  case TRUMPLIMIT_SOLOJACKQUEEN:
  case LOWEST_TRUMPLIMIT_SOLOJACKQUEEN:
    {
      static list<Card> valid_cards;
      if (valid_cards.empty()) {
	valid_cards.push_back(Card(Card::CLUB,    Card::QUEEN));
	valid_cards.push_back(Card(Card::SPADE,   Card::QUEEN));
	valid_cards.push_back(Card(Card::HEART,   Card::QUEEN));
	valid_cards.push_back(Card(Card::DIAMOND, Card::QUEEN));
	valid_cards.push_back(Card(Card::CLUB,    Card::JACK));
	valid_cards.push_back(Card(Card::SPADE,   Card::JACK));
	valid_cards.push_back(Card(Card::HEART,   Card::JACK));
	valid_cards.push_back(Card(Card::DIAMOND, Card::JACK));
      } // if (valid.cards.empty())

      return valid_cards;
    }
  case TRUMPLIMIT_SOLOQUEENKING:
  case LOWEST_TRUMPLIMIT_SOLOQUEENKING:
    {
      static list<Card> valid_cards;
      if (valid_cards.empty()) {
	valid_cards.push_back(Card(Card::CLUB,    Card::KING));
	valid_cards.push_back(Card(Card::SPADE,   Card::KING));
	valid_cards.push_back(Card(Card::HEART,   Card::KING));
	valid_cards.push_back(Card(Card::DIAMOND, Card::KING));
	valid_cards.push_back(Card(Card::CLUB,    Card::QUEEN));
	valid_cards.push_back(Card(Card::SPADE,   Card::QUEEN));
	valid_cards.push_back(Card(Card::HEART,   Card::QUEEN));
	valid_cards.push_back(Card(Card::DIAMOND, Card::QUEEN));
      } // if (valid.cards.empty())

      return valid_cards;
    }
  case TRUMPLIMIT_SOLOKOEHLER:
  case LOWEST_TRUMPLIMIT_SOLOKOEHLER:
    {
      static list<Card> valid_cards;
      if (valid_cards.empty()) {
	valid_cards.push_back(Card(Card::CLUB,    Card::KING));
	valid_cards.push_back(Card(Card::SPADE,   Card::KING));
	valid_cards.push_back(Card(Card::HEART,   Card::KING));
	valid_cards.push_back(Card(Card::DIAMOND, Card::KING));
	valid_cards.push_back(Card(Card::CLUB,    Card::QUEEN));
	valid_cards.push_back(Card(Card::SPADE,   Card::QUEEN));
	valid_cards.push_back(Card(Card::HEART,   Card::QUEEN));
	valid_cards.push_back(Card(Card::DIAMOND, Card::QUEEN));
	valid_cards.push_back(Card(Card::CLUB,    Card::JACK));
	valid_cards.push_back(Card(Card::SPADE,   Card::JACK));
	valid_cards.push_back(Card(Card::HEART,   Card::JACK));
	valid_cards.push_back(Card(Card::DIAMOND, Card::JACK));
      } // if (valid.cards.empty())

      return valid_cards;
    }
  case TRUMPLIMIT_MEATLESS:
  case LOWEST_TRUMPLIMIT_MEATLESS:
    {
      static list<Card> valid_cards;
      if (valid_cards.empty()) {
	valid_cards.push_back(Card(Card::DIAMOND, Card::ACE));
	valid_cards.push_back(Card(Card::DIAMOND, Card::TEN));
	valid_cards.push_back(Card(Card::DIAMOND, Card::KING));
	valid_cards.push_back(Card(Card::DIAMOND, Card::QUEEN));
	valid_cards.push_back(Card(Card::DIAMOND, Card::JACK));
	valid_cards.push_back(Card(Card::DIAMOND, Card::NINE));
      } // if (valid.cards.empty())

      return valid_cards;
    }
  } // switch(type)

  {
    static list<Card> valid_cards;
    return valid_cards;
  }
} // list<Card> const& Aiconfig::valid_cards(TypeCard const type) const

/**
 ** sets 'type' to 'value'
 **
 ** @param	type	Aiconfigtype
 ** @param	value	new value
 **
 ** @return	'true', if the type is valid, otherwise 'false'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
Aiconfig::set(string const& type, string const& value)
{
  unsigned i;

  for (i = Aiconfig::HEURISTIC_FIRST; i <= Aiconfig::HEURISTIC_LAST; i++) {
    if (type == name(Aiconfig::Heuristic(i))) {
      this->set(Aiconfig::Heuristic(i), value);
      return true;
    } // if (type == name(Aiconfig::Heuristic(i)))
  } // (i \in BOOL)
  for (i = Aiconfig::BOOL_FIRST; i <= Aiconfig::BOOL_LAST; i++) {
    if (type == name(Aiconfig::TypeBool(i))) {
      this->set(Aiconfig::TypeBool(i), value);
      return true;
    } // if (type == name(Aiconfig::TypeBool(i)))
  } // (i \in BOOL)
  for (i = Aiconfig::UNSIGNED_FIRST; i <= Aiconfig::UNSIGNED_LAST; i++) {
    if (type == name(Aiconfig::TypeUnsigned(i))) {
      this->set(Aiconfig::TypeUnsigned(i), value);
      return true;
    } // if (type == name(Aiconfig::TypeUnsigned(i)))
  } // (i \in UNSIGNED)
  for (i = Aiconfig::CARD_FIRST; i <= Aiconfig::CARD_LAST; i++) {
    if (type == name(Aiconfig::TypeCard(i))) {
      this->set(Aiconfig::TypeCard(i), value);
      return true;
    } // if (type == name(Aiconfig::TypeCard(i)))
  } // (i \in CARD)

  return false;
} // bool Aiconfig::set(string const& type, string const& value)

/**
 ** sets 'type' to 'value'
 **
 ** @param	type	Aiconfigtype
 ** @param	value	new value
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
void
Aiconfig::set(Heuristic const type, string const& value)
{
  if ((value != "true")
      && (value != "false")
      && (value != "yes")
      && (value != "no")
      && (value != "0")
      && (value != "1")) {
    cerr << "Aiconfig::set(TypeBool, value):\n"
					     << "illegal value '" << value << "' for '" << type << "', "
					       << "must be a boolean ('true' or 'false' or 'yes' or 'no' or '1' or '0'. "
					       << "Taking 'false'."
					       << endl;
  }
  this->set(type, (((value == "false")
		    || (value == "no")
		    || (value == "0"))
		   ? false :	true));

  return ;
} // void Aiconfig::set(Heuristic type, string const& value)

/**
 ** sets 'type' to 'value'
 **
 ** @param	type	Aiconfigtype
 ** @param	value	new value
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
void
Aiconfig::set(TypeBool const type, string const& value)
{
  if ((value != "true")
      && (value != "false")
      && (value != "yes")
      && (value != "no")
      && (value != "0")
      && (value != "1")) {
    cerr << "Aiconfig::set(TypeBool, value):\n"
					     << "illegal value '" << value << "' for '" << type << "', "
					       << "must be a boolean ('true' or 'false' or 'yes' or 'no' or '1' or '0'. "
					       << "Taking 'false'."
					       << endl;
  }
  this->set(type, (((value == "false")
		    || (value == "no")
		    || (value == "0"))
		   ? false :	true));

  return ;
} // void Aiconfig::set(const TypeBool type, string const& value)

/**
 ** sets 'type' to 'value'
 **
 ** @param	type	Aiconfigtype
 ** @param	value	new value
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
void
Aiconfig::set(TypeUnsigned const type, string const& value)
{
  char* end_ptr;
  unsigned number = strtoul(value.c_str(), &end_ptr, 0);
  if (*end_ptr != '\0') {
    cerr << "Aiconfig::set(TypeUnsigned, value):\n"
						 << "illegal value '" << value << "' for '" << type << "', "
						   << "must be a digit. "
						   << "Taking " << number << "."
						   << endl;
  }

  this->set(type, number);

  return ;
} // void Aiconfig::set(const TypeUnsigned type, string const& value)

/**
 ** sets 'type' to 'value'
 **
 ** @param	type	Aiconfigtype
 ** @param	value	new value
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
void
Aiconfig::set(TypeCard const type, string const& value)
{
  this->set(type, Card(value));

  return ;
} // void Aiconfig::set(const TypeCard type, string const& value)

/**
 ** sets the aiconfig 'type' to the value 'value'
 **
 ** @param	type	heuristic type
 ** @param	value	new value
 **
 ** @result	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
void
Aiconfig::set(Heuristic const type, bool const value)
{
  this->heuristic_p[type - HEURISTIC_FIRST] = value;

  return ;
} // void Aiconfig::set(Heuristic, bool);

/**
 ** sets the aiconfig 'type' to the value 'value'
 **
 ** @param	type	heuristic type
 ** @param	value	new value
 **
 ** @result	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
void
Aiconfig::set(TypeBool const type, bool const value)
{
  this->bool_p[type - BOOL_FIRST] = value;

  return ;
} // void Aiconfig::set(TypeBool, bool);

/**
 ** sets the aiconfig 'type' to the value 'value'
 **
 ** @param	type	heuristic type
 ** @param	value	new value
 **
 ** @result	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
void
Aiconfig::set(TypeUnsigned const type, unsigned const value)
{
  this->unsigned_p[type - UNSIGNED_FIRST] = value;

  return ;
} // void Aiconfig::set(TypeUnsigned, unsigned);

/**
 ** sets the aiconfig 'type' to the value 'value'
 **
 ** @param	type	heuristic type
 ** @param	value	new value
 **
 ** @result	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
void
Aiconfig::set(TypeCard const type, Card const value)
{
  this->card_p[type - CARD_FIRST] = value;

  return ;
} // void Aiconfig::set(TypeCard, card);

/**
 **
 ** @param	ostr	output stream
 **
 ** @result	output stream
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 ** 
 ** @version	0.7.2
 **/
ostream&
Aiconfig::write(ostream& ostr) const
{
  if (this->difficulty() != CUSTOM) {
    ostr << "difficulty = " << ::name(this->difficulty()) << '\n';
    return ostr;
  } // if (this->difficulty() != CUSTOM)

  for (unsigned i = 0; i < this->aitype_p.size(); ++i) {
    ostr << "type = " << i << ": " << this->aitype(i) << '\n';
    ostr << "future limit = " << i << ": " << this->future_limit(i) << '\n';
  }
  ostr << TRUSTING << " = "
    << this->value(TRUSTING) << '\n'
    << FAIRPLAYHANDS << " = "
    << this->value(FAIRPLAYHANDS) << '\n'
    << FAIRPLAYTEAMS << " = "
    << this->value(FAIRPLAYTEAMS) << '\n'
    << '\n'
    << PLAY_TO_MARRY << " = "
    << this->value(PLAY_TO_MARRY) << '\n' 
    << CHOOSE_ACE << " = "
    << this->value(CHOOSE_ACE) << '\n' 
    << CHOOSE_FOR_COLOR_TRICK  << " = "
    << this->value(CHOOSE_FOR_COLOR_TRICK) << '\n'
    << RETRY_COLOR  << " = "
    << this->value(RETRY_COLOR) << '\n'
    << SERVE_COLOR_TRICK  << " = "
    << this->value(SERVE_COLOR_TRICK) << '\n'
    << SERVE_TRUMP_TRICK  << " = "
    << this->value(SERVE_TRUMP_TRICK) << '\n'
    << CHOOSE_PFUND            << " = "
    << this->value(CHOOSE_PFUND) << '\n'
    << CHOOSE_PFUND_POVERTY            << " = "
    << this->value(CHOOSE_PFUND_POVERTY) << '\n'
    << JAB_FOR_ACE             << " = "
    << this->value(JAB_FOR_ACE ) << '\n'
    << CREATE_FEHL             << " = "
    << this->value(CREATE_FEHL ) << '\n'
    << BEST_WINNING           << " = "
    << this->value(BEST_WINNING) << '\n'
    << LOW_HIGH                << " = "
    << this->value(LOW_HIGH ) << '\n'
    << PLAY_FOR_TEAM           << " = "
    << this->value(PLAY_FOR_TEAM) << '\n'
    << JAB_FOX                << " = "
    << this->value(JAB_FOX) << '\n'
    << TRY_FOR_DOPPELKOPF                << " = "
    << this->value(TRY_FOR_DOPPELKOPF) << '\n'
    << DRAW_TRUMP                << " = "
    << this->value(DRAW_TRUMP) << '\n'
    << GET_TRICK_FOR_ANNOUNCEMENT                << " = "
    << this->value(GET_TRICK_FOR_ANNOUNCEMENT) << '\n'
    << POVERTY_PLAYER_PLAY_PFUND      << " = "
    << this->value(POVERTY_PLAYER_PLAY_PFUND) << '\n'
    << MEATLESS_PLAYHIGHESTCOLOR      << " = "
    << this->value(MEATLESS_PLAYHIGHESTCOLOR) << '\n'
    << PICTURE_SECONDBESTTRUMP<< " = "
    << this->value(PICTURE_SECONDBESTTRUMP) << '\n'
    << COLOR_CHOOSE_ACE<< " = "
    << this->value(COLOR_CHOOSE_ACE)<< '\n'
    << COLOR_BEST_WINNING<< " = "
    << this->value(COLOR_BEST_WINNING) << '\n'
    << COLOR_JAB_FOR_ACE<< " = "
    << this->value(COLOR_JAB_FOR_ACE) << '\n'
    << COLOR_LOW_HIGH  << " = "
    << this->value(COLOR_LOW_HIGH) << '\n'
    << '\n'
    << FEHLCREATIONONFIRSTCARD  << " = "
    << this->value(FEHLCREATIONONFIRSTCARD) << '\n'
    << '\n'
    << REMEMBERTRICKS << " = "
    << this->value(REMEMBERTRICKS) << '\n'
    << '\n'
    << LIMIT_THROW_FEHL << " = "
    << this->value(LIMIT_THROW_FEHL) << '\n'
    << LIMITQUEEN << " = "
    << this->value(LIMITQUEEN) << '\n'
    << LIMITDOLLE << " = "
    << this->value(LIMITDOLLE) << '\n'
    << '\n'
    << LASTFEHLCREATION << " = "
    << this->value(LASTFEHLCREATION) << '\n'
    << '\n'
    << LASTHEURISTICTRICK << " = "
    << this->value(LASTHEURISTICTRICK) << '\n'
    << '\n'
    << FIRST_TRICK_FOR_TRUMP_POINTS_OPTIMIZATION<< " = "
    << this->value(FIRST_TRICK_FOR_TRUMP_POINTS_OPTIMIZATION) << '\n'
    << '\n'
    << ANNOUNCELIMIT << " = "
    << this->value(ANNOUNCELIMIT) << '\n'
    << ANNOUNCELIMITDEC << " = "
    << this->value(ANNOUNCELIMITDEC) << '\n'
    << '\n'
    << ANNOUNCECONFIG << " = "
    << this->value(ANNOUNCECONFIG) << '\n'
    << '\n'
    << ANNOUNCELIMITREPLY << " = "
    << this->value(ANNOUNCELIMITREPLY) << '\n'
    << ANNOUNCECONFIGREPLY << " = "
    << this->value(ANNOUNCECONFIGREPLY) << '\n'
    << '\n'
    << TAKEPOVERTY << " = "
    << this->value(TAKEPOVERTY) << '\n'
    << '\n'
    << SINGLESOLO << " = "
    << this->value(SINGLESOLO) << '\n'
    << DOUBLESOLO << " = "
    << this->value(DOUBLESOLO) << '\n'
    << TRIPLESOLO << " = "
    << this->value(TRIPLESOLO) << '\n'
    << COLORSOLO << " = "
    << this->value(COLORSOLO) << '\n'
    << MEATLESS << " = "
    << this->value(MEATLESS) << '\n'
    << '\n'
    << FUTURE_LIMIT << " = "
    << this->value(FUTURE_LIMIT) << '\n'
    << '\n'
    << LIMITTHROWING << " = "
    << this->value(LIMITTHROWING) << '\n'
    << '\n'
    << LIMITHIGH << " = "
    << this->value(LIMITHIGH) << '\n'
    << '\n'
    << TRUMPLIMIT_SOLOCOLOR << " = "
    << this->value(TRUMPLIMIT_SOLOCOLOR) << '\n'
    << '\n'
    << TRUMPLIMIT_SOLOJACK << " = "
    << this->value(TRUMPLIMIT_SOLOJACK) << '\n'
    << '\n'
    << TRUMPLIMIT_SOLOQUEEN << " = "
    << this->value(TRUMPLIMIT_SOLOQUEEN) << '\n'
    << '\n'
    << TRUMPLIMIT_SOLOKING << " = "
    << this->value(TRUMPLIMIT_SOLOKING) << '\n'
    << '\n'
    << TRUMPLIMIT_SOLOJACKKING << " = "
    << this->value(TRUMPLIMIT_SOLOJACKKING) << '\n'
    << '\n'
    << TRUMPLIMIT_SOLOJACKQUEEN << " = "
    << this->value(TRUMPLIMIT_SOLOJACKQUEEN) << '\n'
    << '\n'
    << TRUMPLIMIT_SOLOQUEENKING << " = "
    << this->value(TRUMPLIMIT_SOLOQUEENKING) << '\n'
    << '\n'
    << TRUMPLIMIT_SOLOKOEHLER << " = "
    << this->value(TRUMPLIMIT_SOLOKOEHLER) << '\n'
    << '\n'
    << TRUMPLIMIT_MEATLESS << " = "
    << this->value(TRUMPLIMIT_MEATLESS) << '\n'
    << '\n'
    << TRUMPLIMIT_NORMAL << " = "
    << this->value(TRUMPLIMIT_NORMAL) << '\n'
    << '\n'
    << LOWEST_TRUMPLIMIT_SOLOCOLOR << " = "
    << this->value(LOWEST_TRUMPLIMIT_SOLOCOLOR) << '\n'
    << '\n'
    << LOWEST_TRUMPLIMIT_SOLOJACK << " = "
    << this->value(LOWEST_TRUMPLIMIT_SOLOJACK) << '\n'
    << '\n'
    << LOWEST_TRUMPLIMIT_SOLOQUEEN << " = "
    << this->value(LOWEST_TRUMPLIMIT_SOLOQUEEN) << '\n'
    << '\n'
    << LOWEST_TRUMPLIMIT_SOLOKING << " = "
    << this->value(LOWEST_TRUMPLIMIT_SOLOKING) << '\n'
    << '\n'
    << LOWEST_TRUMPLIMIT_SOLOJACKKING << " = "
    << this->value(LOWEST_TRUMPLIMIT_SOLOJACKKING) << '\n'
    << '\n'
    << LOWEST_TRUMPLIMIT_SOLOJACKQUEEN << " = "
    << this->value(LOWEST_TRUMPLIMIT_SOLOJACKQUEEN) << '\n'
    << '\n'
    << LOWEST_TRUMPLIMIT_SOLOQUEENKING << " = "
    << this->value(LOWEST_TRUMPLIMIT_SOLOQUEENKING) << '\n'
    << '\n'
    << LOWEST_TRUMPLIMIT_SOLOKOEHLER << " = "
    << this->value(LOWEST_TRUMPLIMIT_SOLOKOEHLER) << '\n'
    << '\n'
    << LOWEST_TRUMPLIMIT_MEATLESS << " = "
    << this->value(LOWEST_TRUMPLIMIT_MEATLESS) << '\n'
    << '\n'
    << LOWEST_TRUMPLIMIT_NORMAL << " = "
    << this->value(LOWEST_TRUMPLIMIT_NORMAL) << '\n'
    ;

  return ostr;
} // ostream& Aiconfig::write(ostream& ostr) const

/**
 ** loads the infos from the file
 **
 ** @param	filename	file to load the aiconfig from
 **
 ** @return	whether the loading was successful
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
bool
Aiconfig::load(string const& filename)
{
  {
    ifstream istr(filename.c_str());
    if (istr.good())
      return this->load(istr);
  }
  {
    ifstream istr((::setting(Setting::PRIVATE_DATA_DIRECTORY)
		   + "/" + ::setting(Setting::AI_DIRECTORY)
		   + "/" + filename).c_str());
    if (istr.good())
      return this->load(istr);
  }
  {
    ifstream istr((::setting(Setting::PUBLIC_DATA_DIRECTORY)
		   + "/" + ::setting(Setting::AI_DIRECTORY)
		   + "/" + filename).c_str());
    if (istr.good())
      return this->load(istr);
  }

  return false;
} // bool Aiconfig::load(string const& filename)

/**
 ** loads the infos from the stream
 **
 ** @param	istr	stream with the infos
 **
 ** @return	whether the loading was successful
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.4
 **/
bool
Aiconfig::load(istream& istr)
{
#ifndef POSTPHONED
  // if the following is an empty line, ignore it
  if ((istr.peek() == '\n')
      || (istr.peek() == '\r'))
    return true;
#endif

  unsigned depth = 0;

  // load the configuration
  while (istr.good()) {
    Config config;
    istr >> config;
    if (istr.fail())
      break;

    // finished with the config file
    if ((config.name == "") && (config.value == ""))
      break;

    if (config.separator) {
      // a setting
      if (config.name == "difficulty") {
	// a preset difficulty
	int difficulty;
	for (difficulty = DIFFICULTY_FIRST;
	     difficulty <= DIFFICULTY_LAST;
	     difficulty++) {
	  if (config.value == ::name(static_cast<Difficulty>(difficulty))) {
	    this->set_to_difficulty(static_cast<Difficulty>(difficulty));
	    break;
	  }
	}
	if (difficulty > AITYPE::AITYPE_LAST)
	  cerr << "Aiconfig::load()\n"
	    << "  unknown difficulty '" << config.value << "'"
	    << endl;
      } else if (config.name == "type") {
	try {
	  stringstream istr(config.value);

	  // tricknumber
	  unsigned trickno;
	  istr >> trickno;
	  if (!istr.good()) {
	    throw config.value;
	  }

	  // separator
	  if (istr.peek() != ':')
	    throw config.value;
	  istr.get();

	  while (std::isspace(istr.peek())
		 && istr.good())
	    istr.get();

	  if (!istr.good())
	    throw config.value;

	  // aitype
	  string aitype;
	  std::getline(istr, aitype);

	  {
	    int t;
	    for (t = AITYPE::AITYPE_FIRST; t <= AITYPE::AITYPE_LAST; t++)
	      if (aitype == ::name(AiType(t))) {
		this->set_aitype(trickno, AiType(t));
		break;
	      }
	    if (t > AITYPE::AITYPE_LAST) {
	      throw config.value;
	    }
	  }

	} // try
	catch (string value) {
	  cerr << "aiconfig: "
	    << "illegal aitype '" << value << "'."
	    << endl;
	}
      } else if (config.name == "future limit") {
	try {
	  stringstream istr(config.value);

	  // tricknumber
	  unsigned trickno;
	  istr >> trickno;
	  if (!istr.good()) {
	    throw config.value;
	  }

	  // separator
	  if (istr.peek() != ':') {
	    throw config.value;
	  }
	  istr.get();

	  while (std::isspace(istr.peek())
		 && istr.good())
	    istr.get();

	  if (!istr.good()) {
	    throw config.value;
	  }

	  // future limit
	  unsigned future_limit;
	  istr >> future_limit;
	  if (istr.fail()) {
	    throw config.value;
	  }

	  this->set_future_limit(trickno, future_limit);
	} // try
	catch (string value) {
	  cerr << "aiconfig: "
	    << "illegal future limit '" << value << "'."
	    << endl;
	}
      } else {
	if (!this->set(config.name, config.value)) {
	  cerr << "aiconfig: "
	    << "illegal config '" << config.name << "'."
	    << endl;
	}
      } // if (config.name == "type")
    } else { // if (config.separator)
      // a setting
      // if the value is in parentencies, remove both
      if(config.name == "!end") {
	// ignore the rest of the file
	break;
      } else if(config.name == "!stdout") {
	// output of the data to 'stdout'
	cout << config.value << endl;
      } else if(config.name == "!stderr") {
	// output of the data to 'stderr'
	cerr << config.value << endl;
      } else if(config.name == "{") {
	depth += 1;
      } else if(config.name == "}") {
	if (depth == 0) {
	  cerr << "Aiconfig: found a '}' without a '{' before.\n"
	    << "Finish reading the the file."
	    << endl;
	  break;
	} // if (depth == 0)
	depth -= 1;
	if (depth == 0)
	  break;
      } else if(config.name == "") {
	cerr << "Aiconfig: "
	  << "Ignoring line \'" << config.value << "\'.\n";
      } else {
	cerr << "Aiconfig: "
	  << "type '" << config.name << "' unknown.\n"
	  << "Ignoring it.\n";
      } // if (config.name == .)
    } // config.separator
  } // while (istr.good())

  return istr.good();
} // bool Aiconfig::load(istream& istr)

/**
 ** save the infos in the file
 **
 ** @param	filename	file to save the aiconfig
 **
 ** @return	whether the saving was successful
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.4
 **/
bool
Aiconfig::save(string const& filename) const
{
  ofstream ostr(filename.c_str());
  if (ostr.fail())
    return false;

  ostr << *this;

  return ostr.good();
} // bool Aiconfig::save(string const& filename) const

/**
 ** comparison of two aiconfigs
 **
 ** @param	aiconfig_a   first aiconfig
 ** @param	aiconfig_b   second aiconfig
 **
 ** @return	whether the two aiconfigs are equal
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
bool
operator==(Aiconfig const& aiconfig_a, Aiconfig const& aiconfig_b)
{
  return aiconfig_a.equal(aiconfig_b);
} // bool operator==(Aiconfig aiconfig_a, Aiconfig aiconfig_b)

/**
 ** comparison of two aiconfigs
 **
 ** @param	aiconfig_a   first aiconfig
 ** @param	aiconfig_b   second aiconfig
 **
 ** @return	whether the two aiconfigs are different
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
bool
operator!=(Aiconfig const& aiconfig_a, Aiconfig const& aiconfig_b)
{
  return !(aiconfig_a == aiconfig_b);
} // bool operator!=(Aiconfig aiconfig_a, Aiconfig aiconfig_b)

/**
 ** -> result
 **
 ** @param	type	heuristic type
 **
 ** @return	the name of 'type'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **/
string
name(Aiconfig::Heuristic const type)
{
  switch(type) {
  case Aiconfig::NO_HEURISTIC:
    return "no heuristic";
  case Aiconfig::BUG_REPORT:
    return "bug report";
  case Aiconfig::ONLY_VALID_CARD:
    return "only valid card";
  case Aiconfig::PLAY_TO_MARRY:
    return "play to marry";
  case Aiconfig::CHOOSE_ACE:
    return "choose ace";
  case Aiconfig::CHOOSE_FOR_COLOR_TRICK:
    return "choose for color trick";
  case Aiconfig::RETRY_COLOR:
    return "retry color";
  case Aiconfig::SERVE_COLOR_TRICK:
    return "serve color trick";
  case Aiconfig::SERVE_TRUMP_TRICK:
    return "serve trump trick";
  case Aiconfig::CHOOSE_PFUND:
    return "choose pfund";
  case Aiconfig::CHOOSE_PFUND_POVERTY:
    return "choose pfund poverty";
  case Aiconfig::JAB_FOR_ACE:
    return "jab for ace";
  case Aiconfig::CREATE_FEHL:
    return "create fehl";
  case Aiconfig::BEST_WINNING:
    return "best winning card";
  case Aiconfig::LOW_HIGH:
    return "play low high";
  case Aiconfig::PLAY_FOR_TEAM:
    return "play for team";
  case Aiconfig::JAB_FOX:
    return "jab fox";
  case Aiconfig::TRY_FOR_DOPPELKOPF:
    return "try for doppelkopf";
  case Aiconfig::DRAW_TRUMP:
    return "draw trump";
  case Aiconfig::GET_TRICK_FOR_ANNOUNCEMENT:
    return "get trick for announcement";

  case Aiconfig::POVERTY_PLAYER_PLAY_PFUND:
    return "poverty: play pfund";
  case Aiconfig::MEATLESS_PLAYHIGHESTCOLOR:
    return "meatless: play highest color";
  case Aiconfig::PICTURE_SECONDBESTTRUMP:
    return "picture: second best trump";
  case Aiconfig::COLOR_CHOOSE_ACE:
    return "color: choose ace";
  case Aiconfig::COLOR_BEST_WINNING:
    return "color: best winning";
  case Aiconfig::COLOR_JAB_FOR_ACE:
    return "color: jab for ace";
  case Aiconfig::COLOR_LOW_HIGH:
    return "color: play low high";
  case Aiconfig::CHOOSE_BEST_CARD:
    return "choose best card";
  } // switch(type)

  return "";
} // string name(Aiconfig::Heuristic name type)

/**
 ** -> result
 **
 ** @param	difficulty    difficulty
 **
 ** @return	the name of 'difficulty'
 **
 ** @author	Diether Knof
 **
 ** @version	0.7.2
 **/
string
name(Aiconfig::Difficulty const difficulty)
{
  switch(difficulty) {
  case Aiconfig::CUSTOM:
    return "custom";
  case Aiconfig::NOVICE:
    return "novice";
  case Aiconfig::STANDARD_DEFENSIVE:
    return "standard defensive";
  case Aiconfig::STANDARD_OFFENSIVE:
    return "standard offensive";
  case Aiconfig::PROFI:
    return "profi";
  case Aiconfig::PROFI_UNFAIR:
    return "profi unfair";
  } // switch(difficulty)

  return "";
} // string name(Aiconfig::Difficulty difficulty)

/**
 ** -> result
 **
 ** @param	type	bool type
 **
 ** @return	the name of 'type'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
string
name(Aiconfig::TypeBool const type)
{
  switch(type) {
  case Aiconfig::TRUSTING:
    return "trusting";
  case Aiconfig::FAIRPLAYHANDS:
    return "fairplay hands";
  case Aiconfig::FAIRPLAYTEAMS:
    return "fairplay teams";
  case Aiconfig::FEHLCREATIONONFIRSTCARD:
    return "first card fehl creation";
  } // switch(type)

  return "";
} // string name(Aiconfig::TypeBool type)

/**
 ** -> result
 **
 ** @param	type	unsigned type
 **
 ** @return	the name of 'type'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
string
name(Aiconfig::TypeUnsigned const type)
{
  switch(type) {
  case Aiconfig::REMEMBERTRICKS:
    return "remember tricks";
  case Aiconfig::LIMIT_THROW_FEHL:
    return "limit throw fehl";
  case Aiconfig::LIMITQUEEN:
    return "limit queen";
  case Aiconfig::LIMITDOLLE:
    return "limit dolle";
  case Aiconfig::LASTFEHLCREATION:
    return "last fehlcreation";
  case Aiconfig::LASTHEURISTICTRICK:
    return "last trick with heuristics";
  case Aiconfig::FIRST_TRICK_FOR_TRUMP_POINTS_OPTIMIZATION:
    return "first trick for trump points optimization";
  case Aiconfig::ANNOUNCELIMIT:
    return "announce limit";
  case Aiconfig::ANNOUNCELIMITDEC:
    return "announce limit decrement";
  case Aiconfig::ANNOUNCECONFIG:
    return "announce config";
  case Aiconfig::ANNOUNCELIMITREPLY:
    return "announce limit reply";
  case Aiconfig::ANNOUNCECONFIGREPLY:
    return "announce config reply";

  case Aiconfig::TAKEPOVERTY:
    return "take poverty";
  case Aiconfig::SINGLESOLO:
    return "handvalue single solo";
  case Aiconfig::DOUBLESOLO:
    return "handvalue double solo";
  case Aiconfig::TRIPLESOLO:
    return "handvalue trible solo";
  case Aiconfig::COLORSOLO:
    return "handvalue color solo";
  case Aiconfig::MEATLESS:
    return "handvalue meatless";
  case Aiconfig::FUTURE_LIMIT:
    return "future calculating limit";
  } // switch(type)

  return "";
} // string name(Aiconfig::TypeUnsigned type)

/**
 ** -> result
 **
 ** @param	type	card type
 **
 ** @return	the name of 'type'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
string
name(Aiconfig::TypeCard const type)
{
  switch(type) {
  case Aiconfig::LIMITTHROWING:
    return "limit throwing";
  case Aiconfig::LIMITHIGH:
    return "limit high";
  case Aiconfig::TRUMPLIMIT_SOLOCOLOR:
    return "trump limit color";
  case Aiconfig::TRUMPLIMIT_SOLOJACK:
    return "trump limit jack";
  case Aiconfig::TRUMPLIMIT_SOLOQUEEN:
    return "trump limit queen";
  case Aiconfig::TRUMPLIMIT_SOLOKING:
    return "trump limit king";
  case Aiconfig::TRUMPLIMIT_SOLOJACKKING:
    return "trump limit jack king";
  case Aiconfig::TRUMPLIMIT_SOLOJACKQUEEN:
    return "trump limit jack queen";
  case Aiconfig::TRUMPLIMIT_SOLOQUEENKING:
    return "trump limit queen king";
  case Aiconfig::TRUMPLIMIT_SOLOKOEHLER:
    return "trump limit koehler";
  case Aiconfig::TRUMPLIMIT_MEATLESS:
    return "trump limit meatless";
  case Aiconfig::TRUMPLIMIT_NORMAL:
    return "trump limit normal";
  case Aiconfig::LOWEST_TRUMPLIMIT_SOLOCOLOR:
    return "lowest trump limit color";
  case Aiconfig::LOWEST_TRUMPLIMIT_SOLOJACK:
    return "lowest trump limit jack";
  case Aiconfig::LOWEST_TRUMPLIMIT_SOLOQUEEN:
    return "lowest trump limit queen";
  case Aiconfig::LOWEST_TRUMPLIMIT_SOLOKING:
    return "lowest trump limit king";
  case Aiconfig::LOWEST_TRUMPLIMIT_SOLOJACKKING:
    return "lowest trump limit jack king";
  case Aiconfig::LOWEST_TRUMPLIMIT_SOLOJACKQUEEN:
    return "lowest trump limit jack queen";
  case Aiconfig::LOWEST_TRUMPLIMIT_SOLOQUEENKING:
    return "lowest trump limit queen king";
  case Aiconfig::LOWEST_TRUMPLIMIT_SOLOKOEHLER:
    return "lowest trump limit koehler";
  case Aiconfig::LOWEST_TRUMPLIMIT_MEATLESS:
    return "lowest trump limit meatless";
  case Aiconfig::LOWEST_TRUMPLIMIT_NORMAL:
    return "lowest trump limit normal";
  } // switch(type)

  return "";
} // string name(Aiconfig::TypeCard type)

/**
 ** writes the aiconfig into the output stream
 **
 ** @param	ostr       stream to write in
 ** @param	aiconfig   aiconfig to write
 **
 ** @return	output stream
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
ostream&
operator<<(ostream& ostr, Aiconfig const& aiconfig)
{
  aiconfig.write(ostr);

  return ostr;
} // ostream& operator<<(ostream& ostr, const Aiconfig& aiconfig)

/**
 ** -> result
 **
 ** @param	aitype	ai type
 **
 ** @return	the name of 'aitype'
 **
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **/
string
name(AiType const& aitype)
{
  using namespace AITYPE;

  switch(aitype) {
  case NO_CHOOSEBESTCARD:
    return "no choosebestcard";
  case RANDOM:
    return "random";
  case VIRTUAL_GAMES:
    return "virtual games";
  case GAMETREE:
    return "gametree";
  case GAMETREE_WITH_HEURISTICS:
    return "gametree with heuristics";
  case MONTE_CARLO:
    return "monte carlo";
  } // switch(aitype)

  return "";
} // string name(AiType const& aitype)
