/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2004  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 "ai.h"

#include "heuristics.h"
#include "weighting.h"
#include "aiDb.h"
#include "../../party/party.h"
#include "../../party/rule.h"
#include "../namegen.h"



/**
 **
 ** Construktor
 **
 ** @param	istr	stream with the infos
 **
 ** @return	-
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.2
 **
 ** @todo	using constructor Aiconfig(istr)
 **
 **/
Ai::Ai(istream& istr) 
  : Player(AI),
  Aiconfig(),
  playedcards_p(),
  trick_p(::party.rule()(Rule::NUMBER_OF_TRICKS_IN_GAME)),
  trickno_p(0),
  colorjabbed_p(::party.rule()(Rule::NUMBER_OF_CARD_COLORS),
		vector<bool>(::party.rule()(Rule::NUMBER_OF_PLAYERS_IN_GAME),
			     false)),
			     color_runs_p(::party.rule()(Rule::NUMBER_OF_CARD_COLORS), 0),
			     low_p(true),
			     meatlesscolor_p(Card::NOCARDCOLOR),
			     trumpless_(),
			     last_trick_to_calculate_p(12),
			     lastHeuristic_(Aiconfig::NO_HEURISTIC),
			     vgi_(new VirtualGamesAi(*this)),
hi_(new HeuristicAi(*this)),
  silentMarriage_(false)
{
  DEBUG_CALLING(INFO_AI && INFO_INIT,
		"Ai::Ai(istr)");

  // To put this in the Konstruktor leads to a segmentation fault
  this->Aiconfig::load(istr);
  
#ifndef RELEASE
  delete this->db_;
  this->db_ = new AiDb;
#endif

  DEBUG_RETURNING(VOID,
		  INFO_AI && INFO_INIT,
		  "Ai::Ai(istr)");
} // Ai::Ai(istream& istr)

/**********************************************************************
 *
 **    Ai::Ai(const int pl,const Aiconfig aic)
 *
 **    Parameters:
 **       pl : number of this player in the game
 **       aic : Aiconfiguration
 *
 **    Result: Standar initiliazed AI
 *
 **    Version: Alpha
 *
 **    Description:
 *
 *
 **********************************************************************/

Ai::Ai(const Aiconfig aic):
Player(AI),
  Aiconfig(aic),
  playedcards_p(),
  trick_p(::party.rule()(Rule::NUMBER_OF_TRICKS_IN_GAME)),
  trickno_p(0),
  colorjabbed_p(::party.rule()(Rule::NUMBER_OF_CARD_COLORS),
		vector<bool>(::party.rule()(Rule::NUMBER_OF_PLAYERS_IN_GAME),
			     false)),
			     color_runs_p(::party.rule()(Rule::NUMBER_OF_CARD_COLORS), 0),
			     low_p(true),
			     meatlesscolor_p(Card::NOCARDCOLOR),
			     trumpless_(),
			     last_trick_to_calculate_p(12),
			     lastHeuristic_(Aiconfig::NO_HEURISTIC),
			     vgi_(new VirtualGamesAi(*this)),
hi_(new HeuristicAi(*this)),
  silentMarriage_(false)
{
  DEBUG_CALLING(INFO_AI && INFO_INIT,"Ai::Ai()");

  this->name_set( ::generate_name(RAND( 4 ) + 5) );

#ifndef RELEASE
  delete this->db_;
  this->db_ = new AiDb;
#endif

  DEBUG_RETURNING_N(INFO_AI && INFO_INIT,"Ai::Ai()");
}

/**
 **
 ** copy constructor
 **
 ** @param	player	player to copy
 **
 ** @return	-
 **
 ** @version	0.6.0
 **
 ** @author	Diether Knof
 **      
 **/
Ai::Ai(Player const& player) :
Player(player),
  Aiconfig(),
  playedcards_p(),
  trick_p(::party.rule()(Rule::NUMBER_OF_TRICKS_IN_GAME)),
  trickno_p(0),
  colorjabbed_p(::party.rule()(Rule::NUMBER_OF_CARD_COLORS),
		vector<bool>(::party.rule()(Rule::NUMBER_OF_PLAYERS_IN_GAME),
			     false)
	       ) ,
  color_runs_p(::party.rule()(Rule::NUMBER_OF_CARD_COLORS), 0),
  low_p(true),
  meatlesscolor_p(Card::NOCARDCOLOR),
  trumpless_(),
  last_trick_to_calculate_p(12),
  lastHeuristic_(Aiconfig::NO_HEURISTIC),
  vgi_(new VirtualGamesAi(*this)),
hi_(new HeuristicAi(*this)),
  silentMarriage_(false)
{
  DEBUG_CALLING(INFO_AI && INFO_INIT,
		"Ai::Ai(player)");

  this->type_set(AI);

  if (::game_status & GAMESTATUS::GAME)
    DEBUG_ASSERTION(false,
		    "Ai::Ai(player):\n"
		    "  in game");

#ifndef RELEASE
  // ToDo
  delete this->db_;
  this->db_ = new AiDb;
#endif

  DEBUG_RETURNING(VOID,
		  INFO_AI && INFO_INIT,
		  "Ai::Ai(player)");
} // Ai::Ai(Player const& player)

/**********************************************************************
 *
 ** Ai::Ai(const Ai& ai)
 *
 ** Parameters:  ai - Ai to copy
 *
 ** Result:      -
 *
 ** Version:     0.4.2
 *
 ** Description: constructor
 **      
 *
 **********************************************************************/
Ai::Ai(const Ai& ai) :
Player(ai),
  Aiconfig(ai),
  playedcards_p(ai.playedcards_p),
  trick_p(ai.trick_p),
  trickno_p(ai.trickno_p),
  colorjabbed_p(ai.colorjabbed_p),
  color_runs_p(ai.color_runs_p),
  low_p(ai.low_p),
  meatlesscolor_p(ai.meatlesscolor_p),
  trumpless_( ai.trumpless_ ),
  last_trick_to_calculate_p(ai.last_trick_to_calculate_p),
  lastHeuristic_(ai.lastHeuristic_),
  vgi_(new VirtualGamesAi(*this)),
hi_(new HeuristicAi(*this)),
  silentMarriage_( ai.silentMarriage_ )
{
  DEBUG_CALLING(INFO_AI && INFO_INIT,
		"Ai::Ai(Ai)");

  if (::game_status & GAMESTATUS::GAME)
    DEBUG_ASSERTION((this->trumpless_.size() > 0),
		    "Ai::Ai(ai):\n"
		    "  'trumpless_.size() == 0");

  this->type_set(AI);

#ifndef RELEASE
  // ToDo
  delete this->db_;
  this->db_ = new AiDb;
#endif
#ifdef RELEASE
  if (::game_status & GAMESTATUS::GAME) {
    for (unsigned i = 0; i < this->colorjabbed_p.size(); ++i) {
      DEBUG_ASSERTION((ai.colorjabbed_p[i].size() == this->game().playerno()),
		      "Ai::Ai(ai)\n"
		      "ai");
      DEBUG_ASSERTION((this->colorjabbed_p[i].size() == this->game().playerno()),
		      "Ai::Ai(ai)\n"
		      "this");
    }
  } // if (::game_status & GAMESTATUS::GAME)
#endif

  DEBUG_RETURNING(VOID,
		  INFO_AI && INFO_INIT,
		  "Ai::Ai(Ai)");
} // Ai::Ai(const Ai& ai)

/**********************************************************************
 *
 **    Ai::~Ai()
 *
 **    Parameters:
 *
 **    Result: destroyed AI
 *
 **    Version: Alpha
 *
 **    Description:
 *
 *
 **********************************************************************/

Ai::~Ai()
{
  DEBUG_CALLING(INFO_AI && INFO_INIT, "Ai::~Ai()");

  delete this->vgi_;
  delete this->hi_;

  DEBUG_RETURNING_N(INFO_AI && INFO_INIT, "Ai::~Ai()");
}

/**
 **
 ** clone the player
 **
 ** @param       -
 **
 ** @return      pointer of a clone
 **
 ** @author      Diether Knof
 **
 ** @version     0.5.3
 **
 **/
Ai*
Ai::clone() const
{  
  DEBUG_CALLING(INFO_AI && INFO_INIT,
		"Ai::clone()");

  DEBUG_RETURNING(new Ai(*this),
		  INFO_AI && INFO_INIT,
		  "Ai::clone()");
} // virtual Ai* Ai::clone() const

/**********************************************************************
 *
 **    Ai& Ai::operator=(const Ai& ai)
 *
 **    Parameters:  Ai to copy
 *
 **    Result: copy of ai
 *
 **    Version: Alpha
 *
 **    Description: 
 **      
 *
 **********************************************************************/

Ai& Ai::operator=(const Ai& ai)
{
  DEBUG_CALLING(INFO_AI && INFO_INIT, "Ai::operator=");

  static_cast<Player&>(*this) = static_cast<const Player&>(ai);
  static_cast<Aiconfig&>(*this) = static_cast<const Aiconfig&>(ai);

  this->playedcards_p = ai.playedcards_p;
  this->trick_p = ai.trick_p;
  this->trickno_p=ai.trickno_p;
  this->colorjabbed_p = ai.colorjabbed_p;
  this->color_runs_p = ai.color_runs_p;
  this->low_p=ai.low_p;
  this->meatlesscolor_p=ai.meatlesscolor_p;
  this->trumpless_ = ai.trumpless_;
  this->last_trick_to_calculate_p=ai.last_trick_to_calculate_p;
  this->lastHeuristic_ = ai.lastHeuristic_;
  this->silentMarriage_ = ai.silentMarriage_;

  DEBUG_RETURNING(*this,INFO_AI && INFO_INIT, "Ai::operator=");
}

/**
 **
 ** cast to 'VirtualGamesInterface'
 **
 ** @param	-
 **
 ** @return	the cast
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.4
 **
 **/
Ai::operator VirtualGamesInterface const&() const
{
  DEBUG_CALLING(INFO_AI && INFO_INIT,
		"Ai::operator VirtualGamesInterface()");

  DEBUG_RETURNING(*(this->vgi_),
		  INFO_AI && INFO_INIT,
		  "Ai::operator VirtualGamesInterface()");
} // virtual Ai::operator VirtualGamesInterface const&() const

/**
 **
 ** cast to 'HeuristicInterface'
 **
 ** @param	-
 **
 ** @return	the cast
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.4
 **
 **/
Ai::operator HeuristicInterface const&() const
{
  DEBUG_CALLING(INFO_AI && INFO_INIT,
		"Ai::operator HeuristicInterface()");

  DEBUG_RETURNING(*(this->hi_),
		  INFO_AI && INFO_INIT,
		  "Ai::operator HeuristicInterface()");
} // virtual Ai::operator HeuristicInterface const&() const

/**
 **
 ** writes the ai into the stream
 **
 ** @param	ostr	stream the ai is to be written in
 **
 ** @return	the output stream
 **
 ** @author	Diether Knof
 **
 ** @version	0.5.2
 **
 **/
ostream&
Ai::out(ostream& ostr) const
{
  DEBUG_CALLING(INFO_AI && INFO_IO,
		"Ai::out(ostr)");

  // output of the number, name and type
  this->Player::out(ostr);
  // output of the configuration
  this->Aiconfig::out(ostr);

  DEBUG_RETURNING(ostr,
		  INFO_AI && INFO_IO,
		  "Ai::out(ostr)");
} // virtual ostream& Ai::out(ostream& ostr) const

/**
 **
 ** compares the '*this' with 'ai'
 **
 ** @param       ai	ai to compare with
 **
 ** @return      whther 'ai' is equal to '*this'
 **
 ** @author      Diether Knof
 **
 ** @version     0.5.4
 **
 **/
bool
Ai::isequal(Ai const& ai) const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,
		"Ai::isequal(ai)");

  if (this->playedcards_p != ai.playedcards_p)
    DEBUG_RETURNING(false,
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::isequal(ai) = false (playedcards)");

  // vector<vector<bool> > colorjabbed_p;
  if (this->colorjabbed_p.size() != ai.colorjabbed_p.size())
    DEBUG_RETURNING(false,
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::isequal(ai) = false (colorjabbed)");

  for (unsigned i = 0; i < this->colorjabbed_p.size(); i++) {
    if (this->colorjabbed_p[i].size() != ai.colorjabbed_p[i].size())
      DEBUG_RETURNING(false,
		      INFO_AI && INFO_OTHER_FUNCTION,
		      "Ai::isequal(ai) = false (colorjabbed)");

    for (unsigned j = 0; j < this->colorjabbed_p[i].size(); j++)
      if (this->colorjabbed_p[i][j] != ai.colorjabbed_p[i][j])
	DEBUG_RETURNING(false,
			INFO_AI && INFO_OTHER_FUNCTION,
			"Ai::isequal(ai) = false (colorjabbed)");
  } // for (i < this->colorjabbed_p.size())

  // vector<unsigned> color_runs_p;
  if (this->color_runs_p.size() != ai.color_runs_p.size())
    DEBUG_RETURNING(false,
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::isequal(ai) = false (color_runs)");

  for (unsigned i = 0; i < this->color_runs_p.size(); i++)
    if (this->color_runs_p[i] != ai.color_runs_p[i])
      DEBUG_RETURNING(false,
		      INFO_AI && INFO_OTHER_FUNCTION,
		      "Ai::isequal(ai) = false (color_runs)");


  // vector<unsigned> trumpless_p;
  if (this->trumpless_.size() != ai.trumpless_.size())
    DEBUG_RETURNING(false,
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::isequal(ai) = false (trumpless)");

  // vector<bool> trumpless_;
  for (unsigned i = 0; i < this->trumpless_.size(); i++)
    if (this->trumpless_[i] != ai.trumpless_[i])
      DEBUG_RETURNING(false,
		      INFO_AI && INFO_OTHER_FUNCTION,
		      "Ai::isequal(ai) = false (trumpless)");

  // unsigned trickno_p;  
  if (this->trickno_p != ai.trickno_p)
    DEBUG_RETURNING(false,
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::isequal(ai) = false (trickno)");

  // bool low_p;  
  if (this->low_p != ai.low_p)
    DEBUG_RETURNING(false,
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::isequal(ai) = false (low)");

  // Card::Color meatlesscolor_p;  
  if (this->meatlesscolor_p != ai.meatlesscolor_p)
    DEBUG_RETURNING(false,
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::isequal(ai) = false (meatlesscolor)");

  // unsigned last_trick_to_calculate_p;  
  if (this->last_trick_to_calculate_p != ai.last_trick_to_calculate_p)
    DEBUG_RETURNING(false,
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::isequal(ai) = false (last_trick_to_calculate)");

  // Aiconfig::TypeBool lastHeuristic_;  
  if (this->lastHeuristic_ != ai.lastHeuristic_)
    DEBUG_RETURNING(false,
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::isequal(ai) = false (lastHeuristic)");

#if 0
  vector<Trick> trick_p; // last played tricks
#endif

  DEBUG_RETURNING(true,
		  INFO_AI && INFO_OTHER_FUNCTION,
		  "Ai::isequal(ai) = true");
} // virtual bool Ai::isequal(Ai const& ai) const

/**********************************************************************
 *
 ** Ai::sethand(Hand h, bool const playedcards_update = true)
 *
 ** Parameters:  h                       - new hand for this player
 *
 ** Result:      -
 *
 ** Version:     0.4.2
 *
 ** Description: creation of a new hand for this player with all cards of h
 **              and memorizing of all this cards for not playable by other 
 **              players in the field playedcards_p 
 *
 **********************************************************************/
Hand const&
Ai::hand_set(const Hand& h)
{
  DEBUG_CALLING(INFO_AI && INFO_VALUE,
		"Ai::hand_set(const Hand& h)");

  DEBUG_RETURNING(this->hand_set(h, true),
		  INFO_AI && INFO_VALUE,
		  "Ai::hand_set(const Hand& h)");
} // Hand const& Ai::hand_set(const Hand& h)

/**
 **
 ** Ai::hand_set(Hand h, bool const playedcards_update = true)
 **
 ** Parameters:  h                       - new hand for this player
 **              playedcards_update      - whether to update the played cards
 **
 ** Result:      -
 **
 ** Version:     0.4.2
 **
 ** Description: creation of a new hand for this player with all cards of h
 **              and memorizing of all this cards for not playable by other 
 **              players in the field playedcards_p 
 **
 **/
Hand const&
Ai::hand_set(const Hand& h, bool const playedcards_update)
{
  DEBUG_CALLING(INFO_AI && INFO_VALUE,
		"Ai::hand_set(const Hand& h)");

  Player::hand_set(h);
  if (playedcards_update)
  {
    unsigned x,y;
    // first reset
    for (x = 0; x < this->game().rule()(Rule::NUMBER_OF_CARD_COLORS) ; x++ )
      for (y = 0; y < this->game().rule()(Rule::NUMBER_OF_CARD_VALUES); y++ )
      {
	this->playedcards_p[Card(x, y)] = 0;
      }

    // GameTree does need this
    // DEBUG_ASSERT(!this->game().isvirtual());

    // take new hand in account
    // if the game is virtual, the hand can contain more than 12 cards,
    // so they aren't taken into account
    if (!this->game().isvirtual()) {
      for (unsigned i = 0; i < h.cardsnumber_all(); i++)
	this->playedcards_p[h.card_all(i)] += 1;
    }

    this->trickno_p = 0;
    // take already played tricks into account
    for (vector<Trick>::const_iterator
	 trick = this->game().trick_begin();
	 trick != this->game().trick_end();
	 trick++)
      if (trick->isfull()) {
	this->updateplayedcards(*trick);
	// ToDo: trick remember
	if (this->game().isvirtual()) {
	  playedcards_p[trick->card_of_player(*this)] += 1;
	}
      }
  }

  DEBUG_RETURNING(this->hand(),
		  INFO_AI && INFO_VALUE,
		  "Ai::hand_set(const Hand& h)");
} // virtual Hand const& Ai::hand_set(const Hand& h, bool const playedcards_update = true)


/**********************************************************************
 *
 **    Hand Ai::hand(player)
 *
 **    Parameters:  player
 *
 **    Result: hand of player  depending on fairplayhands
 *
 **    Version: Alpha
 *
 **    Description: 
 **       
 *
 **********************************************************************/
Hand
Ai::handofplayer( Player const& player ) const throw(InvalidGameException)
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION, "Ai::handofplayer()");

  if ( player.no() == this->no() )
    DEBUG_RETURNING(this->hand(),
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::handofplayer()");

  // If it is the last trick and the player has already played a card,
  // his hand is empty.
  if ((this->game().tricks_remaining_no() == 1)
      && (this->game().trick_current().cardno_of_player(player)
	  < this->game().trick_current().actcardno())) {
    DEBUG_RETURNING(Hand(),
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::handofplayer()");
  }

  if (this->Aiconfig::value(Aiconfig::FAIRPLAYHANDS))
  {
    unsigned x,y;

    vector<Card> c;

    unsigned noTrumps = 0;
    if( this->game().type() == GAMETYPE::POVERTY )
    {
      for (x = 0; x < this->game().rule()(Rule::NUMBER_OF_CARD_COLORS) ; x++ )
	for (y = 0; y < this->game().rule()(Rule::NUMBER_OF_CARD_VALUES); y++ )
	{
	  if (playedcard(Card( x, y )) < 2)
	    noTrumps+= 2 - playedcard(Card( x, y ));
	}
    }

    bool noTrumpsAllowed = noTrumps < this->game().tricks_remaining_no();


    for (x = 0; x < this->game().rule()(Rule::NUMBER_OF_CARD_COLORS) ; x++ )
      for (y = 0; y < this->game().rule()(Rule::NUMBER_OF_CARD_VALUES); y++ )
      {
	if (playedcard(Card( x, y )) < this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) {
	  if( ( Card( x, y ) == Card( Card::CLUB, Card:: QUEEN ) )
	      && this->teamofplayer( player ) == TEAM::CONTRA 
	      && !GAMETYPE::is_solo( this->game().type() )  ) // contra has no club queen
	    continue;
	  if ( !Card(x, y).istrump()
	       && colorjabbed_p[ x ][player.no()] ) {
	    continue; // player has jabbed so he has this colors no longer
	  }
	  if (( this->trumpless_[player.no()] )
	      && Card(x, y).istrump()) {
	    continue; // player has no trumps
	  }
	  if( ( Card( x, y ) == Card( Card::CLUB, Card:: QUEEN ) )
	      && this->game().type() == GAMETYPE::MARRIAGE 
	      && player != this->game().soloplayer() ) {
	    continue; // only marriage has club queens
	  }
	  if( Card(x, y ).isswine() &&
	      (player.no() != this->game().swines_owner()->no()) ) {
	    continue;
	  }
	  if( Card(x, y ).ishyperswine() &&
	      player.no() != this->game().hyperswines_owner()->no() ) {
	    continue;
	  }
	  if( this->game().type() == GAMETYPE::POVERTY )
	  {
	    if( !Card( x, y ).istrump() &&
		player.no() == this->game().povertyPartner().no() )
	      continue; // heuristic poverty partner has no trumps.
	    if( Card( x, y ).istrump() &&
		player.no() == this->game().soloplayer().no() ||
		noTrumpsAllowed )
	      continue; // heuristic player with poverty has no trumps.
	  } 


	  for (unsigned n = this->game().rule()(Rule::NUMBER_OF_SAME_CARDS);
	       n > playedcard(Card( x, y ));
	       n--)
	    c.push_back(Card(x, y));
	}
      }

    // test whether there are enough cards on the hand to play all remaining tricks
    if ((c.empty())
	|| (c.size() 
	    + ( (this->game().trick_current().cardno_of_player(player)
		 < this->game().trick_current().actcardno())
		? 1
		: 0)
	    < this->game().tricks_remaining_no()))
      DEBUG_THROW( InvalidGameException, InvalidGameException() );

    DEBUG_RETURNING(Hand(c),
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::handofplayer()");
  } else { // if !(this->Aiconfig::value(Aiconfig::FAIRPLAYHANDS))
    DEBUG_RETURNING(player.hand(),
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::handofplayer()");
  } // if !(this->Aiconfig::value(Aiconfig::FAIRPLAYHANDS))


  // ***
  // problem with the compiler :-(

  DEBUG_ASSERTION(false,
		  "Ai::handofplayer(pl):\n"
		  "  control is after if-else-'DEBUG_RETURNING'");

  DEBUG_RETURNING(Hand(),
		  INFO_AI && INFO_OTHER_FUNCTION,
		  "Ai::handofplayer()");
} // Hand Ai::handofplayer(unsigned pl) const

/**********************************************************************
 *
 **    Team team(const unsigned i) const; 
 ** 
 *
 **    Parameters:  player
 *
 **    Result: team of player depending on fairplayteams
 *
 **    Version: Alpha
 *
 **    Description: 
 **       
 *
 **********************************************************************/

Team Ai::teamofplayer(Player const& player) const

{
  DEBUG_CALLING(INFO_AI && INFO_VALUE,
		"Ai::team(i)");

  if (this->Aiconfig::value(Aiconfig::FAIRPLAYTEAMS)
    || (this->game().type() == GAMETYPE::MARRIAGE))
  {  
    // in the marriage case, as long as the teams are not determined, the player
    // shall think that every other player is against him
    DEBUG_RETURNING(this->teaminfo( player ),
		    INFO_AI && INFO_VALUE,
		    "Ai::team( player )");
  } else
  {
    DEBUG_RETURNING( player.team(),
		     INFO_AI && INFO_VALUE,
		     "Ai::team( player )");
  }
}

/**
 **
 ** the trick is full
 **
 ** @param      trick   full trick
 **
 ** @return     -
 **
 ** @version    0.4.5
 **
 ** @author     Diether Knof
 **
 **/
void
Ai::trick_full(Trick const& trick)
{ 
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,
		"Ai::trick_full(trick)");

  DEBUG_ASSERT( trick.isfull() );
  if( trick.startcard().istrump() )
  {
    for ( unsigned i = 0; i < trick.actcardno() ; i++)
    {
      if (!trick.card(i).istrump())
      {
	DEBUG_ASSERTION((this->trumpless_.size()
			 > trick.player_of_card(i).no()),
			"Ai::trick_full(trick):\n"
			"  'this->trumpless_' is too small: "
			<< this->trumpless_.size() << " <= "
			<< trick.player_of_card(i).no());
	this->trumpless_[ trick.player_of_card(i).no() ] = true;
      }
    }
  }

  this->updateplayedcards(trick);

  DEBUG_RETURNING(VOID,
		  INFO_AI && INFO_GAMEPLAY,
		  "Ai::trick_full(trick)");
} // void Ai::trick_full(Trick const& trick)


/**********************************************************************
 *
 **    void Ai::updateplayedcards(Trick t)
 *
 **    Parameters: last played trick 
 *
 **    Result: 
 *
 **    Version: Alpha
 *
 **    Description: 
 **      memorizing of all the cards of t for not playable by other 
 **      players in the field playedcards_p 
 *
 *
 **********************************************************************/

void Ai::updateplayedcards(const Trick& t) throw(InvalidGameException)
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,"Ai::updateplayedcards(const Trick& t)");

  unsigned i;
  Trick tr;
  Card::Color jabbed=Card::NOCARDCOLOR;

  if (!t.startcard().istrump())
  {
    jabbed=t.startcard().color();
    color_runs_p[jabbed]++;
  }

  // count all played cards of the other players
  for ( i = 0; i < t.actcardno() ; i++)
  {
    if (t.player_of_card(i).no() != this->no())
      playedcards_p[t.card(i)] += 1;

    if (jabbed!=Card::NOCARDCOLOR && t.card(i).istrump())
    {
      colorjabbed_p[jabbed][t.player_of_card(i).no()]=true;
    }  
  }

  trick_p[trickno_p]=t;

  trickno_p++;

  // the ai knows too much let's remove the oldest trick(s)
  while(trickno_p > this->Aiconfig::value(Aiconfig::REMEMBERTRICKS))
  {
    tr=trick_p[0];

    // lets forget this trick
    for( i = 0 ; i < tr.actcardno(); i++)
    {
      if (!tr.card(i).isdolle()        || 
	  !tr.card(i).isswine()      ||
	  !tr.card(i).ishyperswine() )
      {

	if( !this->game().isvirtual() ) {
	  if(playedcard(tr.card(i)) == 0)
	    COUT << playedcard(tr.card(i)) << endl;
	  DEBUG_ASSERT(playedcard(tr.card(i)) > 0);
	}

	if (playedcards_p[tr.card(i)] <= 0)
	  DEBUG_THROW( InvalidGameException, InvalidGameException() );
	playedcards_p[tr.card(i)] -= 1;
      }
    }

    // remove this trick from list
    for ( i = 0; i < trickno_p-1; i++)
      trick_p[i]=trick_p[i+1];

    trickno_p--;
  }     
#if 1
  { // test the playedcardsnumber
    unsigned no = 0;
    for (unsigned x = 0; x < this->game().rule()(Rule::NUMBER_OF_CARD_COLORS) ; x++ )
      for (unsigned y = 0; y < this->game().rule()(Rule::NUMBER_OF_CARD_VALUES); y++ ) {
	no += this->playedcards_p[Card(x, y)];
      }

    DEBUG_ASSERTION((no <= (this->game().trick_current_no() + 1) * 4
		     + this->game().tricks_remaining_no()),
		    "Ai::updateplayedcards(trick):\n"
		    "  too many played cards: " << no
		    << ", current trick no: " << this->game().trick_current_no());
  } // test the playedcardsnumber
#endif

  DEBUG_RETURNING(VOID,
		  INFO_AI && INFO_OTHER_FUNCTION,
		  "Ai::updateplayedcards(const Trick& t)");
} // void Ai::updateplayedcards(const Trick& t)


/**********************************************************************
 *
 **    Card Ai::nextcard(Trick t) 
 *
 **    Parameters:  
 **        t    : actual trick
 **        h    : hands of all players in order of players in this trick
 *
 **    Result: nextcard played by ai
 *
 **    Version: Alpha
 *
 **    Description:
 **      uses heuristics and choosebestcard
 *
 **********************************************************************/
Card Ai::nextcard(const Trick& t)
{
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,"Ai::nextcard(const Trick& t, const Hand* const h)");

  DEBUG_RETURNING(nextcard(t,true),INFO_AI && INFO_GAMEPLAY,"Ai::nextcard(const Trick& t, const Hand* const h)");
}

/**********************************************************************
 *
 ** Card Ai::card_get()
 *
 ** Parameters:  -
 *
 ** Result:      card played by the ai
 *
 ** Version:     0.4.4
 *
 ** Description: returns the card, the ai wishes to play
 *
 **********************************************************************/
Card
Ai::card_get()
{
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,
		"Ai::card_get()");

  DEBUG_RETURNING(this->nextcard(this->game().trick_current(), true),
		  INFO_AI && INFO_GAMEPLAY,
		  "Ai::card_get()");
} // Card Ai::card_get() const

/**
 **
 ** 'player' has played the card 'card'
 **
 ** @param       card    played card
 ** @param       player  player who has played the card
 **
 ** @return      -
 **
 ** @author      Borg Enders
 ** @author      Diether Knof
 **
 ** @version     0.6.3
 **
 **/
void
Ai::card_played(Card const& card, Player const& player)
{ 
  DEBUG_CALLING(INFO_PLAYER && INFO_VALUE,
		"Ai::card_played(card, player)");

  this->teaminfo_update();

  DEBUG_RETURNING(VOID,
		  INFO_PLAYER && INFO_VALUE,
		  "Ai::card_played(card, player)");
} // void Ai::card_played(Card const& card, Player const& player)

/**********************************************************************
 *
 **    Card Ai::nextcard(Trick t,Hand *h,bool output) 
 *
 **    Parameters:  
 **        t      : actual trick
 **        h      : hands of all players in order of players in this trick
 **        output : if this routine should produce output (used for debugging)
 *
 **    Result: nextcard played by ai
 *
 **    Version: Alpha
 *
 **    Description:
 **      uses heuristics and choosebestcard
 *
 **********************************************************************/
Card
Ai::nextcard(const Trick& t,bool output)
{
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,
		"Ai::nextcard()");

  output = (output && (&(this->game()) == &(::party.game())));

  Hand const& han = this->hand().validcards(t);
  DEBUG_ASSERTION((han.cardsnumber() > 0),
		  "Ai::nextcard(Trick, output):\n"
		  "  found no valid card."
		  "Trick: " << t
		  << "Hand: " << this->hand());
#ifndef RELEASE
  if( output )
    db()->heuristicInc( Aiconfig::NO_HEURISTIC );
#endif
  // if there is only one valid card that's it
  if (han.cardsnumber()==1)
  {
    this->lastHeuristic_ = Aiconfig::ONLY_VALID_CARD;
    if (HEURISTIC_OUTPUT && output)
      cout<<no()<<" only one valid card "<<han.card(0)<<endl;
    DEBUG_RETURNING(han.card(0),
		    INFO_AI && INFO_GAMEPLAY,
		    "Ai::nextcard() -- one card");
  }

  GameType gt=game().type();

  if ( silentMarriage_ ||
  ( (gt==GAMETYPE::SOLO_CLUB) ||
	(gt==GAMETYPE::SOLO_HEART) ||
	(gt==GAMETYPE::SOLO_SPADE) ||
	(gt==GAMETYPE::SOLO_DIAMOND))
      && ( *this == game().soloplayer()))
  {
    DEBUG_RETURNING(nextcard_colorsolo(t,output),
		    INFO_AI && INFO_GAMEPLAY,
		    "Ai::nextcard() -- colorsolo");
  }

  if ((gt==GAMETYPE::SOLO_MEATLESS) && t.isstartcard())
  {
    DEBUG_RETURNING(nextcard_meatless(t,output),
		    INFO_AI && INFO_GAMEPLAY,
		    "Ai::nextcard() -- meatless");
  }


  if ( ( (gt==GAMETYPE::SOLO_JACK)  ||
	 (gt==GAMETYPE::SOLO_QUEEN) ||
	 (gt==GAMETYPE::SOLO_KING)  ||
	 (gt==GAMETYPE::SOLO_QUEEN_JACK)      ||
	 (gt==GAMETYPE::SOLO_KING_JACK)       ||
	 (gt==GAMETYPE::SOLO_KING_QUEEN)      ||
	 (gt==GAMETYPE::SOLO_KOEHLER)         )
       && ( *this == game().soloplayer()))
  {
    DEBUG_RETURNING(nextcard_picturesolo(t,output),
		    INFO_AI && INFO_GAMEPLAY,
		    "Ai::nextcard() -- picturesolo");
  }


  Hand ha;
  Card c=Card();
  if ( this->game().trick_current_no() < 
       this->Aiconfig::value(Aiconfig::LASTHEURISTICTRICK ) )
  {
    if (this->Aiconfig::value(Aiconfig::CHOOSE_ACE))
    {
      c=Heuristics::choose_ace(t,*this);
      if (!(c==Card())) {
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::choose_ace "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::CHOOSE_ACE;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::CHOOSE_ACE );    
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- choose_ace");
      }
    }


    if (this->Aiconfig::value(Aiconfig::CHOOSE_FOR_COLOR_TRICK) &&
	!(gt==GAMETYPE::SOLO_MEATLESS))
    {

      c=Heuristics::choose_for_color_trick(t,*this); 

      if (!(c==Card()))
      {
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::choose_for_color_trick "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::CHOOSE_FOR_COLOR_TRICK;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::CHOOSE_FOR_COLOR_TRICK );
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- choose_for_color_trick");
      }
    }

    if (     this->Aiconfig::value(Aiconfig::CHOOSE_PFUND) 
	     && ( t.islastcard() || gt!=GAMETYPE::POVERTY  
		  || *this != game().soloplayer()))
    {
      c=Heuristics::choose_pfund(t,*this);
      if (!(c==Card()))
      {
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::choose_pfund "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::CHOOSE_PFUND;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::CHOOSE_PFUND );
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- choose_pfund");
      }
    }

    if (this->Aiconfig::value(Aiconfig::JAB_FOX))
    {
      c=Heuristics::jab_fox(t,*this);
      if (!(c==Card()))
      {
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::jab_fox "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::JAB_FOX;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::JAB_FOX );
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- jab_fox");
      }
    }

    if (this->Aiconfig::value(Aiconfig::JAB_FOR_ACE))
    {
      c=Heuristics::jab_for_ace(t,*this);
      if (!(c==Card())){
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::jab_for_ace "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::JAB_FOR_ACE;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::JAB_FOR_ACE );
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- jab_for_ace");
      }
    }

    if (this->Aiconfig::value(Aiconfig::PLAY_FOR_TEAM) 
	&& ( gt!=GAMETYPE::POVERTY  
	     || *this != game().soloplayer()))
    {
      c=Heuristics::play_for_team(t,*this);
      if (!(c==Card())){
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::play_for_team "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::PLAY_FOR_TEAM;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::PLAY_FOR_TEAM );
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- play_for_team");
      }
    }


    if (this->Aiconfig::value(Aiconfig::BEST_WINNING))
    {
      if (maybe_to_team(teaminfo(t.winnerplayer()))!=team())
      {
	c=Heuristics::best_winning_card(t,*this);
	if (!(c==Card())){
	  if (HEURISTIC_OUTPUT && output)
	    cout<<no()<<" Heuristics::best_winning_card "<<c<<endl;
	  DEBUG_ASSERT(c);    
	  if( output)
	  {
	    lastHeuristic_ = Aiconfig::BEST_WINNING;
#ifndef RELEASE
	    db()->heuristicInc( Aiconfig::BEST_WINNING );
#endif
	  }
	  DEBUG_RETURNING(c,
			  INFO_AI && INFO_GAMEPLAY,
			  "Ai::nextcard() -- best_winner_card");
	}
      }
    }


    if (this->Aiconfig::value(Aiconfig::CREATE_FEHL))
    {
      c=Heuristics::create_fehl(t,*this);
      if (c){
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::create_fehl "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::CREATE_FEHL;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::CREATE_FEHL );
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- create_fehl");
      }
    }

    if ( this->Aiconfig::value(Aiconfig::LOW_HIGH)  
	 && ( gt!=GAMETYPE::POVERTY  
	      || *this == game().povertyPartner()))
    {
      c=Heuristics::play_low_high(t,*this);
      if (!(c==Card())){
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::play_low_high("<<next_low()<<") "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  check_low_high(c);	
	  lastHeuristic_ = Aiconfig::LOW_HIGH;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::LOW_HIGH );
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- play_low_high");
      }
    }

    if (this->Aiconfig::value(Aiconfig::DRAW_TRUMP))
    {
      c=Heuristics::draw_trump(t,*this);
      if (c){
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::draw_trump "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::DRAW_TRUMP;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::DRAW_TRUMP );
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- draw_trump");
      }
    }

  } // actTrick < LASTHEURISTICTRICK
  c = Weighting(this->aitype()).choosebestcard(*this,t,no(),output);

  if (HEURISTIC_OUTPUT && output)
    cout<<no()<<" Heuristics ChooseBestCard "
      << "(" << this->aitype() << ") "
      << c <<endl;

  if (this->aitype() != AITYPE::NO_CHOOSEBESTCARD)
    DEBUG_ASSERT(c);

  if( output)
  {
    lastHeuristic_ = Aiconfig::CHOOSE_BEST_CARD;
#ifndef RELEASE
    db()->heuristicInc( Aiconfig::CHOOSE_BEST_CARD );
#endif
  }
  DEBUG_RETURNING(c,
		  INFO_AI && INFO_GAMEPLAY,
		  "Ai::nextcard() -- check_card");
} // Card Ai::nextcard(,,)


/**********************************************************************
 *
 **    Card Ai::nextcard_colorsolo(Trick t,Hand *h) 
 *
 **    Parameters:  
 **        t    : actual trick
 **        h    : hands of all players in order of players in this trick
 *
 **    Result: nextcard played by ai fo soo player of color solo
 *
 **    Version: Alpha
 *
 **    Description:
 **      uses heuristics and choosebestcard
 *
 **********************************************************************/

Card Ai::nextcard_colorsolo(const Trick& t,bool output)
{
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,"Ai::nextcard_colorsolo()");

  Hand ha;
  Card c=Card();
  Weighting w = Weighting(aitype());

  if ( this->game().trick_current_no() < 
       this->Aiconfig::value(Aiconfig::LASTHEURISTICTRICK ) )
  {
    if (this->Aiconfig::value(Aiconfig::COLOR_CHOOSE_ACE))
    {
      c=Heuristics::choose_ace(t,*this);
      if (!(c==Card())) {
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::choose_ace "<<c<<endl;
#ifdef INFO_FUNCTION_AI
	info_ostr << "Ai::nextcard_colorsolo() finished:" <<c<< endl;
#endif
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::COLOR_CHOOSE_ACE;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::COLOR_CHOOSE_ACE );
#endif
	}
	DEBUG_RETURNING(c,INFO_AI && INFO_GAMEPLAY,"Ai::nextcard_colorsolo()");  
      }
    }

    if (this->Aiconfig::value(Aiconfig::COLOR_BEST_WINNING))
    {
      if (maybe_to_team(teaminfo(t.winnerplayer()))!=team())
      {
	c=Heuristics::best_winning_card(t,*this);
	if (!(c==Card())){
	  if (HEURISTIC_OUTPUT && output)
	    cout<<no()<<" Heuristics::best_winning_card "<<c<<endl;
#ifdef INFO_FUNCTION_AI
	  info_ostr << "Ai::nextcard_colorsolo() finished:" <<c<< endl;
#endif
	  DEBUG_ASSERT(c);
	  if( output)
	  {
	    lastHeuristic_ = Aiconfig::COLOR_BEST_WINNING;
#ifndef RELEASE
	    db()->heuristicInc( Aiconfig::COLOR_BEST_WINNING );
#endif
	  }
	  DEBUG_RETURNING(c,INFO_AI && INFO_GAMEPLAY,"Ai::nextcard_colorsolo()");
	}
      }
    }

    if (this->Aiconfig::value(Aiconfig::COLOR_JAB_FOR_ACE))
    {
      c=Heuristics::jab_for_ace(t,*this);
      if (!(c==Card())){
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::jab_for_ace "<<c<<endl;
#ifdef INFO_FUNCTION_AI
	info_ostr << "Ai::nextcard_colorsolo() finished:" <<c<< endl;
#endif
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::COLOR_JAB_FOR_ACE;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::COLOR_JAB_FOR_ACE );
#endif
	}
	DEBUG_RETURNING(c,INFO_AI && INFO_GAMEPLAY,"Ai::nextcard_colorsolo()");
      }
    }

    if (this->Aiconfig::value(Aiconfig::LOW_HIGH))
    {
      c=Heuristics::play_low_high(t,*this);
      if (!(c==Card())){
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::play_low_high("<<next_low()<<") "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  check_low_high(c);
	  lastHeuristic_ = Aiconfig::LOW_HIGH;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::LOW_HIGH );
#endif
	}
	DEBUG_RETURNING(c,INFO_AI && INFO_GAMEPLAY,"Ai::nextcard_colorsolo()");
      }
    }

    if (this->Aiconfig::value(Aiconfig::DRAW_TRUMP))
    {
      c=Heuristics::draw_trump( t, *this );
      if (c){
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::draw_trump "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::DRAW_TRUMP;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::DRAW_TRUMP );
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- draw_trump");
      }
    }

  } //actTrick < LASTHEURISTICTRICK  
  c=w.choosebestcard(*this,t,no(),output);
  if (HEURISTIC_OUTPUT && output)
    cout<<no()<<" Heuristics ChooseBestCard "<<c<<endl;

  if (this->aitype() != AITYPE::NO_CHOOSEBESTCARD)
    DEBUG_ASSERT(c);

  if( output)
  {
    lastHeuristic_ = Aiconfig::CHOOSE_BEST_CARD;
#ifndef RELEASE
    db()->heuristicInc( Aiconfig::CHOOSE_BEST_CARD );
#endif
  }
  DEBUG_RETURNING(c,INFO_AI && INFO_GAMEPLAY,"Ai::nextcard_colorsolo()");
}


/**********************************************************************
 *
 **    int Ai::calculate_meatless_color( Color c)
 *
 **    Parameters:  
 **        c color to calculate
 *
 **    Result: value of card 
 *
 **    Version: 0.6.1
 *
 **    Description:
 **      
 *
 **********************************************************************/
int Ai::calculate_meatless_color( Card::Color co )
{
  int modi = 0;
  if ( playedcard(Card(co,Card::ACE)) < 2 ) 
    modi = -1;
  if ( playedcard(Card(co,Card::ACE)) == 2 ) 
    modi = 1;

  return 	(int)hand().numberof(co,GAMETYPE::SOLO_MEATLESS)
    * (int)hand().numberof(Card(co,Card::ACE))
    +  (int)hand().numberof(co,GAMETYPE::SOLO_MEATLESS)
    * (int)hand().numberof(Card(co,Card::TEN)) 
    * modi;
}
/**********************************************************************
 *
 **    Card Ai::nextcard_meatless(Trick t,Hand *h) 
 *
 **    Parameters:  
 **        t    : actual trick
 **        h    : hands of all players in order of players in this trick
 *
 **    Result: nextcard played by ai for meatless
 *
 **    Version: Alpha
 *
 **    Description:
 **      uses heuristics and choosebestcard
 *
 **********************************************************************/

Card Ai::nextcard_meatless(const Trick& t,bool output)
{
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,"Ai::nextcard_colorsolo()");

  Hand ha;
  Card c=Card();
  Weighting w = Weighting(aitype());
  int maxcards=0;
  if ( this->game().trick_current_no() < 
       this->Aiconfig::value(Aiconfig::LASTHEURISTICTRICK ) )
  {  
    if (meatlesscolor_p==Card::NOCARDCOLOR ||
	(hand().numberof( meatlesscolor_p, Card::ACE ) == 0 &&
	 playedcard( Card( meatlesscolor_p, Card::ACE )) < 2 ))
    {
      Card::Color co=Card::DIAMOND;

      maxcards=calculate_meatless_color(co);
      meatlesscolor_p=co;

      co=Card::HEART;

      if (calculate_meatless_color(co)>maxcards)
      {
	maxcards=calculate_meatless_color(co);
	meatlesscolor_p=co;
      }

      co=Card::SPADE;

      if (calculate_meatless_color(co)>maxcards)
      {      	
	maxcards=calculate_meatless_color(co);
	meatlesscolor_p=co;
      }

      co=Card::CLUB;

      if (calculate_meatless_color(co)>maxcards)
      {

	maxcards= calculate_meatless_color( co );
	meatlesscolor_p=co;
      }
    } 
    if (this->Aiconfig::value(Aiconfig::MEATLESS_PLAYHIGHESTCOLOR))
    {    
      c=Heuristics::play_highest_color(t,*this,meatlesscolor_p);
      if (!(c==Card())){
	if( output)
	{
	  lastHeuristic_ = Aiconfig::MEATLESS_PLAYHIGHESTCOLOR;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::MEATLESS_PLAYHIGHESTCOLOR );
#endif
	}
	DEBUG_RETURNING(c,INFO_AI && INFO_GAMEPLAY,"Ai::nextcard_colorsolo()");
      }
    }
  } //actTRick < LASTHEURISTUCTRICK

  c=w.choosebestcard(*this,t,no(),output);
  if (HEURISTIC_OUTPUT && output)
    cout<<no()<<" Heuristics ChooseBestCard "<<c<<endl;

  if (this->aitype() != AITYPE::NO_CHOOSEBESTCARD)
    DEBUG_ASSERT(c);
  if( output)
  {
    lastHeuristic_ = Aiconfig::CHOOSE_BEST_CARD;
#ifndef RELEASE
    db()->heuristicInc( Aiconfig::CHOOSE_BEST_CARD );
#endif
  }
  DEBUG_RETURNING(c,INFO_AI && INFO_GAMEPLAY,"Ai::nextcard_colorsolo()");


}

/**********************************************************************
 *
 **    Card Ai::nextcard_picturesolo(Trick t,Hand *h) 
 *
 **    Parameters:  
 **        t    : actual trick
 **        h    : hands of all players in order of players in this trick
 *
 **    Result: nextcard played by ai fo soo player of color solo
 *
 **    Version: Alpha
 *
 **    Description:
 **      uses heuristics and choosebestcard
 *
 **********************************************************************/

Card Ai::nextcard_picturesolo(const Trick& t,bool output)
{
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,
		"Ai::nextcard_picturesolo()");

  if (t.isstartcard() && single_ace())
  {
    DEBUG_RETURNING(nextcard_colorsolo(t,output),
		    INFO_AI && INFO_GAMEPLAY,
		    "Ai::nextcard_picturesolo()");
  }

  if ( this->game().trick_current_no() < 
       this->Aiconfig::value(Aiconfig::LASTHEURISTICTRICK ) )
  {
    if (this->Aiconfig::value(Aiconfig::DRAW_TRUMP))
    {
      Card c=Heuristics::draw_trump(t,*this);
      if (c){
	if (HEURISTIC_OUTPUT && output)
	  cout<<no()<<" Heuristics::draw_trump "<<c<<endl;
	DEBUG_ASSERT(c);
	if( output)
	{
	  lastHeuristic_ = Aiconfig::DRAW_TRUMP;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::DRAW_TRUMP );
#endif
	}
	DEBUG_RETURNING(c,
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard() -- draw_trump");
      }
    }
    if (this->Aiconfig::value(Aiconfig::PICTURE_SECONDBESTTRUMP))
    {
      if (remaining_trumps()>0 && t.isstartcard() && hand().numberoftrumps()>1)
      {
	if( output)
	{
	  lastHeuristic_ = Aiconfig::PICTURE_SECONDBESTTRUMP;
#ifndef RELEASE
	  db()->heuristicInc( Aiconfig::PICTURE_SECONDBESTTRUMP );
#endif
	}
	// Bug: 'Heuristics::SecondBestTrump' can return an empty card (if there are not two trump cards on the hand)
	DEBUG_RETURNING(Heuristics::SecondBestTrump(*this),
			INFO_AI && INFO_GAMEPLAY,
			"Ai::nextcard_picturesolo()");
      }
    }
  }// actTrick < HEURISTICLASTTRICK  

  DEBUG_RETURNING(nextcard_colorsolo(t,output),
		  INFO_AI && INFO_GAMEPLAY,
		  "Ai::nextcard_picturesolo()");
}

/**********************************************************************
 *
 ** unsigned Ai::playedcard(Card const& c)
 *
 ** Parameters:  c - card for which number of known already playedcards
 *
 ** Result:      number of played cards of c
 *
 ** Version:     0.4.2
 *
 ** Description: -> result
 *
 **********************************************************************/
unsigned
Ai::playedcard(Card const& c) const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,
		"Ai::playedcard(Card)");

#ifdef POSTPHONED
  DEBUG_RETURNING(this->playedcards_p[c],
		  INFO_AI && INFO_OTHER_FUNCTION,
		  "Ai::playedcard(Card)");
#else
  DEBUG_RETURNING(this->playedcards_p.find(c)->second,
		  INFO_AI && INFO_OTHER_FUNCTION,
		  "Ai::playedcard(Card)");
#endif
} // unsigned Ai::playedcard(Card const& c) const


/**********************************************************************
 *
 **    bool Ai::colorjabbed(Card::Color c)
 *
 **    Parameters: color of for which should be known if it was jabbed
 *
 **    Result: true if color was jabbed and c is legal
 **            false otherwise
 *
 **    Version: Alpha
 *
 **    Description:
 *
 *
 **********************************************************************/
bool
Ai::colorjabbed(const Card::Color c) const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,"Ai::colorjabbed(const Card::Color c)");

  bool result=false;
  if (!(c==Card::NOCARDCOLOR)) 
    for(unsigned i=0; i<game().playerno();i++) {
      result=result||colorjabbed_p[c][i];
    }

  DEBUG_RETURNING(result,INFO_AI && INFO_OTHER_FUNCTION,"Ai::colorjabbed(const Card::Color c)");
}


/**********************************************************************
 *
 **    bool Ai::jabbedbyownteam(Card::Color c)
 *
 **    Parameters: color of for which should be known if it was jabbed by own team
 *
 **    Result: true if color was jabbed aby own team nd c is legal
 **            false otherwise
 *
 **    Version: Alpha
 *
 **    Description:
 *
 *
 **********************************************************************/

bool
Ai::jabbedbyownteam(const Card::Color c) const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,"Ai::jabbedbyownteam()");

  bool res=false;

  if (!(c==Card::NOCARDCOLOR)) 
    for(unsigned i=0; i<game().playerno();i++) {
      if (i!=no() && maybe_to_team(teaminfo( game().player( i )))==team())
	res=res||colorjabbed_p[c][i];
    }

  DEBUG_RETURNING(res,INFO_AI && INFO_OTHER_FUNCTION,"Ai::jabbedbyownteam()");


}

/**********************************************************************
 *
 **    bool Ai::jabbedbyownteam(Card::Color c)
 *
 **    Parameters: color of for which should be known if it was jabbed by own team
 *
 **    Result: true if color was jabbed aby own team nd c is legal
 **            false otherwise
 *
 **    Version: Alpha
 *
 **    Description:
 *
 *
 **********************************************************************/

bool
Ai::jabbedbyotherteam(const Card::Color c) const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,"Ai::jabbedbyownteam()");

  bool res=false;

  if (!(c==Card::NOCARDCOLOR)) 
    for(unsigned i=0; i<game().playerno();i++) {
      if (i!=no() && maybe_to_team(teaminfo( game().player( i )))!=team())
	res=res||colorjabbed_p[c][i];
    }

  DEBUG_RETURNING(res,INFO_AI && INFO_OTHER_FUNCTION,"Ai::jabbedbyownteam()");


}

/**********************************************************************
 *
 **    int Ai::color_runs(Card::Color c)
 *
 **    Parameters: color of for which should be known how many times played
 *
 **    Result: number of color runs otherwise infinity
 *
 **    Version: Alpha
 *
 **    Description:
 *
 *
 **********************************************************************/
unsigned
Ai::color_runs(const Card::Color c) const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,
		"Ai::color_runs(const Card::Color c)");

  if (c != Card::NOCARDCOLOR)
    DEBUG_RETURNING(color_runs_p[c],
		    INFO_AI && INFO_OTHER_FUNCTION,
		    "Ai::color_runs(const Card::Color c)");

  DEBUG_RETURNING(UINT_MAX,
		  INFO_AI && INFO_OTHER_FUNCTION,
		  "Ai::color_runs(const Card::Color c)");
}


/**********************************************************************
 *
 **     bool Ai::next_low()
 *
 **    Parameters: 
 *
 **    Result: returns true if next card should be low trump otherwise false
 *
 **    Version: Alpha
 *
 **    Description:
 *
 *
 **********************************************************************/

bool Ai::next_low() const
{
  DEBUG_CALLING(INFO_AI && INFO_VALUE, "Ai::next_low()");
  DEBUG_RETURNING(low_p,INFO_AI && INFO_VALUE, "Ai::next_low()");
}


/**********************************************************************
 *
 **     void Ai::check_low_high(Card c)
 *
 **    Parameters: next palyed card
 *
 **    Result: 
 *
 **    Version: Alpha
 *
 **    Description:
 **      sets low_p to true if c>=DIAMOND QUEEN to false if card<DIAMOND QUENN
 *
 **********************************************************************/ 
void Ai::check_low_high(const Card c)
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,"Ai::check_low_high(Card c)");
  if ( c.istrump()
       && !(c.less(Card(Card::DIAMOND,Card::QUEEN), this->game())))
  {
    low_p = true;
  } else
  {
    low_p = false;
  }
  DEBUG_RETURNING(VOID,
		  INFO_AI && INFO_OTHER_FUNCTION,
		  "Ai::check_low_high(Card c)");
}

/**********************************************************************   
 **                                                                        
 **    void Ai::new_game()                                             
 **                                                                        
 **    Parameters: none                                                    
 **                                                                        
 **    Result: none                                                        
 **                                                                        
 **    Version: Alpha                                                      
 **                                                                        
 **    Description: inits the player for a new game                        
 **                 (the game-pointer is set in 'Game::Game(Party, int)')  
 **                                                                        
 **********************************************************************/  

void                                                                      
Ai::new_game(Game& game)                                                        
{    
  DEBUG_CALLING(INFO_AI && INFO_INIT,"Ai::new_game()");

  this->Player::new_game(game);

  this->meatlesscolor_p=Card::NOCARDCOLOR;

  this->low_p=true;


  this->playedcards_p.clear();

  this->trick_p = vector<Trick>(this->game().rule()(Rule::NUMBER_OF_TRICKS_IN_GAME));

  this->trumpless_ = vector<bool>(this->game().playerno(), false );
  this->trickno_p=0;
  for ( unsigned x = 0;
	x < this->game().rule()(Rule::NUMBER_OF_CARD_COLORS); x++)
  {
    this->colorjabbed_p[x]= vector<bool>(this->game().playerno(), false );
    this->color_runs_p[x]=0;
    for(unsigned y=0;y<this->game().playerno();y++) {
      this->colorjabbed_p[x][y]=false;
    } // for (y < this->game().playerno())
  } // for (x < this->game().playerno())

  this->silentMarriage_ = false;

  DEBUG_RETURNING_N(INFO_AI && INFO_INIT,"Ai::new_game()");                                                                         
}                                                                         


/**********************************************************************
 *
 **    MarriageSelector Ai::get_Marriage() const
 *
 **    Parameters: none
 *
 **    Result: tricktype which determines teams for a marraige
 *
 **    Version: Alpha
 *
 **    Description: 
 **      
 *
 **********************************************************************/

MarriageSelector Ai::get_Marriage() const
{
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,"Marriage Ai::get_Marriage()");   

  // ToDo: Test rules

  if (hand().numberoftrumps()<6 && hand().numberoftrumpaces()==1)
    DEBUG_RETURNING(MARRIAGE_SELECTOR::FIRST_TRUMP,INFO_AI && INFO_INIT,"Marriage Ai::get_Marriage()");   

  if ( Card(Card::HEART,Card::TEN).istrump() )
  { 
    if (hand().numberof(Card::HEART)>3 &&
	(hand().numberof(Card::HEART,Card::ACE)>0) )
      DEBUG_RETURNING(MARRIAGE_SELECTOR::FIRST_HEART,INFO_AI && INFO_INIT,"Marriage Ai::get_Marriage()"); 
  } else
  {
    if (hand().numberof(Card::HEART)<2 &&
	((hand().numberof(Card::HEART,Card::ACE)==1) ||
	 (hand().numberof(Card::HEART,Card::TEN)==1)))
      DEBUG_RETURNING(MARRIAGE_SELECTOR::FIRST_HEART,INFO_AI && INFO_INIT,"Marriage Ai::get_Marriage()"); 
  }
  if (hand().numberof(Card::CLUB)<2 &&
      ((hand().numberof(Card::CLUB,Card::ACE)==1) ||
       (hand().numberof(Card::CLUB,Card::TEN)==1)))
    DEBUG_RETURNING(MARRIAGE_SELECTOR::FIRST_CLUB,INFO_AI && INFO_INIT,"Marriage Ai::get_Marriage()"); 

  if (hand().numberof(Card::SPADE)<2 &&
      ((hand().numberof(Card::SPADE,Card::ACE)==1) ||
       (hand().numberof(Card::SPADE,Card::TEN)==1)))
    DEBUG_RETURNING(MARRIAGE_SELECTOR::FIRST_SPADE,INFO_AI && INFO_INIT,"Marriage Ai::get_Marriage()");  

  if (hand().numberoftrumps()>=8)
    DEBUG_RETURNING(MARRIAGE_SELECTOR::FIRST_COLOR,INFO_AI && INFO_INIT,"Marriage Ai::get_Marriage()");  

  DEBUG_RETURNING(MARRIAGE_SELECTOR::FIRST_FOREIGN,INFO_AI && INFO_INIT,"Marriage Ai::get_Marriage()");   
}

/**********************************************************************
 *
 **    Reservation Ai::reservation_get() const
 *
 **    Parameters: none
 *
 **    Result: reservation
 *
 **    Version: Alpha
 *
 **    Description: returns the reservation of the player
 **  
 **      
 *
 **********************************************************************/

Reservation
Ai::reservation_get( bool isDuty ) 
{
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY, "Ai::reservation_get()");

  Reservation res = this->reservation_get_default();
  Reservation best_res = Reservation();
  unsigned solo_best = 0;

  // always announce swines/hyperswines
  // -- the game will reject it if the announcement is not valid
  res.swines=true;
  res.hyperswines=true;
  best_res.swines=true;
  best_res.hyperswines=true;


  int solovalue=0;  
  unsigned solomax=0;

  solovalue=checksolo(Card::KING,Card::QUEEN,
		      Card::JACK,GAMETYPE::SOLO_KOEHLER);
  if (solovalue >= 0 && party.rule()(Rule::SOLO_KOEHLER))
  {
    if ( unsigned(solovalue) > this->Aiconfig::value(Aiconfig::TRIPLESOLO) )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::TRIPLESOLO);
      solo_best = solovalue-this->Aiconfig::value(Aiconfig::TRIPLESOLO);
      res.game_type=GAMETYPE::SOLO_KOEHLER;
      best_res.game_type = GAMETYPE::SOLO_KOEHLER;
    }
  }

  solovalue=checksolo(Card::JACK,Card::QUEEN,
		      GAMETYPE::SOLO_QUEEN_JACK);
  if (solovalue >= 0
      && party.rule()(Rule::SOLO_QUEEN_JACK))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::DOUBLESOLO)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO);
      res.game_type=GAMETYPE::SOLO_QUEEN_JACK;
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO);
      best_res.game_type=GAMETYPE::SOLO_QUEEN_JACK;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO);
	best_res.game_type=GAMETYPE::SOLO_QUEEN_JACK;
      }
  }

  solovalue=checksolo(Card::JACK,Card::KING,
		      GAMETYPE::SOLO_KING_JACK);
  if (solovalue >= 0
      && party.rule()(Rule::SOLO_KING_JACK))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::DOUBLESOLO)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO);
      res.game_type=GAMETYPE::SOLO_KING_JACK;
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO);
      best_res.game_type=GAMETYPE::SOLO_KING_JACK;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO);
	best_res.game_type=GAMETYPE::SOLO_KING_JACK;
      }

  }

  solovalue=checksolo(Card::QUEEN, Card::KING,
		      GAMETYPE::SOLO_KING_QUEEN);
  if (solovalue >= 0
      && party.rule()(Rule::SOLO_KING_QUEEN))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::DOUBLESOLO)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO);
      res.game_type=GAMETYPE::SOLO_KING_QUEEN;
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO);
      best_res.game_type=GAMETYPE::SOLO_KING_QUEEN;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::DOUBLESOLO);
	best_res.game_type=GAMETYPE::SOLO_KING_QUEEN;
      }
  }

  solovalue=checksolo(Card::QUEEN,
		      GAMETYPE::SOLO_QUEEN);
  if (solovalue >= 0
      && party.rule()(Rule::SOLO_QUEEN))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::SINGLESOLO)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO);
      res.game_type=GAMETYPE::SOLO_QUEEN;
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO);
      best_res.game_type=GAMETYPE::SOLO_QUEEN;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO);
	best_res.game_type=GAMETYPE::SOLO_QUEEN;
      }
  }

  solovalue=checksolo(Card::KING,
		      GAMETYPE::SOLO_KING);
  if (solovalue >= 0
      && party.rule()(Rule::SOLO_KING))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::SINGLESOLO)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO);
      res.game_type=GAMETYPE::SOLO_KING;
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO);
      best_res.game_type=GAMETYPE::SOLO_KING;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO);
	best_res.game_type=GAMETYPE::SOLO_KING;
      }
  }

  solovalue=checksolo(Card::JACK,
		      GAMETYPE::SOLO_JACK);
  if (solovalue >= 0
      && party.rule()(Rule::SOLO_JACK))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::SINGLESOLO)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO);
      res.game_type=GAMETYPE::SOLO_JACK;
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO);
      best_res.game_type=GAMETYPE::SOLO_JACK;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::SINGLESOLO);
	best_res.game_type=GAMETYPE::SOLO_JACK;
      }
  }

  solovalue=checksolo();
  if (solovalue >= 0
      && party.rule()(Rule::SOLO_MEATLESS))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::MEATLESS)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::MEATLESS))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::MEATLESS);
      res.game_type=GAMETYPE::SOLO_MEATLESS;
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::MEATLESS);
      best_res.game_type=GAMETYPE::SOLO_MEATLESS;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::MEATLESS))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::MEATLESS);
	best_res.game_type=GAMETYPE::SOLO_MEATLESS;
      }
  }

  solovalue=checksolo(Card::DIAMOND,
		      GAMETYPE::SOLO_DIAMOND);
  if (solovalue >= 0
      && party.rule()(Rule::SOLO))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::COLORSOLO)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
      res.game_type=GAMETYPE::SOLO_DIAMOND;
      // playing silent solo
      if (hand().numberofclubqueens()==2) 
      {
		res.game_type=GAMETYPE::NORMAL;
		silentMarriage_ = true;
      }
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
      best_res.game_type=GAMETYPE::SOLO_DIAMOND;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
	best_res.game_type=GAMETYPE::SOLO_DIAMOND;
      }	


  }

  solovalue=checksolo(Card::HEART,
		      GAMETYPE::SOLO_HEART);

  if (solovalue >= 0
      && party.rule()(Rule::SOLO))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::COLORSOLO)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
      res.game_type=GAMETYPE::SOLO_HEART;
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
      best_res.game_type=GAMETYPE::SOLO_HEART;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
	best_res.game_type=GAMETYPE::SOLO_HEART;
      }	
  }


  solovalue=checksolo(Card::SPADE,
		      GAMETYPE::SOLO_SPADE);

  if (solovalue >= 0
      && party.rule()(Rule::SOLO))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::COLORSOLO)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
      res.game_type=GAMETYPE::SOLO_SPADE;
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
      best_res.game_type=GAMETYPE::SOLO_SPADE;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
	best_res.game_type=GAMETYPE::SOLO_SPADE;
      }	
  }

  solovalue=checksolo(Card::CLUB,
		      GAMETYPE::SOLO_CLUB);

  if (  solovalue >= 0
	&& party.rule()(Rule::SOLO))
  {
    if (unsigned(solovalue) > this->Aiconfig::value(Aiconfig::COLORSOLO)
	&& unsigned(solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO))>=solomax )
    {
      solomax=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
      res.game_type=GAMETYPE::SOLO_CLUB;
      solo_best=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
      best_res.game_type=GAMETYPE::SOLO_CLUB;
    } else
      if ( unsigned(solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO))>=solo_best)
      {
	solo_best=solovalue-this->Aiconfig::value(Aiconfig::COLORSOLO);
	best_res.game_type=GAMETYPE::SOLO_CLUB;
      }	
  }

  if( isDuty )
  {
    DEBUG_RETURNING(best_res,INFO_AI && INFO_GAMEPLAY, "Ai::reservation_get()");
  }

#ifndef OLD
  if (res.game_type == GAMETYPE::MARRIAGE) {
    res.marriage_selector=get_Marriage();
#ifndef POSTPHONED
    // ToDo: to be removed
    if (!this->game().rule()(res.marriage_selector))
      res.marriage_selector = this->reservation_get_default().marriage_selector;
#endif
  }
#endif

  DEBUG_RETURNING(res,INFO_AI && INFO_GAMEPLAY, "Ai::reservation_get()");
}

/**
 **
 ** updates the information of the teams (uses the current trick)
 **
 ** @param      -
 **
 ** @return     the known teams
 **
 ** @version    0.4.4
 **
 ** @author     Borg Enders
 ** @author     Diether Knof
 **
 **/
void
Ai::teaminfo_update()
{
  DEBUG_CALLING(INFO_AI && INFO_VALUE,
		"Ai::teaminfo_update()");

  this->Player::teaminfo_update();

  using namespace TEAM;
  if( silentMarriage_ )
	 for (unsigned i = 0; i < 4; i++)   
	  if( i != this->no() )
		  this->teaminfo_p[ i ] = CONTRA;

  if (::game_status == GAMESTATUS::GAME_RESERVATION) {
    if (this->game().type() == GAMETYPE::MARRIAGE) {
      for (unsigned p = 0; p < this->game().playerno(); p++)
	if (this->teaminfo_p[p] == TEAM::UNKNOWN)
	  this->teaminfo_p[p] = TEAM::MAYBE_CONTRA;
    } // if (this->game().type() == GAMETYPE::MARRIAGE)
  } // if (::game_status == GAMESTATUS::RESERVATION)

  if (::game_status >= GAMESTATUS::GAME_PLAY) {
    // set some maybe-teaminfo
    Trick const& trick = this->game().trick_current();
    if (trick.isfull()) {
      bool anyKnown = false;
      for (unsigned i = 0; i < trick.actcardno(); i++) {
	// the team is already set
	if ( (this->game().teaminfo(trick.player_of_card(i)) == RE) ||
	     (this->game().teaminfo(trick.player_of_card(i)) == CONTRA) )
	  anyKnown = true;
      }

      if (!anyKnown) return;
      for (unsigned i = 0; i < trick.actcardno(); i++) {
	// the team is already set
	if ( (this->teaminfo(trick.player_of_card(i)) == RE) ||
	     (this->teaminfo(trick.player_of_card(i)) == CONTRA) )
	  continue;

	// jab a club queen
	if ( (trick.card(i) == Card::CLUB_QUEEN)
	     && (trick.winnerplayer() != trick.player_of_card(i))
	     && (this->teaminfo(trick.winnerplayer()) != RE)
	     && (this->teaminfo(trick.winnerplayer()) != CONTRA)
	   )
	  this->teaminfo_p[trick.winnerplayer().no()] = team_to_maybe(CONTRA);

	// no jab in (color) trick (and there are trumps remaining)
	if ( (trick.card(i).tcolor() != trick.startcard().tcolor())
	     && (!trick.card(i).istrump())
	     && (trick.winnerplayer() != trick.player_of_card(i))
	     && (this->game().trumps_no()
		 > (this->game().trumps_played_no()
		    + this->hand().numberoftrumps())
		)
	   )
	{

	  this->teaminfo_p[trick.player_of_card(i).no()]
	    = team_to_maybe(this->teaminfo(trick.winnerplayer()));
	}
	// pfund in a color trick
	if ( (this->type() & AI)
	     && (trick.card(i).tcolor() == trick.startcard().tcolor())
	     &&(trick.winnerplayer() != trick.player_of_card(i))
	     && (trick.card(i).value() >= 10)
	     && this->value(Aiconfig::TRUSTING)
	   )
	{
	  this->teaminfo_p[trick.player_of_card(i).no()]
	    = team_to_maybe(this->teaminfo(trick.winnerplayer()));

	}
      } // for (i < trick.actcard())
    } // if (trick.isfull())
    else
    {
      if ( !trick.isstartcard()
	   && (this->game().type() == GAMETYPE::MARRIAGE) ) {
	switch (this->game().marriage_selector()) {
	case MARRIAGE_SELECTOR::TEAM_SET:
	  // the marriage is determined
	  break;
	case MARRIAGE_SELECTOR::FIRST_COLOR:
	case MARRIAGE_SELECTOR::FIRST_FOREIGN:
	  if (!trick.card(0).istrump()
	      && (trick.card(0).value() == Card::ACE)
	      && (this->color_runs( trick.card( 0 ).color() ) == 0) )
	  {//2do jabbing for colors
	    this->teaminfo_p[trick.player_of_card(0).no()] = TEAM::MAYBE_RE;
	  }
	  if (this->game().marriage_selector()
	      == MARRIAGE_SELECTOR::FIRST_COLOR)
	    break;
	case MARRIAGE_SELECTOR::FIRST_TRUMP:
	  // ToDo
	  break;
	case MARRIAGE_SELECTOR::FIRST_CLUB:
	  if ( (trick.card(0) == Card(Card::CLUB, Card::ACE))
	       && (this->color_runs( trick.card( 0 ).color() ) == 0) )
	  {//2do jabbing for colors
	    this->teaminfo_p[trick.player_of_card(0).no()] = TEAM::MAYBE_RE;
	  }
	  break;
	case MARRIAGE_SELECTOR::FIRST_SPADE:
	  if ( (trick.card(0) == Card(Card::SPADE, Card::ACE))
	       && (this->color_runs( trick.card( 0 ).color() ) == 0) )
	  {//2do jabbing for colors
	    this->teaminfo_p[trick.player_of_card(0).no()] = TEAM::MAYBE_RE;
	  }
	  break;
	case MARRIAGE_SELECTOR::FIRST_HEART:
	  if ( (trick.card(0) == Card(Card::HEART, Card::ACE))
	       && (this->color_runs( trick.card( 0 ).color() ) == 0) )
	  {//2do jabbing for colors
	    this->teaminfo_p[trick.player_of_card(0).no()] = TEAM::MAYBE_RE;
	  }
	  break;
	} // switch (this->game().marriage_selector())
      } // if !(marriage && !trick.isstartcard())
    } // if !(trick.isfull())

  } // if (::game_status >= GAMESTATUS::GAME_PLAY)

  DEBUG_RETURNING(VOID,
		  INFO_AI && INFO_VALUE,
		  "Ai::teaminfo_update()");
} // void Ai::teaminfo_update()


/**
 **
 ** -> result
 ** (uses Heuristics)
 **
 ** @see        Heuristics::make_announcement()
 ** @see        Heuristics::say_no90
 ** @see        Heuristics::say_no60
 ** @see        Heuristics::say_no30
 ** @see        Heuristics::say_no0
 **
 ** @param      -
 **
 ** @return     announcement of the player
 **
 ** @version    0.4.4
 **
 ** @author     Borg Enders
 ** @author     Diether Knof
 **
 **/
Announcement
Ai::announcement_request() const
{
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,
		"Ai::announcement_request()");

  Announcement res = ANNOUNCEMENT::NOANNOUNCEMENT;

  if (Heuristics::make_announcement(*this, this->game()))
    res = ANNOUNCEMENT::NO120;

  for (unsigned a = std::max(int(ANNOUNCEMENT::NO90),
			     this->announcement() + 1);
       a < ANNOUNCEMENT::NUMBER;
       a++) {
    // make the announcement as late as possible
    if ((this->game().rule().remaining_tricks(Announcement(a))
	 == this->game().tricks_remaining_no())
	&& (this->game().trick_current().isfull())
       ) {
      switch(Announcement(a)) {
      case ANNOUNCEMENT::NOANNOUNCEMENT:
      case ANNOUNCEMENT::REPLY:
      case ANNOUNCEMENT::NO120_REPLY:
      case ANNOUNCEMENT::NO90_REPLY:
      case ANNOUNCEMENT::NO60_REPLY:
      case ANNOUNCEMENT::NO30_REPLY:
      case ANNOUNCEMENT::NO0_REPLY:
      case ANNOUNCEMENT::NO120:
	DEBUG_ASSERTION(false,
			"Ai::announcement_request():\n"
			"  wrong announcement in 'switch': "
			<< Announcement(a));
	break;
      case ANNOUNCEMENT::NO90:
	if (Heuristics::say_no90(*this, this->game()))
	  res = Announcement(a);
	break;
      case ANNOUNCEMENT::NO60:
	if (Heuristics::say_no60(*this, this->game()))
	  res = Announcement(a);
	break;
      case ANNOUNCEMENT::NO30:
	if (Heuristics::say_no30(*this, this->game()))
	  res = Announcement(a);
	break;
      case ANNOUNCEMENT::NO0:
	if (Heuristics::say_no0(*this, this->game()))
	  res = Announcement(a);
	break;
      } // switch(Announcement(a))
    } // if (last chance for announcement)
  } 
  // for (a \in announcements)

  if ( ( res == ANNOUNCEMENT::NOANNOUNCEMENT )
       && this->game().announcement_valid(ANNOUNCEMENT::REPLY, *this) )
    res = Heuristics::make_reply(*this, this->game());

  DEBUG_RETURNING(res,
		  INFO_AI && INFO_GAMEPLAY,
		  "Ai::announcement_request()");
}

/**********************************************************************   
 **                                                                        
 **    int Ai::checksolo(Card::Value c)                                          
 **                                                                        
 **    Parameters: Value of Car dor which to check if cardvalue for solo                                                 
 **                                                                        
 **    Result: cardvalue for solo                                                        
 **                                                                        
 **    Version: Alpha                                                      
 **                                                                        
 **    Description: 
 **                                                                        
 **********************************************************************/  

int Ai::checksolo(Card::Value c,GameType t) const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,"Ai::checksolo(Card::Value c)");

  int res=0;
  for (unsigned i=0; i<hand().cardsnumber();i++)
  {
    if (hand().card(i).value()==c)
    {
      if (hand().card(i).color()==Card::CLUB)
	res+=4;

      if (hand().card(i).color()==Card::SPADE)
	res+=3;

      if (hand().card(i).color()==Card::HEART)
	res+=2;  

      if (hand().card(i).color()==Card::DIAMOND)
	res+=1;
      continue;
    }

    if (hand().card(i).value()==Card::ACE)
    {
      res+=1;
      continue;
    }
    res -= 1;
  }

  res+=number_of_fehl(t)*2;

  res-=number_of_single_tens(t)*4;


  DEBUG_RETURNING(res,INFO_AI && INFO_OTHER_FUNCTION,"Ai::checksolo(Card::Value c)");
}

/**********************************************************************   
 **                                                                        
 **    int Ai::checksolo(Card::Value c1, Card::Value c2) const                                      
 **                                                                        
 **    Parameters: c1 highest trumpcardvalue, c2 second trump cardvalue                                                    
 **                                                                        
 **    Result: cardvalue for solo                                                        
 **                                                                        
 **    Version: Alpha                                                      
 **                                                                        
 **    Description: 
 **                                                                        
 **********************************************************************/  

int Ai::checksolo(Card::Value c1, Card::Value c2,GameType t) const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,"Ai::checksolo(Card::Value c1, Card::Value c2)");
  int res=0;
  for (unsigned i=0; i<hand().cardsnumber();i++)
  {
    if (hand().card(i).value()==c1)
    {
      if (hand().card(i).color()==Card::CLUB)
	res+=8;

      if (hand().card(i).color()==Card::SPADE)
	res+=7;

      if (hand().card(i).color()==Card::HEART)
	res+=6;  

      if (hand().card(i).color()==Card::DIAMOND)
	res+=5;
      continue;
    }

    if (hand().card(i).value()==c2)
    {
      if (hand().card(i).color()==Card::CLUB)
	res+=4;

      if (hand().card(i).color()==Card::SPADE)
	res+=3;

      if (hand().card(i).color()==Card::HEART)
	res+=2;  

      if (hand().card(i).color()==Card::DIAMOND)
	res+=1;
      continue;
    }

    if (hand().card(i).value()==Card::ACE)
    {
      res+=1;
      continue;
    }

    res-=2;
  }

  res+=number_of_fehl(t)*6;
  res-=number_of_single_tens(t)*8;

  DEBUG_RETURNING(res,INFO_AI && INFO_OTHER_FUNCTION,"Ai::checksolo(Card::Value c1, Card::Value c2)");
}

/**********************************************************************   
 **                                                                        
 **    int Ai::checksolo(Card::Value c1, Card::Value c2, Card::Value c3) const;                                         
 **                                                                        
 **    Parameters : c1 highest trumpcardvalue, c2 second trump cardvalue                                                    
 **                  c3 third trump cardvalue
 **                                                                        
 **    Result: cardvalue for solo                                                       
 **                                                                        
 **    Version: Prototype                                                      
 **                                                                        
 **    Description: 
 **                                                                        
 **********************************************************************/  

int Ai::checksolo(Card::Value c1, Card::Value c2, Card::Value c3,GameType t) const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,"Ai::checksolo(Card::Value c1, Card::Value c2, Card::Value c3)");
  int res=0;
  for (unsigned i=0; i<hand().cardsnumber();i++)
  {

    if (hand().card(i).value()==c1)
    {
      if (hand().card(i).color()==Card::CLUB)
	res+=12;

      if (hand().card(i).color()==Card::SPADE)
	res+=11;

      if (hand().card(i).color()==Card::HEART)
	res+=10;  

      if (hand().card(i).color()==Card::DIAMOND)
	res+=9;
      continue;
    }

    if (hand().card(i).value()==c2)
    {
      if (hand().card(i).color()==Card::CLUB)
	res+=8;

      if (hand().card(i).color()==Card::SPADE)
	res+=7;

      if (hand().card(i).color()==Card::HEART)
	res+=6;  

      if (hand().card(i).color()==Card::DIAMOND)
	res+=5;
      continue;
    }

    if (hand().card(i).value()==c3)
    {
      if (hand().card(i).color()==Card::CLUB)
	res+=4;

      if (hand().card(i).color()==Card::SPADE)
	res+=3;

      if (hand().card(i).color()==Card::HEART)
	res+=2;  

      if (hand().card(i).color()==Card::DIAMOND)
	res+=1;
      continue;
    }

    if (hand().card(i).value()==Card::ACE)
    {
      res+=1;
      continue;
    }

    res -= 3;
  }

  res+=number_of_fehl(t)*10;

  res-=number_of_single_tens(t)*12;

  if (t==GAMETYPE::SOLO_KOEHLER)
    for ( unsigned i=0; i < ::party.rule()(Rule::NUMBER_OF_CARD_COLORS);i++)
    {
      if ( this->hand().existstcolor(Card::InttoColor(i)) )
	res-=5;
    }
  DEBUG_RETURNING(res,INFO_AI && INFO_OTHER_FUNCTION,"Ai::checksolo(Card::Value c1, Card::Value c2, Card::Value c3)");
}

/**********************************************************************   
 **                                                                        
 **    int Ai::checksolo(Card::Color c) const;                                           
 **                                                                        
 **    Parameters: trumpcolor                                                    
 **                                                                        
 **    Result: cardvalue for solo                                              
 **                                                                        
 **    Version: Alpha                                              
 **                                                                        
 **    Description: 
 **                                                                        
 **********************************************************************/  

int Ai::checksolo(Card::Color c,GameType t) const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,"Ai::checksolo(Card::Color c)");
  int res=0;

  if ((hand().numberof(Card(c,Card::ACE))==2) && party.rule()(Rule::SWINES_IN_SOLO))
  {
    res+=9;
  } 

  for (unsigned i=0; i<hand().cardsnumber();i++)
  {  
    if (hand().card(i).isdolle())
    {

      res+=4;
      continue;
    }

    if (hand().card(i).value()==Card::QUEEN)
    {
      res+=3;
      continue;

    }

    if (hand().card(i).value()==Card::JACK)
    {
      res+=2;
      continue;

    }

    if (hand().card(i).value()==Card::ACE && hand().card(i).color()!=c && hand().numberof(hand().card(i).color())<3)
    {
      res+=1;
      continue;
    }

    if( hand().card(i).color() != c )
    {
      res -= 1;
    }

  }

  res+=number_of_fehl(t)*2;

  if (hand().numberof(c)==0) res-=3;

  res-=number_of_single_tens(t)*4;



  DEBUG_RETURNING(res,INFO_AI && INFO_OTHER_FUNCTION,"Ai::checksolo(Card::Color c)");
}

/**********************************************************************   
 **                                                                        
 **   int Ai::checksolo() const;                                         
 **                                                                        
 **    Parameters: none                                                    
 **                                                                        
 **    Result: cardvalue for solo                                                      
 **                                                                        
 **    Version: Prototype                                                      
 **                                                                        
 **    Description: 
 **                                                                        
 **********************************************************************/  

int Ai::checksolo() const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION,"Ai::checksolo()");
  int res=0;

  Card::Color co=Card::DIAMOND;
  res+=hand().numberof(co,GAMETYPE::SOLO_MEATLESS)*hand().numberof(Card(co,Card::ACE))+
    hand().numberof(co,GAMETYPE::SOLO_MEATLESS)*hand().numberof(Card(co,Card::TEN));

  co=Card::HEART;
  res+=hand().numberof(co,GAMETYPE::SOLO_MEATLESS)*hand().numberof(Card(co,Card::ACE))+
    hand().numberof(co,GAMETYPE::SOLO_MEATLESS)*hand().numberof(Card(co,Card::TEN));

  co=Card::SPADE; 
  res+=hand().numberof(co,GAMETYPE::SOLO_MEATLESS)*hand().numberof(Card(co,Card::ACE))+
    hand().numberof(co,GAMETYPE::SOLO_MEATLESS)*hand().numberof(Card(co,Card::TEN));

  co=Card::CLUB;
  res+=hand().numberof(co,GAMETYPE::SOLO_MEATLESS)*hand().numberof(Card(co,Card::ACE))+
    hand().numberof(co,GAMETYPE::SOLO_MEATLESS)*hand().numberof(Card(co,Card::TEN));

  res-=number_of_single_tens(GAMETYPE::SOLO_MEATLESS)*5;

  for (unsigned int i=0; i < ::party.rule()(Rule::NUMBER_OF_CARD_COLORS);i++)
  {
    if ( this->hand().existstcolor(Card::InttoColor(i)) )
    {

      res-=2*(2 - this->hand().numberof(Card::InttoColor(i),Card::ACE) );
    }
  }


  DEBUG_RETURNING(res,INFO_AI && INFO_OTHER_FUNCTION,"Ai::checksolo()");
}





/**********************************************************************   
 **                                                                        
 **   unsigned Ai::remaining_trumps()                              
 **                                                                        
 **    Parameters: none 
 *
 **    Result: number of trumps still in game                                            
 **                                                                        
 **    Version: Alpha                                                    
 **                                                                        
 **    Description: 
 **                                                                        
 **********************************************************************/ 

unsigned Ai::remaining_trumps() const
{
  DEBUG_CALLING(INFO_AI && INFO_VALUE,"int Ai::remaining_trumps()");

  int result=game().trumps_no();


  for (unsigned x = 0;
       x < this->game().rule()(Rule::NUMBER_OF_CARD_COLORS);
       x++)
    for (unsigned y = 0;
	 y < this->game().rule()(Rule::NUMBER_OF_CARD_VALUES);
	 y++)
      if(Card(x, y).istrump())
      {
	if ( playedcard(Card( x, y )) < 3 )
	{
	  result -= playedcard(Card( x, y ));
	} else
	  result -= 2;
      }


  DEBUG_RETURNING(result > 0 ? result : 0,INFO_AI && INFO_VALUE,"int Ai::remaining_trumps()");

}

/**********************************************************************   
 **                                                                        
 **   unsigned Ai::number_of_single_tens()                         
 **                                                                        
 **    Parameters: none 
 *
 **    Result: number of single tens on hand                                            
 **                                                                        
 **    Version: Alpha                                                    
 **                                                                        
 **    Description: 
 **                                                                        
 **********************************************************************/

unsigned Ai::number_of_single_tens(GameType t) const
{

  DEBUG_CALLING(INFO_AI && INFO_VALUE, "int Ai::number_of_single_tens()");
  unsigned result=0;
  if (hand().numberof(Card::DIAMOND)<=2 && hand().numberof(Card::DIAMOND,Card::ACE)==0 
      && !Card(Card::DIAMOND,Card::ACE).istrump(t))
    result+=hand().numberof(Card::DIAMOND,Card::TEN);
  if (hand().numberof(Card::HEART)<=2 && hand().numberof(Card::HEART,Card::ACE)==0
      && !Card(Card::HEART,Card::ACE).istrump(t))
    result+=hand().numberof(Card::HEART,Card::TEN);
  if (hand().numberof(Card::SPADE)<=2 && hand().numberof(Card::SPADE,Card::ACE)==0 
      && !Card(Card::SPADE,Card::ACE).istrump(t))
    result+=hand().numberof(Card::SPADE,Card::TEN);
  if (hand().numberof(Card::CLUB)<=2 && hand().numberof(Card::CLUB,Card::ACE)==0 
      && !Card(Card::CLUB,Card::ACE).istrump(t))
    result+=hand().numberof(Card::CLUB,Card::TEN);


  DEBUG_RETURNING(result,INFO_AI && INFO_VALUE, "int Ai::number_of_single_tens()");  
}

/**********************************************************************   
 **                                                                        
 **   unsigned Ai::number_of_fehl()                      
 **                                                                        
 **    Parameters: none 
 *
 **    Result: number of colors not on hand                                           
 **                                                                        
 **    Version: Alpha                                                  
 **                                                                        
 **    Description: 
 **                                                                        
 **********************************************************************/

unsigned Ai::number_of_fehl(GameType t) const
{
  DEBUG_CALLING(INFO_AI && INFO_VALUE, "int Ai::number_of_fehl()");
  unsigned result=0;
  if (hand().numberof(Card::DIAMOND,t)==0)
  {
    result++;
  }
  if (hand().numberof(Card::HEART,t)==0)
  {
    result++;
  }
  if (hand().numberof(Card::SPADE,t)==0)
  {
    result++;
  }
  if (hand().numberof(Card::CLUB,t)==0)
  {
    result++;
  }

  DEBUG_RETURNING(result,INFO_AI && INFO_VALUE, "int Ai::number_of_fehl()");  
} 


/**********************************************************************   
 **                                                                        
 **   bool Ai::single_ace()                         
 **                                                                        
 **    Parameters: none 
 *
 **    Result: number of single tens on hand                                            
 **                                                                        
 **    Version: Alpha                                                    
 **                                                                        
 **    Description: 
 **                                                                        
 ** @todo	'Card::inttovalue()' in the Card-constructors
 **                                                                        
 **********************************************************************/
bool
Ai::single_ace() const
{
  DEBUG_CALLING(INFO_AI && INFO_VALUE,
		"int Ai::single_ace()");

  bool result = false;

  for (unsigned x = 0; x < this->game().rule()(Rule::NUMBER_OF_CARD_COLORS) ; x++ )
  {
    if (hand().numberof( Card( (Card::TColor)x, Card::ACE )) == 1
	&& !Card((Card::TColor)x,Card::ACE).istrump()
	&& (hand().numberof( Card((Card::TColor)x, Card::ACE )) 
	    + hand().numberof(Card( (Card::TColor)x, Card::TEN )) == hand().numberof((Card::TColor)x)) )
    {
      result=true;
      break;
    }
  }


  DEBUG_RETURNING(result,
		  INFO_AI && INFO_VALUE,
		  "int Ai::single_ace()");  
} // bool Ai::single_ace() const


/**
 ** -> result
 **
 ** @param      -
 **
 ** @return     modificator for the end depth for calculating tricks
 **             in WVirtualGames
 **
 ** @version    0.4.4
 **
 ** @author     Borg Enders
 **
 **/
unsigned const&
Ai::last_trick_to_calculate() const
{

  DEBUG_CALLING(INFO_AI && INFO_VALUE,
		"Ai::last_trick_to_calculate()");

  DEBUG_RETURNING(this->last_trick_to_calculate_p,
		  INFO_AI && INFO_VALUE,
		  "Ai::last_trick_to_calculate()");
} // unsigned const& Ai::last_trick_to_calculate() const

/**
 ** sets the parameter to the last trick to calculate
 **
 ** @param      last trick to calculate to be seted
 **
 ** @return     modificator for the end depth for calculating tricks
 **             in WVirtualGames
 **
 ** @version    0.4.4
 **
 ** @author     Borg Enders
 **
 **/
unsigned const&
Ai::last_trick_to_calculate_set(unsigned const last_trick_to_calculate)
{

  DEBUG_CALLING(INFO_AI && INFO_VALUE,
		"Ai::last_trick_to_calculate_set()");

  this->last_trick_to_calculate_p = last_trick_to_calculate;

  DEBUG_RETURNING(this->last_trick_to_calculate(),
		  INFO_AI && INFO_VALUE,
		  "Ai::last_trick_to_calculate_set()");
} // unsigned const& Ai::last_trick_to_calculate_set(short const last_trick_to_calculate)

/**
 **
 ** returns which cards the player shifts
 **
 ** @param      -
 **
 ** @return     the cards that are to be shifted
 **
 ** @version    0.4.5
 **
 ** @author     Borg Enders
 **
 **/
Hand
Ai::poverty_shift()
{ 
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,
		"Ai::poverty_shift()");

  DEBUG_ASSERT( this->hand().numberoftrumps() < 4 );

  SortedHand& sh = this->sorted_hand();
  Hand const& h = this->hand();

  vector<Card> res;

  for( unsigned int i = 0; i < h.cardsnumber(); i++ )
    if ( h.card( i ).istrump())
    {
      res.push_back( h.card( i ) );
    }

  sh.remove( res );

  int rem = 3 - res.size();

  if ( !this->game().rule()( Rule::POVERTY_SHIFT_ONLY_TRUMP ) && rem > 0  )
  {
    vector<Card> addon;

    Card::Color c = Card::HEART ;
    unsigned int size = h.numberof( c );

    if ( h.numberof( Card::SPADE ) < size )
    {
      c = Card::SPADE;
      size = h.numberof( Card::SPADE );      
    }

    if ( h.numberof( Card::CLUB ) < size )
    {
      c = Card::CLUB;
      size = h.numberof( Card::CLUB );      
    }

    unsigned int i = 0;
    while ( rem > 0 )
    {
      if ( i >= h.cardsnumber() )
	i = 0;
      if ( size > 0 )
      {
	if ( h.card( i ).color() == c )
	{
	  addon.push_back( h.card( i ) );
	  sh.remove( h.card( i ) );
	  size--;
	  rem--;
	}
      } else
      {
	addon.push_back( h.card( i ) );
	sh.remove( h.card( i ) );
	rem--;
      }
      i += 1;
    }

    res.insert(res.end(), addon.begin(), addon.end());
  }// !this->game().rule()( Rule::POVERTY_SHIFT_ONLY_TRUMP ) && rem > 0

  DEBUG_RETURNING(res,
		  INFO_AI && INFO_GAMEPLAY,
		  "Ai::poverty_shift()");
} // Hand Ai::poverty_shift(Player const& player)

/**
 **
 ** returns whether 'player' accepts the shifted cards
 **
 ** @param      cardno  the number of shifted cards
 **
 ** @return     whether to accept the cards
 **
 ** @version    0.4.5
 **
 ** @author     Borg Enders
 **
 **/
bool
Ai::poverty_take_accept(unsigned const cardno)
{ 
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,
		"Ai::poverty_take_accept(cardno)");

  int value = Heuristics::CalcHandValue( *this, this->game() );

  if( this->hand().numberoftrumps() > 12 - cardno )
    value += this->hand().numberof( Card::QUEEN ) * 2 
      + this->hand().numberof( Card::JACK ) +
      this->hand().numberoftrumpaces();



  DEBUG_RETURNING( value
		   >= static_cast<signed>(this->value(Aiconfig::TAKEPOVERTY)),
		   INFO_AI && INFO_GAMEPLAY,
		   "Ai::poverty_take_accept(cardno)");
} // void Ai::poverty_take_accept(unsigned const cardno)

/**
 **
 ** changes the cards from the poverty-player
 **
 ** @param      cards   the cards that are given to the player
 **
 ** @return     the cards that are returned to the poverty-player
 **
 ** @version    Prototype
 **
 ** @author     Borg Enders
 **
 **/
Hand
Ai::poverty_cards_change(Hand const& cards)
{ 
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,
		"Ai::poverty_cards_change(cards)");

  SortedHand& sh = this->sorted_hand();
  Hand const& h = this->hand();

  sh.add(cards);


  vector<Card> res;
  int rem = cards.cardsnumber();

  // create Fehl for Club
  if( h.numberof( Card::CLUB ) > 0 && (int)h.numberof( Card::CLUB ) <= rem )
  {
    vector<Card> addon;
    for( unsigned int i = 0; i < h.cardsnumber(); i++ )
      if ( ( h.card( i ).color() == Card::CLUB ) && !h.card(i).istrump() )
	addon.push_back( h.card( i ) );
    sh.remove( addon );
    res.insert(res.end(), addon.begin(), addon.end());
    rem-= addon.size();
  }

  // create Fehl for Spade
  if( h.numberof( Card::SPADE ) > 0 && (int)h.numberof( Card::SPADE ) <= rem )
  {
    vector<Card> addon;
    for( unsigned int i = 0; i < h.cardsnumber(); i++ )
      if( ( h.card( i ).color() == Card::SPADE ) && !h.card(i).istrump() )
	addon.push_back( h.card( i ) );
    sh.remove( addon );
    res.insert(res.end(), addon.begin(), addon.end());
    rem-= addon.size();
  }

  // create Fehl for Heart
  if( h.numberof( Card::HEART ) > 0 && (int)h.numberof( Card::HEART ) <= rem )
  {
    vector<Card> addon;
    for( unsigned int i = 0; i < h.cardsnumber(); i++ )
      if( ( h.card( i ).color() == Card::HEART ) && !h.card(i).istrump() )
	addon.push_back( h.card( i ) );
    sh.remove( addon );
    res.insert(res.end(), addon.begin(), addon.end());
    rem-= addon.size();
  }



  while ( rem > 0 )
  {
    //adding highest color card
    if ( h.numberoftrumps() < h.cardsnumber() && rem > 0 )
    {
      Card c;
      for( unsigned int i = 0; i < h.cardsnumber(); i++ )
	if ( !h.card( i ).istrump() )
	  c = h.card( i );

      if( c )
      {
	for( unsigned int i = 0; i < h.cardsnumber(); i++ )
	  if ( !h.card( i ).istrump() && !h.card( i ).less(c, this->game()) )
	    c = h.card( i );

	sh.remove( c );
	res.push_back( c );
	rem--;      
	continue;
      }
    }

    //adding lowest trump card
    if (  rem > 0 )
    {
      Card c;
      for( unsigned int i = 0; i < h.cardsnumber(); i++ )
	if ( h.card( i ).istrump() )
	  c = h.card( i );

      if( c )
      {
	for( unsigned int i = 0; i < h.cardsnumber(); i++ )
	  if ( h.card( i ).istrump() && h.card( i ).less(c, this->game()) )
	    c = h.card( i );

	sh.remove( c );
	res.push_back( c );
	rem--;      
	continue;
      }
    }
  }

  // setting own hand
  this->hand_set( h );

  DEBUG_RETURNING( res,
		   INFO_AI && INFO_GAMEPLAY,
		   "Ai::poverty_cards_change(cards)");
} // Hand Ai::poverty_cards_change(Hand const& cards)


/**
 **
 ** gets the cards from the partner and add them to the hand
 **
 ** @param	cards	the cards that are given to the player
 **
 ** @return	-
 **
 ** @author	Borg Enders
 **
 ** @version	0.6.1
 **
 **/
void
Ai::poverty_cards_get_back(Hand const& cards)
{ 
  DEBUG_CALLING(INFO_AI && INFO_GAMEPLAY,
		"AI::poverty_cards_get_back(cards)");

  this->sorted_hand().add(cards);
  this->hand_set( this->hand() ); // to update playedcards

  DEBUG_RETURNING(VOID,
		  INFO_AI && INFO_GAMEPLAY,
		  "Ai::poverty_cards_get_back(cards)");
} // void Ai::poverty_cards_get_back(Hand const& cards)


/**********************************************************************
 *
 **    Card trump_card_limit()
 *
 **    Parameters:  
 *
 **    Result: limit card for some heuristic decisions
 *
 **    Version: Beta
 *
 **    Description:
 **       Results: 
 **          Normal    : Heart Queen
 **          Color Solo: Heart Queen
 **          Jack Solo : Spade Jack
 **          Queen Solo: Spade Queen
 **          King Solo : Spade King
 **          Queen-Jack: Club Jack
 **          King-Jack : Club Jack
 **          King-Queen: Club Queen
 **          Köhler    : Club Queen
 **          Meatless  : Diamond Ten
 *
 **********************************************************************/

Card Ai::trump_card_limit() const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");

  GameType gt=party.game().type();


  if  ( (gt==GAMETYPE::SOLO_CLUB) ||
	(gt==GAMETYPE::SOLO_HEART) ||
	(gt==GAMETYPE::SOLO_SPADE) ||
	(gt==GAMETYPE::SOLO_DIAMOND) ||
	silentMarriage_ )
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::TRUMPLIMIT_SOLOCOLOR),INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");
  }

  if  (gt==GAMETYPE::SOLO_JACK) 
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::TRUMPLIMIT_SOLOJACK),INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");
  }

  if  (gt==GAMETYPE::SOLO_QUEEN) 
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::TRUMPLIMIT_SOLOQUEEN),INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");
  }

  if  (gt==GAMETYPE::SOLO_KING) 
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::TRUMPLIMIT_SOLOKING),INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");
  }

  if   (gt==GAMETYPE::SOLO_QUEEN_JACK)      
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::TRUMPLIMIT_SOLOJACKQUEEN),INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");
  }
  if      (gt==GAMETYPE::SOLO_KING_JACK)       
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::TRUMPLIMIT_SOLOJACKKING),INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");
  }

  if (gt==GAMETYPE::SOLO_KING_QUEEN)
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::TRUMPLIMIT_SOLOQUEENKING),INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");
  }
  if    (gt==GAMETYPE::SOLO_KOEHLER) 
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::TRUMPLIMIT_SOLOKOEHLER),INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");
  }

  if (gt==GAMETYPE::SOLO_MEATLESS)
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::TRUMPLIMIT_MEATLESS),INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");
  }

  DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::TRUMPLIMIT_NORMAL),INFO_AI && INFO_OTHER_FUNCTION, "Card trump_card_limit()");
}


/**********************************************************************
 *
 **    Card lowest_trump_card_limit()
 *
 **    Parameters:  
 *
 **    Result: limit card for some heuristic decisions
 *
 **    Version: Beta
 *
 **    Description:
 **      Results:
 **        Normal    : Diamond jack
 **        Color Solo: Diamond jack
 **        Jack Solo : Heart jack
 **        Queen Solo: heart Queen
 **        King Solo : heart king
 **        Queen-jack: Spade jack
 **        King-jack : Spade jack
 **        King-Queen: Spade Queen
 **        Koehler   : Spade Queen
 **        Meatless  : Diamond Queen
 *
 *
 **********************************************************************/

Card Ai::lowest_trump_card_limit() const
{
  DEBUG_CALLING(INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");

  GameType gt=party.game().type();


  if  ( (gt==GAMETYPE::SOLO_CLUB) ||
	(gt==GAMETYPE::SOLO_HEART) ||
	(gt==GAMETYPE::SOLO_SPADE) ||
	(gt==GAMETYPE::SOLO_DIAMOND) ||
	silentMarriage_ )
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::LOWEST_TRUMPLIMIT_SOLOCOLOR),INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");
  }

  if  (gt==GAMETYPE::SOLO_JACK) 
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::LOWEST_TRUMPLIMIT_SOLOJACK),INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");
  }

  if  (gt==GAMETYPE::SOLO_QUEEN) 
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::LOWEST_TRUMPLIMIT_SOLOQUEEN),INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");
  }

  if  (gt==GAMETYPE::SOLO_KING) 
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::LOWEST_TRUMPLIMIT_SOLOKING),INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");
  }

  if   (gt==GAMETYPE::SOLO_QUEEN_JACK)      
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::LOWEST_TRUMPLIMIT_SOLOJACKQUEEN),INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");
  }
  if      (gt==GAMETYPE::SOLO_KING_JACK)       
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::LOWEST_TRUMPLIMIT_SOLOJACKKING),INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");
  }

  if (gt==GAMETYPE::SOLO_KING_QUEEN)
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::LOWEST_TRUMPLIMIT_SOLOQUEENKING),INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");
  }
  if    (gt==GAMETYPE::SOLO_KOEHLER) 
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::LOWEST_TRUMPLIMIT_SOLOKOEHLER),INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");
  }

  if (gt==GAMETYPE::SOLO_MEATLESS)
  {
    DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::LOWEST_TRUMPLIMIT_MEATLESS),INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");
  }

  DEBUG_RETURNING(this->Aiconfig::value(Aiconfig::LOWEST_TRUMPLIMIT_NORMAL),INFO_AI && INFO_OTHER_FUNCTION, "Card LOWEST_trump_card_limit()");
}

/**********************************************************************
 *
 **    Player const* genscher_partner()
 *
 **    Parameters:  
 *
 **    Result: Genscher Partner
 *
 **    Version: 0.5.4
 *
 **    Description:
 **      
 *
 *
 **********************************************************************/

Player const* Ai::genscher_partner()
{
  Player const* res = NULL;

  // first find original teammate
  for ( unsigned i = 0; i < this->game().playerno(); i++ ) {
    if( this->teamofplayer( this->game().player(i) ) == this->team() &&
	this->game().player(i).no() != this->no())
    {	
      res = &(this->game().player(i));
      break;
    }
  }
  // check Specialpoints
  std::vector<int> bonusPoints( this->game().playerno(), 0 );
  for( unsigned i = 0; i < trick_p.size(); i++ )
  {
    // DK: if hinzugefuegt
    if (!trick_p[i].isempty())
      bonusPoints[ trick_p[i].winnerplayer().no() ] += trick_p[i].specialpoints().size() * 30;
  }

  // secure victory
  unsigned int points = 0;
  bool found = false;
  if ( this->game().points_of_team( this->team() ) < 120 )
    for ( unsigned i = 0; i < this->game().playerno(); i++ ) 
    {
      if(   this->game().points_of_player( this->game().player(i) ) 
	    + this->game().points_of_player( this->game().player(this->no()) ) 
	    > 120 && this->game().player(i).no() != this->no() &&
	    this->game().points_of_player( this->game().player(i) )
	    + bonusPoints[ this->game().player(i).no() ] > points )
      {	
	res = &(this->game().player(i));
	points = this->game().points_of_player( this->game().player(i) )
	  + bonusPoints[ this->game().player(i).no() ] ;
	found = true;

      }
    }
  if( found )
    return res;

  for ( unsigned i = 0; i < this->game().playerno(); i++ ) 
  {
    if( this->game().points_of_player( this->game().player(i) )
	+ bonusPoints[ this->game().player(i).no() ]  > points &&
	this->game().player(i).no() != this->no() )
    {  
      points = this->game().points_of_player( this->game().player(i) )
	+ bonusPoints[ this->game().player(i).no() ] ;
      res = &(this->game().player(i));
    }
  }

  Announcement highest_announcement = ANNOUNCEMENT::NOANNOUNCEMENT;
  // calculate highest announcements of this team
  for( unsigned i = 0; i < this->game().playerno(); i++ )
  {
    Player const& pl = this->game().player( i );
    if( this->game().team( this->game().player( i ) ) == this->team() )
    {
      if( pl.announcement() > highest_announcement )
	highest_announcement = pl.announcement();
    }
  }

  if ( highest_announcement >= ANNOUNCEMENT::NO0 &&
       this->game().hastrick( opposite( this->team() ) ) ) 
    return res;

  if ( highest_announcement >= ANNOUNCEMENT::NO30 &&
       this->game().points_of_team( opposite( this->team() ) ) >= 30 )
    return res;

  if ( highest_announcement >= ANNOUNCEMENT::NO60 &&
       this->game().points_of_team( opposite( this->team() ) ) >= 60 )
    return res;

  if ( highest_announcement >= ANNOUNCEMENT::NO90 &&
       this->game().points_of_team( opposite( this->team() ) ) >= 90 )
    return res;

  // don't use genscher
  return NULL;
}

