/**********************************************************************
 *
 *   FreeDoko a Doppelkopf-Game
 *    
 *   Copyright (C) 2001-2006  by Diether Knof and Borg Enders
 *
 *   This program is free software; you can redistribute it and/or 
 *   modify it under the terms of the GNU General Public License as 
 *   published by the Free Software Foundation; either version 2 of 
 *   the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details. 
 *   You can find this license in the file 'gpl.txt'.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
 *   MA  02111-1307  USA
 *
 *  Contact:
 *    Diether Knof dknof@gmx.de
 *    Borg Enders  borg@borgsoft.de
 *
 ********************************************************************/ 

#include "constants.h"

#include "heuristics.h"

#include "HeuristicInterface.h"
#include "../../game/game.h"
#include "../../party/party.h"
#include "../../party/rule.h"
#include "../../card/trick.h"

unsigned number_of_no_trumps_per_color();
Card lowest_card(Hand const& hand, Card::Value const cv,
		 HeuristicInterface const& hi);
Card lowest_jack(Hand const& hand, HeuristicInterface const& hi);
Card lowest_queen(Hand const& hand, HeuristicInterface const& hi);
Card highest_queen(Hand const& hand, HeuristicInterface const& hi);
Card highest_card(Hand const& hand, Card::Value const cv,
		  HeuristicInterface const& hi);
bool better_points_optimize(HandCard const& old_card,
			    HandCard const& test_card,
			    HeuristicInterface const& hi);
Card lowest_color_card(Trick const& t, Hand h);


/**********************************************************************
 *
 **    int number_of_no_trumps_per_color()
 *
 **    Parameters:  number of cards per miscolor
 *
 **    Result: 
 *
 **    Version: Beta
 *
 **    Description:
 *
 *
 **********************************************************************/
unsigned
number_of_no_trumps_per_color()
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"number_of_no_trumps_per_color()");

  unsigned i;  
  unsigned notrump_per_color=0;

  // calculate number of no trump cards per color
  // only one color can be trump
  // if spade is trump caculate with club otherwise with spade
  if( Card( Card::SPADE, Card::ACE ).istrump( ::party.game() ) )
  {
    for ( i = 0;  i < 2 * ::party.rule()(Rule::NUMBER_OF_CARD_VALUES); i += 2 )
      if( !Card( Card::InttoColor(Card::CLUB), Card::InttoValue(i) ).istrump( 
									     ::party.game() ) 
	)
	notrump_per_color+=2;
  } else {
    for ( i = 0;  i < 2 * ::party.rule()(Rule::NUMBER_OF_CARD_VALUES); i += 2 )
      if( !Card( Card::InttoColor( Card::SPADE ), Card::InttoValue(i) ).istrump(
										::party.game() )
	)
	notrump_per_color+=2;
  }

  DEBUG_RETURNING( notrump_per_color,
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "number_of_no_trumps_per_color()" );

} // unsigned number_of_no_trumps_per_color()

/**
 ** returns a card to play for the bride in an open marriage
 **
 ** @param	trick	the current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play (or Card())
 **
 ** @author	Borg Enders
 **
 ** @version	0.7.0
 **/
Card
Heuristics::play_to_marry( Trick const& trick,
			  HeuristicInterface const& hi )
{
  if( !(   hi.game().type() == GAMETYPE::MARRIAGE 
	&& hi.game().soloplayer().no() == hi.no()
       )
    )
    return Card();

  Card::Color color = Card::NOCARDCOLOR;

  switch( hi.game().marriage_selector() ) 
  {
  case MARRIAGE_SELECTOR::TEAM_SET:
    return Card();

  case MARRIAGE_SELECTOR::FIRST_FOREIGN:
    if( trick.isempty() )
      return Card();

    return Heuristics::choose_pfund_card( trick, hi );

  case MARRIAGE_SELECTOR::FIRST_TRUMP:
    if(   !trick.isempty()
       && trick.startcard().istrump() )
      return Heuristics::choose_pfund_card( trick, hi );

    if( trick.isempty() ) 
    {
      // take a 'good' card
      if(   hi.hand().numberoftrumpaces() > 0 
	 && !hi.hand().has_swines()
	)
	return Card(hi.game().trumpcolor(), Card::ACE);

      if(  hi.hand().numberof( Card( hi.game().trumpcolor(), Card::TEN ) ) > 0 )
	return Card(hi.game().trumpcolor(), Card::TEN);

      return Heuristics::lowest_best_trump_card(trick, hi.hand(), hi);
    } // if (trick.startplayer().no() == hi.no())
    break;

  case MARRIAGE_SELECTOR::FIRST_CLUB:
    color = Card::CLUB;
  case MARRIAGE_SELECTOR::FIRST_SPADE:
    if( color == Card::NOCARDCOLOR )
      color = Card::SPADE;
  case MARRIAGE_SELECTOR::FIRST_HEART:
    if( color == Card::NOCARDCOLOR )
      color = Card::HEART;

    if(   !trick.isempty()
       && trick.startcard().tcolor() == color
      )
      return Heuristics::choose_pfund_card( trick, hi );

    if( trick.isempty() ) 
    {
      vector<Card> cards;
      cards.push_back(Card(color, Card::TEN));
      cards.push_back(Card(color, Card::KING));
      cards.push_back(Card(color, Card::NINE));
      cards.push_back(Card(color, Card::ACE));
      for( vector<Card>::const_iterator c = cards.begin();
	  c != cards.end();
	  ++c )
	if( hi.hand().numberof( *c ) > 0 )
	  return *c;
    }
    break;

  case MARRIAGE_SELECTOR::FIRST_COLOR:
    if(    trick.startplayer().no() != hi.no()
       && !trick.startcard().istrump()
      )
      return Heuristics::choose_pfund_card( trick, hi );

    if( trick.startplayer().no() == hi.no() )
      return lowest_color_card(trick, hi.hand());

    break;

  } // switch(marriage_selector)

  return Card();
} // static Card Heuristics::play_to_marry(Trick trick, HeuristicInterface hi)


/**********************************************************************
 *
 **    Card Heuristics::choose_ace(Trick const& t,HeursticInterface const& hi)
 *
 **    Parameters:  actual played trick, HeuristicInterface who plays next card
 *
 **    Result: an Color Ace, if there is a good one
 **            otherwise a card with Card::NOCARDVALUE and Card::NOCARDCOLOR
 *
 **    Version: Beta
 *
 **    Description:
 **      for a first card in a trick play a color Ace,
 **      if there are still enough cards of this color in the game
 **      and none of this colors is already been jabbed.
 **      choose then that ace with most cards of that color
 **      still in the game
 *
 *
 **********************************************************************/
Card
Heuristics::choose_ace( Trick const& t, HeuristicInterface const& hi )
{

  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::choose_ace()");

  // I'am not the start Player
  if( !t.isstartcard() )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::choose_ace()" );

  Card c = Card();
  unsigned i, h;
  vector<int> numberofcards( party.rule()(Rule::NUMBER_OF_CARD_COLORS), 0 );
  unsigned bestcolor = 0;


  int notrump_per_color = number_of_no_trumps_per_color();


  // calculate number of played no Trump cards per color
  for (vector<Card>::const_iterator
       c = hi.game().rule().valid_cards().begin();
       c != hi.game().rule().valid_cards().end();
       ++c )
    if( !c->istrump( hi.game() ) )
      numberofcards[ c->color() ] += hi.playedcard( *c );

  HandCards const ha = hi.hand().validcards( t );

  for ( i = 0; i < ha.cardsnumber(); i++ ) 
  {
    // cards still in game of this color

    h = notrump_per_color - numberofcards[ ha.card( i ).color() ];
    if( Card( ha.card(i).color(), Card::TEN ) == Card::DOLLE && 
       hi.game().rule()(Rule::DOLLEN) )
      h -= 2;

    // find best ace, which color, which was not already jabbed, or which was
    // last jabbed by an member of own team
    // where are for each player still one card or more in the game
    // and choose than that ace with most cards of its color still in the game
    if(    ha.card(i).value() == Card::ACE
       && !ha.card(i).istrump()
       && h + 1 >= hi.game().playerno()
       && (  h > bestcolor 
	   || (    h  == bestcolor 
	       && hi.hand().numberof( ha.card(i) ) < hi.hand().numberof( c )
	      ) 
	   ||   c == Card() 
	  )
       &&(   !hi.colorjabbed( ha.card(i).color() )
	  || (    hi.jabbedbyownteam( ha.card(i).color() ) 
	      && !hi.jabbedbyotherteam( ha.card(i).color()  ) 
	     )
	 )
      )
    {
      bestcolor = h;
      c = ha.card(i);
    }
  }

  DEBUG_RETURNING( c,
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::choose_ace()");

}

/**********************************************************************
 *
 **    Card lowest_card(Hand h,Card::Value cv)
 *
 **    Parameters:
 *
 **    Result: finds lowest card with value cv
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/
Card
lowest_card( Hand const& h, Card::Value const cv, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::lowest_card()");

  Card c=Card();

  unsigned i;

  for( i = 0; i < h.cardsnumber(); i++ )
  {
    // find any card
    if( h.card(i).value() ==cv )
    {
      c = h.card(i);
      break;
    }
  }

  if( c == Card() ) 
  {
    DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::lowest_jack()" );
  }

  for( i = 0; i < h.cardsnumber(); i++ )
  {
    // find lowest card
    if(   h.card(i).value() == cv 
       && h.card(i).less(c) )
      c = h.card(i);
  }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		  "Heuristics::lowest_card()" );
}


/**********************************************************************
 *
 **    Card lowest_card(Hand h,Card::Value cv)
 *
 **    Parameters:
 *
 **    Result: finds lowest card with value cv
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/
Card
highest_card( Hand const& h, Card::Value const cv, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::lowest_card()");

  Card c = Card();

  unsigned i;

  for( i = 0; i < h.cardsnumber(); i++ )
  {
    // find any card
    if( h.card(i).value() == cv )
    {
      c = h.card(i);
      break;
    }
  }

  if( c == Card() ) 
  {
    DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::lowest_jack()" );
  }

  for( i = 0; i < h.cardsnumber(); i++ )
  {
    // find highest card
    if(   h.card(i).value() == cv 
       && !h.card(i).less(c) )
      c=h.card(i);
  }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::lowest_card()" );
}


/**********************************************************************
 *
 **    Card lowest_jack(Hand h)
 *
 **    Parameters:
 *
 **    Result: finds lowest jack on Hand h
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/
Card
lowest_jack( Hand const& h, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::lowest_jack()");

  DEBUG_RETURNING( lowest_card( h, Card::JACK, hi ),
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::lowest_jack()");
}

/**********************************************************************
 *
 **    Card lowest_queen(Hand h)
 *
 **    Parameters:
 *
 **    Result: finds lowest queen on Hand h
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card
highest_queen( Hand const& h, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::lowest_queen()");

  DEBUG_RETURNING( highest_card( h, Card::QUEEN, hi ),
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::lowest_queen()");
}

/**********************************************************************
 *
 **    Card lowest_queen(Hand h)
 *
 **    Parameters:
 *
 **    Result: finds lowest queen on Hand h
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card
lowest_queen( Hand const& h, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::lowest_queen()");

  DEBUG_RETURNING( lowest_card( h, Card::QUEEN, hi ),
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::lowest_queen()");
}

/**
 ** checks if there are any trumps in trick t
 **
 ** @author Borg Enders
 **
 **/   
bool 
trumpInTrick( Trick t )
{
  bool trumpintrick = false;

  for( unsigned i = 0 ; i < t.actcardno(); i++ )
  {
    trumpintrick = trumpintrick || t.card( i ).istrump();
  }

  return trumpintrick;
}

/**
 ** checks if all remaining player in the trick are of my team
 **/
bool
checkAllMyTeam( Trick const& t, HeuristicInterface const& hi )
{
  bool allmyteam = true;

  // take a look if all players coming in this trick are of mine team
  for( unsigned i = t.actcardno(); i < hi.game().playerno(); i++ )
    allmyteam = allmyteam && ( hi.team() == hi.teaminfo( t.player_of_card( i ) ) );
  return allmyteam;
}
/**********************************************************************
 *
 **    Card Heuristics::best_winning_card(Trick const& t, HeursticInterface const& hi)
 *
 **    Parameters:  actual trick, HeuristicInterface who plays next card
 **                 point modifikator to add to points of t 
 *
 **    Result: finds best card on Hand of hi, which wins this trick, 
 **            non trump ace or lowest trump
 **            greater than jack, only in lastcard lower trumps are allowed
 **            the card will be chosen taking the value of the trick in account
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card 
Heuristics::best_winning_card_single_picture( 
					     Trick const& t, Card::Value v, HeuristicInterface const& hi, unsigned pointmodi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()");


  if ( t.isstartcard() ) 
    DEBUG_RETURNING( Card(), INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		    "Heuristics::best_winning_card()");

  Trick tr;
  Card c = Card();
  HandCards const ha = hi.hand().validcards( t );
  unsigned i;

  bool allmyteam = checkAllMyTeam( t, hi );

  // find any card that wins this trick greater or equal to lowest_trump_card_limit
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    tr = t;
    tr += ha.card( i );
    if(   tr.winnerplayer().no() == hi.no() 
       && t.isvalid( ha.card(i), hi.hand() )  
       && (   hi.lowest_trump_card_limit().less( ha.card( i ) ) 
	   || allmyteam 
	  )  
      )
    {
      c = ha.card(i);
      break;
    }
  }
  // if can't win this trick and I'am not the last player: that's it
  if( c == Card() && !t.islastcard() )  
  {
    DEBUG_RETURNING(c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		    "Heuristics::best_winning_card()");
  }

  bool trumpintrick = trumpInTrick( t );

  // or this is a color trick of a first color run with my card the first trump
  allmyteam =    allmyteam 
    || (   t.actcardno() > 1
	&& hi.color_runs(t.startcard().color()) == 0
	&& !t.startcard().istrump()
	&& !trumpintrick 
       );

  // find a better card
  for( i = 0; i < ha.cardsnumber(); i++ )
  {

    tr = t;
    tr += ha.card( i );
    if(   tr.winnerplayer().no() == hi.no() 
       && t.isvalid( ha.card( i ), hi.hand() ) 
      )
    {
      // non trump ace is always the best choice if color is not already jabbed
      if(   ha.card(i).value() == Card::ACE 
	 && !ha.card(i).istrump() 
	 && (   !hi.colorjabbed( ha.card(i).color() )
	     || hi.jabbedbyownteam( ha.card(i).color() ) 
	     || allmyteam 
	    )
	)
      {
	c=ha.card(i);
	break;
      } 

      if ( ha.card(i).istrump() )
      {

	// find lowest trump
	if( (    hi.lowest_trump_card_limit().less( ha.card(i) ) 
	     || t.islastcard() 
	     || allmyteam
	    )
	   && ha.card(i).less( c )
	  )
	{

	  c = ha.card(i);
	  continue;
	}

      }// if card istrump
    } // if winneris
  }

  // if last card or first_color_run of trick let's check for a better card
  // Ace, ten, king, nine

  if(   t.islastcard() 
     || allmyteam
     ||(    !t.isstartcard()
	&& !t.startcard().istrump()
	&& hi.color_runs(t.startcard().color()) < 1
	&& (   t.startcard().color()!= Card::HEART
	    || !HandCard( hi.hand(), Card::HEART, Card::TEN ).isdolle() 
	   )	           
       )
    )
  {

    for( i = 0; i < ha.cardsnumber(); i++ )
    {
      tr = t;
      tr += ha.card( i );


      if(   tr.winnerplayer().no() == hi.no() 
	 && t.isvalid( ha.card( i ), hi.hand() )
	)
      {


	// ace is always the best card 
	if(  !ha.card(i).isswine() 
	   && ha.card(i).value()==Card::ACE 
	   && (    allmyteam 
	       || !ha.card( i ).istrump() 
	       || t.islastcard()
	      )
	  )
	{          
	  c = ha.card(i);
	  break;
	} 

	if(   ha.card(i).value() == Card::TEN 
	   && !ha.card(i).isdolle() 
	   && (     t.islastcard() 
	       || allmyteam 
	       || (  hi.color_runs( t.startcard().color() ) == 0 
		   && !t.startcard().istrump() 
		  )
	      )
	  )
	{      
	  c = ha.card(i);
	  continue;
	}

	if(   ha.card( i ).value() != v 
	   && ha.card( i ).value() < Card::TEN )
	{        
	  c = ha.card(i);             
	  continue;
	}

      }
    }
  }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		  "Heuristics::best_winning_card()" );
}



/**********************************************************************
 *
 **    Card Heuristics::best_winning_card(Trick const& t, HeursticInterface const& hi)
 *
 **    Parameters:  actual trick, HeuristicInterface who plays next card
 **                 point modifikator to add to points of t 
 *
 **    Result: finds best card on Hand of hi, which wins this trick, 
 **            non trump ace or lowest trump
 **            greater than jack, only in lastcard lower trumps are allowed
 **            the card will be chosen taking the value of the trick in account
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card 
Heuristics::best_winning_card_double_picture( 
					     Trick const& t, Card::Value v1, Card::Value v2, 
					     HeuristicInterface const& hi, unsigned pointmodi )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		"Heuristics::best_winning_card()" );


  if ( t.isstartcard() ) 
    DEBUG_RETURNING( Card(), INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		    "Heuristics::best_winning_card()" );

  Trick tr;
  Card c = Card();
  HandCards const ha = hi.hand().validcards(t);
  unsigned i;

  bool allmyteam = checkAllMyTeam( t, hi );;


  // find any card that wins this trick greater or equal to lowest_trump_card_limit
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    tr = t;
    tr += ha.card(i);
    if(   tr.winnerplayer().no() == hi.no() 
       && t.isvalid( ha.card(i),hi.hand() )  
       && (   hi.lowest_trump_card_limit().less( ha.card(i) ) 
	   || allmyteam 
	  )
      )
    {
      c = ha.card(i);
      break;
    }
  }

  // if can't win this trick and I'am not the last player: that's it
  if( c == Card() && !t.islastcard() )  
  {
    DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		    "Heuristics::best_winning_card()" );
  }

  bool trumpintrick = trumpInTrick( t );


  // or this is a color trick of a first color run with my card the first trump
  allmyteam =    allmyteam 
    ||(   t.actcardno() > 1
       && hi.color_runs(t.startcard().color()) == 0
       && !t.startcard().istrump() 
       && !trumpintrick 
      );

  // find a better card
  for ( i = 0; i < ha.cardsnumber(); i++ )
  {
    tr = t;
    tr += ha.card( i );
    if(   tr.winnerplayer().no() == hi.no() 
       && t.isvalid( ha.card(i),hi.hand() ) 
      )
    {
      // non trump ace is always the best choice if color is not already jabbed
      if(   ha.card(i).value() == Card::ACE 
	 && !ha.card(i).istrump() 
	 && (   !hi.colorjabbed( ha.card(i).color() )
	     || hi.jabbedbyownteam(ha.card(i).color()) 
	     || allmyteam
	    )
	)
      {
	c = ha.card( i );
	break;
      } 

      if( ha.card(i).istrump() )
      {

	// find lowest trump
	if( (   hi.lowest_trump_card_limit().less( ha.card(i) ) 
	     || t.islastcard() 
	     || allmyteam 
	    )
	   && ha.card(i).less(c)
	  )
	{
	  c = ha.card(i);
	  continue;
	}

      }// if card istrump

    } // if winneris
  }

  // if last card or first_color_run of trick let's check for a better card
  // Ace, ten, king, nine
  if(   t.islastcard() 
     || allmyteam
     ||(   !t.isstartcard()
	&& !t.startcard().istrump()
	&& hi.color_runs(t.startcard().color())<1
	&& (   t.startcard().color()!= Card::HEART
	    || !HandCard( hi.hand(), Card::HEART, Card::TEN ).isdolle() 
	   )
       )
    )
  {

    for( i = 0; i < ha.cardsnumber(); i++ )
    {
      tr = t;
      tr += ha.card(i);

      if(   tr.winnerplayer().no()==hi.no() 
	 && t.isvalid(ha.card(i),hi.hand())
	)
      {
	// ace is always the best card 
	if(   !ha.card(i).isswine() 
	   &&  ha.card(i).value()==Card::ACE 
	   && (   allmyteam 
	       || !ha.card(i).istrump() 
	       || t.islastcard()
	      )
	  )
	{          
	  c = ha.card(i);
	  break;
	} 

	if(   ha.card(i).value() == Card::TEN 
	   && !ha.card(i).isdolle() 
	   && (   t.islastcard() 
	       || allmyteam 
	       || (   hi.color_runs(t.startcard().color()) == 0 
		   && !t.startcard().istrump() 
		  )
	      )
	  )
	{      
	  c = ha.card(i);
	  continue;
	}

	if(    ha.card(i).value() != v1 
	   && ha.card(i).value() != v2 
	   && ha.card(i).value() < Card::TEN )
	{        
	  c = ha.card(i);             
	  continue;
	}

      }
    }
  }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		  "Heuristics::best_winning_card()");
}



/**********************************************************************
 *
 **    Card Heuristics::best_winning_card(Trick const& t, HeursticInterface const& hi)
 *
 **    Parameters:  actual trick, HeuristicInterface who plays next card
 **                 point modifikator to add to points of t 
 *
 **    Result: finds best card on Hand of hi, which wins this trick, 
 **            non trump ace or lowest trump
 **            greater than jack, only in lastcard lower trumps are allowed
 **            the card will be chosen taking the value of the trick in account
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card 
Heuristics::best_winning_card_triple_picture( Trick const& t, 
					     HeuristicInterface const& hi, unsigned pointmodi )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		"Heuristics::best_winning_card()" );


  if ( t.isstartcard() ) 
    DEBUG_RETURNING( Card(), INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		    "Heuristics::best_winning_card()" );

  Trick tr;
  Card c = Card();
  HandCards const ha = hi.hand().validcards( t );
  unsigned i;

  bool allmyteam = checkAllMyTeam( t, hi );;

  // find any card that wins this trick greater or equal to lowest_trump_card_limit
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    tr = t;
    tr += ha.card( i );

    if(   tr.winnerplayer().no() == hi.no() 
       && t.isvalid( ha.card(i), hi.hand() )  
       && (  hi.lowest_trump_card_limit().less( ha.card(i) ) 
	   || allmyteam 
	  )
      )
    {
      c = ha.card(i);
      break;
    }
  }
  // if can't win this trick and I'am not the last player: that's it
  if( c==Card() && !t.islastcard() )  
  {
    DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		    "Heuristics::best_winning_card()" );
  }

  bool trumpintrick = trumpInTrick( t );

  // or this is a color trick of a first color run with my card the first trump
  allmyteam =    allmyteam 
    || (   t.actcardno() > 1
	&& hi.color_runs(t.startcard().color()) == 0
	&& !t.startcard().istrump()
	&& !trumpintrick 
       );

  // find a better card

  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    tr = t;
    tr += ha.card(i);
    if(   tr.winnerplayer().no() == hi.no() 
       && t.isvalid( ha.card( i ), hi.hand() ) )
    {
      // non trump ace is always the best choice if color is not already jabbed
      if(   ha.card(i).value() == Card::ACE 
	 && !ha.card(i).istrump() 
	 && (   !hi.colorjabbed( ha.card( i ).color() )
	     || hi.jabbedbyownteam( ha.card( i ).color() ) 
	     || allmyteam
	    )
	)
      {
	c = ha.card( i );
	break;
      } 

      if( ha.card(i).istrump() )
      {

	// find lowest trump
	if( (   hi.lowest_trump_card_limit().less( ha.card(i) ) 
	     || t.islastcard() 
	     || allmyteam )
	   && ha.card(i).less(c) )
	{

	  c = ha.card( i );
	  continue;
	}

      }// if card istrump

    } // if winneris
  }



  // if last card or first_color_run of trick let's check for a better card
  // Ace, ten, king, nine
  if(   t.islastcard() 
     || allmyteam
     ||(   !t.isstartcard()
	&& !t.startcard().istrump()
	&& hi.color_runs( t.startcard().color() ) < 1
	&& (   t.startcard().color()!= Card::HEART 
	    || !HandCard( hi.hand(), Card::HEART, Card::TEN ).isdolle() 
	   )
       )
    )
  {

    for( i = 0; i < ha.cardsnumber(); i++ )
    {
      tr = t;
      tr += ha.card( i );

      if(   tr.winnerplayer().no() == hi.no() 
	 && t.isvalid( ha.card( i ), hi.hand() ) )
      {

	// ace is always the best card 
	if(   !ha.card(i).isswine() 
	   && ha.card(i).value() == Card::ACE 
	   && (   allmyteam 
	       || !ha.card(i).istrump() 
	       || t.islastcard()
	      )
	  )
	{          
	  c = ha.card(i);
	  break;
	}	 

	if(    ha.card(i).value() == Card::TEN 
	   && !ha.card(i).isdolle() 
	   && (   t.islastcard() 
	       || allmyteam 
	       || (   hi.color_runs( t.startcard().color()) == 0 
		   && !t.startcard().istrump() 
		  )
	      )
	  )
	{      
	  c = ha.card( i );
	  continue;
	}

	if( ha.card(i).value() == Card::NINE )
	{        
	  c = ha.card(i);             
	  continue;
	}

      }
    }
  }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		  "Heuristics::best_winning_card()" );
}


/**********************************************************************
 *
 **    Card Heuristics::best_winning_card(Trick const& t, HeursticInterface const& hi)
 *
 **    Parameters:  actual trick, HeuristicInterface who plays next card
 **                 point modifikator to add to points of t 
 *
 **    Result: finds best card on Hand of hi, which wins this trick, 
 **            non trump ace or lowest trump
 **            greater than jack, only in lastcard lower trumps are allowed
 **            the card will be chosen taking the value of the trick in account
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card Heuristics::best_winning_card(Trick const& t, HeuristicInterface const& hi, unsigned pointmodi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()");

  switch (hi.game().type()) 
  {
  case GAMETYPE::SOLO_JACK:
    DEBUG_RETURNING( best_winning_card_single_picture( t, Card::JACK, hi, pointmodi ), 
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()" );
  case GAMETYPE::SOLO_QUEEN:
    DEBUG_RETURNING( best_winning_card_single_picture( t, Card::QUEEN, hi, pointmodi ),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()" );
  case GAMETYPE::SOLO_KING: 
    DEBUG_RETURNING( best_winning_card_single_picture( t, Card::KING, hi, pointmodi ),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()" );
  case GAMETYPE::SOLO_QUEEN_JACK:                       
    DEBUG_RETURNING( best_winning_card_double_picture( t, Card::JACK, Card::QUEEN, hi, pointmodi ),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()" );
  case GAMETYPE::SOLO_KING_JACK:
    DEBUG_RETURNING( best_winning_card_double_picture( t, Card::JACK, Card::KING, hi, pointmodi ),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()" );                      
  case GAMETYPE::SOLO_KING_QUEEN:
    DEBUG_RETURNING( best_winning_card_double_picture( t, Card::KING, Card::QUEEN, hi, pointmodi ),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()" );
  case GAMETYPE::SOLO_KOEHLER: 
    DEBUG_RETURNING( best_winning_card_triple_picture( t, hi, pointmodi ),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()" );
  default:
    break;
  }


  if( t.isstartcard() ) 
    DEBUG_RETURNING( Card(), INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		    "Heuristics::best_winning_card()" );

  Trick tr;
  HandCard c( hi.hand() );
  HandCards const ha = hi.hand().validcards( t );
  unsigned i;
  Game const& game = t.game();

  bool allmyteam = checkAllMyTeam( t, hi );


  unsigned tpoints = t.points() + pointmodi;

  // find any card that wins this trick greater or equal to lowest_trump_card_limit
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    tr = t;
    tr += ha.card( i );
    if(   tr.winnerplayer().no() == hi.no() 
       && t.isvalid( ha.card( i ), hi.hand() )  
       && (  hi.lowest_trump_card_limit().less( ha.card( i ) ) 
	   || allmyteam 
	  )
      )
    {
      if(   ha.card(i).value() == Card::QUEEN 
	 && tpoints > hi.value(Aiconfig::LIMITQUEEN) )
      {
	c = ha.card( i );
	break;
      } 
      if( ha.card(i).value() == Card::QUEEN )
	continue;

      if(  (    ha.card(i).isdolle() 
	    || ha.card(i).possible_swine() 
	    || ha.card(i).possible_hyperswine()
	   )
	 && tpoints > hi.value( Aiconfig::LIMITDOLLE )
	)
      {     
	c = ha.card( i );
	break;
      } 

      if(   ha.card(i).isdolle() 
	 || ha.card(i).possible_swine() 
	 || ha.card(i).possible_hyperswine() )
	continue;

      c = ha.card( i );
      break;
    } // if()
  } // for(i)


  // if can't win this trick and I'am not the last player: that's it
  if( c == Card() && !t.islastcard() )  
  {
    DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		    "Heuristics::best_winning_card()" );
  }

  bool trumpintrick = trumpInTrick( t );

  // or this is a color trick of a first color run with my card the first trump
  allmyteam =   allmyteam 
    ||(   t.actcardno() > 1
       && hi.color_runs(t.startcard().color()) == 0
       && !t.startcard().istrump() 
       && !trumpintrick 
       && (   t.startcard().color()!= Card::HEART 
	   || !HandCard(hi.hand(), Card::HEART, Card::TEN ).isdolle() 
	  )
      );

  // find a better card
  for( i = 0; i < ha.cardsnumber(); i++ )
  {


    tr = t;
    tr += ha.card( i );
    if(   tr.winnerplayer().no() == hi.no() 
       && t.isvalid( ha.card( i ),hi.hand() ) 
      )
    {
      // non trump ace is always the best choice if color is not already jabbed
      if(   ha.card(i).value() == Card::ACE 
	 && !ha.card(i).istrump() 
	 && (   !hi.colorjabbed( ha.card( i ).color() )
	     || hi.jabbedbyownteam( ha.card( i ).color() ) 
	     || allmyteam
	    )
	)
      {
	c = ha.card( i );
	break;
      } 

      if( ha.card( i ).istrump() )
      {

	// find lowest trump
	if(     t.islastcard()
	   && ha.card(i).value() < Card::JACK
	   && ha.card( i ).less( c ) 
	  )
	{ 
	  c = ha.card( i );
	  continue;
	}

	if( ha.card(i).value() >= Card::JACK )
	{
	  if(   !t.isstartcard() 
	     && ha.card(i).value() < Card::QUEEN
	     && !t.startcard().istrump() 
	     && (   !hi.colorjabbed( t.startcard().color() )
		 || tpoints <= hi.value( Aiconfig::LIMITQUEEN )
		 || ha.numberof( Card::QUEEN ) < 2
		)
	    )
	  {
	    c = ha.card(i);
	    continue;
	  }
	  if(    ha.card(i).value() == Card::QUEEN
	     && tpoints > hi.value( Aiconfig::LIMITQUEEN ) 
	     && (  (    c.value() < Card::QUEEN
		         && hi.color_runs( t.startcard().color() ) != 0 )
		    || (    ha.card(i).less( c )
		      && (     c != Card( Card::CLUB, Card::QUEEN )
		            || ha.card(i).less( Card( Card::SPADE, Card::QUEEN ) )  
		            || hi.game().player( hi.no() ).announcement() != ANNOUNCEMENT::NOANNOUNCEMENT
		          )
		           
			    )
	         )
	       )
	  {

	    c = ha.card( i );
	    continue;
	  }

	  if(  
	     (   ha.card(i).isdolle() 
	      || ha.card(i).possible_swine() 
	      || ha.card(i).possible_hyperswine() 
	     )
	     && (   ha.card(i).less(c) 
		 || (   tpoints > hi.value( Aiconfig::LIMITDOLLE ) 
		     && c.value() < Card::QUEEN 
		     && !hi.color_runs( t.startcard().color() ) < 1
		     && !t.islastcard()
		    )
		)
	    ) 
	  {
	    c = ha.card( i );
	    continue;
	  }

	}//if card>jack
      }// if card istrump

    } // if winneris
  }


  // if last card or first_color_run of trick let's check for a better card
  // Ace, ten, king, nine
  if(   t.islastcard() 
     || allmyteam
     ||(   !t.isstartcard()
	&& !t.startcard().istrump()
	&& hi.color_runs( t.startcard().color() ) < 1
	&& (   t.startcard().color() != Card::HEART
	    || !HandCard( hi.hand(), Card::HEART, Card::TEN ).isdolle() 
	   )	  
       )
    )
  {

    for( i = 0; i < ha.cardsnumber(); i++ )
    {
      tr = t;
      tr += ha.card( i );


      if(   tr.winnerplayer().no() == hi.no() 
	 && t.isvalid( ha.card( i ), hi.hand() )
	)
      {

	// Maybe it's time to get the heart ten back home
	if(   ha.card(i).isdolle() 
	   &&(   (    hi.hand().numberoftrumps() < 5
		  &&  hi.hand().numberof( Card::DOLLE ) == 1
		  &&  hi.game().rule()(Rule::EXTRAPOINT_DOLLE_JABS_DOLLE) 
		  &&  hi.playedcard( Card::DOLLE ) == 0 
		 )
	      || (    hi.hand().numberoftrumps() < 6 
		  && hi.game().swines_owner() != NULL 
		  && hi.teaminfo( *hi.game().swines_owner() ) != hi.team() 
		 )
	      || (    hi.hand().numberoftrumps() < 6 
		  && hi.game().hyperswines_owner() != NULL 
		  && hi.teaminfo( *hi.game().hyperswines_owner() ) != hi.team() 
		 )
	     )
	  )
	{    
	  c = ha.card( i );
	  break;
	} 	 

	// ace is always the best card 
	if(   !ha.card(i).possible_swine()
	   && ha.card(i).value() == Card::ACE
	   && (    allmyteam 
	       || !ha.card(i).istrump() 
	       || t.islastcard()
	      )
	  )
	{          
	  c = ha.card( i );
	  break;
	} 

	if(    ha.card( i ).value() == Card::TEN 
	   && !ha.card(i).isdolle() 
	   && (   t.islastcard() 
	       || allmyteam 
	       || (   hi.color_runs( t.startcard().color() ) == 0
		   && !t.startcard().istrump() 
		  )
	      )
	  )
	{      
	  c = ha.card( i );
	  continue;
	}

	if(   ha.card( i ).value() == Card::KING
	   && game.type() != GAMETYPE::SOLO_KOEHLER
	   && !ha.card( i ).possible_hyperswine()
	  )
	{        
	  if( c.value()!=Card::TEN || c.isdolle() )
	  {
	    c = ha.card(i);             
	  }
	  continue;
	}
	if(   ha.card(i).value() == Card::NINE 
	   && !ha.card(i).possible_hyperswine() )
	{
	  if(  (   c.value() != Card::TEN
		&& c.value() != Card::KING 
		&& better_points_optimize( c, ha.card(i), hi )
	       )
	     || c.isdolle()
	     || c.possible_hyperswine() 	          
	    )
	  {
	    c = ha.card( i );
	  }
	  continue;
	}

	if(   c.istrump() 
	   && ha.card(i).istrump() 
	   && ha.card(i).less(c)  
	  )
	{
	  if(  (   c.value() != Card::TEN
		&& c.value() != Card::KING  
		&& better_points_optimize( c, ha.card(i), hi )
		&& (  c != Card( Card::CLUB, Card::QUEEN )
		     || hi.game().player( hi.no() ).announcement() != ANNOUNCEMENT::NOANNOUNCEMENT
		          )
	       )
	     || c.isdolle() 
	     || c.possible_hyperswine() 
	    )
	  { 
	    c = ha.card( i );
	  }
	  continue;
	}
      }
    }
  }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		  "Heuristics::best_winning_card()" );
}

/**********************************************************************
 *
 **    Card lowest_color_card(Trick const& t, Hand h)
 *
 **    Parameters:  actual trick, and hand of player
 *
 **    Result: finds lowest color card on Hand h
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card 
lowest_color_card( Trick const& t, Hand h )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::lowest_color_card()" );

  Card c = Card();
  HandCards const ha = h.validcards( t );
  unsigned i;

  // find any card that's allowed for this trick
  for( i =0; i < ha.cardsnumber(); i++ )
  {
    if( !ha.card( i ).istrump() )
    {
      c = ha.card( i );
      break;
    }
  }

  if( c == Card() ) 
  {
    DEBUG_RETURNING(c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::lowest_color_card()" );
  }

  // find a better card
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    if(   !ha.card(i).istrump()
       && c.value() <= ha.card( i ).value()
      )
      c = ha.card( i );
  }

  DEBUG_RETURNING(c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::lowest_color_card()" );
}

/**********************************************************************
 *
 **    Card lowest_color_card_meatless(Trick const& t, Hand h)
 *
 **    Parameters:  actual trick, and hand of player
 *
 **    Result: finds lowest color card on Hand h, wihich is the best 
 ** 	          in a meatless solo
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card 
lowest_color_card_meatless( Trick const& t, Hand h )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::lowest_color_card()" );

  Card c = Card();
  HandCards const ha = h.validcards( t );
  unsigned i;

  // find any card that's allowed for this trick
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    if( !ha.card( i ).istrump() )
    {
      c = ha.card( i );
      break;
    }
  }

  if( c == Card() ) 
  {
    DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::lowest_color_card()" );
  }

  // find a better card
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    if(   !ha.card( i ).istrump()
       && c.value() <= ha.card(i).value()
       && (   ha.numberof( ha.card(i).color() ) >  ha.numberof( c.color() ) 
	   || (   ha.numberof( ha.card(i).color(), Card::TEN ) == 0 
	       && ha.numberof( ha.card(i).color(), Card::ACE ) == 0 
	      )
	  )
      )
      c = ha.card(i);
  }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::lowest_color_card()");
}


/**********************************************************************
 *
 **    Card lowest_trump_card(Trick const& t, Hand h)
 *
 **    Parameters:  actual trick, and hand of player
 *
 **    Result: finds lowest trump card on Hand h
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card 
lowest_trump_card( Trick const& t, Hand h )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::lowest_trump_card()" );

  Card c = Card();
  HandCards const ha = h.validcards( t );
  unsigned i;

  // find any card that's allowed for this trick
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    if( ha.card( i ).istrump() )
    {
      c = ha.card( i );
      break;
    }
  }

  if( c == Card() ) 
  {
    DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::lowest_trump_card()" );
  }

  // find a better card
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    if(   ha.card(i).less( c ) 
       && ha.card(i).istrump()
      )
      c = ha.card( i );
  }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		  "Heuristics::lowest_trump_card()" );
}

/**********************************************************************
 *
 **    Card lowest_best_trump_card(Trick const& t, Hand h)
 *
 **    Parameters:  actual trick, and hand of player
 *
 **    Result: finds lowest trump card on Hand h, but gives only fuchs or ten back
 **            if there are better cards to keep
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card 
Heuristics::lowest_best_trump_card( Trick const& t, Hand const& h, 
				   HeuristicInterface const& hi )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::lowest_best_trump_card()" );

  HandCard c( h );
  HandCards const ha = hi.hand().validcards( t );
  unsigned i;

  // find any card that's allowed for this trick
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    if ( ha.card( i ).istrump() )
    {
      c = ha.card( i );
      break;
    }
  }

  if( c == Card() ) 
  {
    DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::lowest_best_trump_card()" );
  }

  // find a better card
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    if(   ha.card( i ).less( c ) 
       && ha.card( i ).istrump() 
       && !ha.card( i ).isfox() 
       && (   ha.card(i).value() != Card::TEN 
	   || c.isfox()
	  )
      )
      c = ha.card( i );
  }

  if( !c.less( hi.trump_card_limit() ) )
    // maybe a diamond ten isn't all that bad
  {
    for( i = 0; i < ha.cardsnumber(); i++ )
    {
      if(   ha.card( i ).less( c ) 
	 && ha.card( i ).istrump() 
	 && !ha.card( i ).isfox()
	)
	c = ha.card( i );
    }
  }

  if( !c.less( hi.trump_card_limit() ) )
    // maybe a diamond Ace isn't all that bad
  {
    for( i = 0; i < ha.cardsnumber(); i++ )
    {
      if(   ha.card( i ).less( c ) 
	 && ha.card(i).istrump()
	)
	c = ha.card( i );
    }
  }

  DEBUG_RETURNING(c,
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::lowest_best_trump_card()");
}

/**********************************************************************
 *
 **    Card Heuristics::choose_pfund_card(Trick t, HeursticInterface const& hi)
 *
 **    Parameters:  actual played trick, HeuristicInterface who plays next card
 *
 **    Result: result see description, otherwise
 **            result is a card with Card::NOCARDVALUE and Card::NOCARDCOLOR
 *
 **    Version: Beta
 *
 **    Description:
 **     best pfund on hand
 **      a trump Ass, color ten or Ace, trump ten, a king,
 **      a notrump queen or jack, nine
 *
 **********************************************************************/

Card 
Heuristics::choose_pfund_card( Trick const& trick, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::choose_pfund_card()");

  Trick t = trick;

  DEBUG_ASSERTION( !t.isempty(),
		  "Heuristics::choose_pfund_card():\n"
		  "  Called with empty trick");

  unsigned i;

  bool allmyteam = checkAllMyTeam( t, hi ) && hi.jabbedbyownteam( t.startcard().color() ) && !t.islastcard(); 


  Card c = Card();

  HandCards const ha = hi.hand().validcards( t );

  int notrump_per_color = number_of_no_trumps_per_color();

  if(   !t.startcard().istrump()
     && t.startcard().color() == Card::HEART 
     && ha.existstcolor( Card::HEART )
     && hi.game().rule()(Rule::DOLLEN))
    notrump_per_color -= 2;

  vector<int> numberofcards( party.rule()(Rule::NUMBER_OF_CARD_COLORS), 0 );

  // calculate number of played no Trump cards per color
  for( vector<Card>::const_iterator c = hi.game().rule().valid_cards().begin();
      c != hi.game().rule().valid_cards().end(); ++c )
    if( !c->istrump( hi.game() ) )
      numberofcards[ c->color() ] += hi.playedcard( *c );

  bool solo_check = 
    (   GAMETYPE::is_solo( hi.game().type() )  
     && hi.color_runs(t.startcard().color()) == 0
     && !t.startcard().istrump()
     && t.winnerplayer() == hi.game().soloplayer() 
     && t.winnercard().less( hi.trump_card_limit() ) 
     && !t.islastcard() ) ;


  // then find first trump ace
  for( i = 0; i < ha.cardsnumber(); i++ )
    if(   ha.card(i).value() == Card::ACE 
       && ha.card(i).istrump() 
       && (   t.islastcard() 
	   || ha.numberoftrumps() < 3 
	   || (   hi.color_runs(t.startcard().color()) == 0
	       && !t.startcard().istrump()
	      )
	   || (    !t.winnercard().less( Card(Card::CLUB,Card::QUEEN) ) // 2do see bugreport 57381
	        && hi.teamofplayer( t.winnerplayer() )== hi.team()  // better would be check in cardinformation now higherCardExists
	       )
	  ) 
       && !ha.card(i).possible_swine()
      )
    {
    	
      t += ha.card( i );
      if(   hi.teamofplayer( t.winnerplayer() )== hi.team()
	 && (    !hi.game().rule()(Rule::GENSCHER)
	     || hi.playedcard( Card(Card::DIAMOND, Card::KING ) ) > 0
	     || ha.numberoftrumps() < 4 
	    )
	)
      {          
	DEBUG_RETURNING( ha.card(i),
			INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			"Heuristics::choose_pfund_card()" );
      }
    }

  Card best_ace;
  // choose an ace of a color if the player has two aces
  for( vector<Card::Color>::const_iterator
      color = hi.game().rule().valid_card_colors().begin();
      color != hi.game().rule().valid_card_colors().end();
      ++color ) 
  {
    Card const local_ace( *color, Card::ACE );
#ifdef OUTDATED
    // Das mit den Klammern solltest Du noch einmal üben/nachprüfen!
    if(   (   hi.hand().numberof( local_ace ) == hi.game().rule()(Rule::NUMBER_OF_SAME_CARDS) )
       || (    hi.hand().numberof(local_ace) == 1
	   && numberofcards[local_ace.color()] > 3
	   && hi.game().type() != GAMETYPE::SOLO_MEATLESS 
	  )
       && !local_ace.istrump( hi.game() )
       && t.isvalid(local_ace, hi.hand())
       && (   hi.teamofplayer(t.winnerplayer()) == hi.team() 
	   || solo_check 
	   || allmyteam 
	  ) 
      )
#else
      if(   (   hi.hand().numberof( local_ace ) == hi.game().rule()(Rule::NUMBER_OF_SAME_CARDS)
	     || (    hi.hand().numberof(local_ace) == 1
		 && numberofcards[local_ace.color()] > 3
		 && hi.game().type() != GAMETYPE::SOLO_MEATLESS 
		) )
	 && !local_ace.istrump( hi.game() )
	 && t.isvalid(local_ace, hi.hand())
	 && (   hi.teamofplayer(t.winnerplayer()) == hi.team() 
	     || solo_check 
	     || allmyteam 
	    ) 
	)
#endif
      {
	if(   !best_ace
	   || numberofcards[ local_ace.color() ] > numberofcards[ best_ace.color()] 
	  )
	  best_ace = local_ace;
      }
  } // for (color \in valid_card_colors)

  if( best_ace )
    DEBUG_RETURNING( best_ace,
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::choose_pfund_card()");

  // or a ten of color
  for( i = 0; i < ha.cardsnumber(); i++ )
    if(   ha.card(i).value() == Card::TEN
       && !ha.card(i).istrump()
       && t.isvalid( ha.card( i ),hi.hand() )
       && (   hi.teamofplayer(t.winnerplayer()) == hi.team() 
	   || solo_check 
	   || allmyteam 
	  ) 
      )
    {
      DEBUG_RETURNING( ha.card(i),
		      INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_pfund_card()");
    }

  bool to_fat_solo =    GAMETYPE::is_solo( hi.game().type() ) 
    && t.points() > hi.value( Aiconfig::LIMITQUEEN )
    && t.player_of_card( 3 ) == hi.game().soloplayer(); 
  // or a Ace of color  
  for( i = 0; i < ha.cardsnumber(); i++ )
  {
    int cardsInGame = 0;
    // cards still in game of this color
    cardsInGame = notrump_per_color - numberofcards[ ha.card(i).color() ];

    if(  Card( ha.card( i ).color(), Card::TEN ) == Card::DOLLE 
       && hi.game().rule()(Rule::DOLLEN) 
      )
      cardsInGame-=2;
    for( unsigned n = 0; n < t.actcardno(); n++ )
      if(   t.card( n ).color() == ha.card(i).color() 
	 && !t.card( n ).istrump() )
	cardsInGame --;

    if(   ha.card(i).value()==Card::ACE 
       && cardsInGame >= 2 
       && !ha.card(i).istrump() 
       && t.isvalid( ha.card(i),hi.hand() )
       && !to_fat_solo
       && (   (   hi.game().type()!=GAMETYPE::SOLO_JACK // with not much trumps
	       && hi.game().type()!=GAMETYPE::SOLO_QUEEN // it is good to
	       && hi.game().type()!=GAMETYPE::SOLO_KING  // keep a second ace
	      )
	   || hi.color_runs(t.startcard().color()) > 0 
	  )
      )
    {
      DEBUG_RETURNING( ha.card(i), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_pfund_card()" );
    }
  }

  // or a ten of trump
  for( i = 0; i < ha.cardsnumber(); i++ )
    if(   ha.card(i).value() == Card::TEN 
       && ha.card(i).istrump() 
       && !ha.card(i).isdolle()
      )
    {
      DEBUG_RETURNING( ha.card(i),INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_pfund_card()" );
    }

  // or a King of color
  for ( i = 0; i < ha.cardsnumber(); i++ )
    if(   ha.card(i).value() == Card::KING 
       && !ha.card(i).istrump()
      )
    {
      DEBUG_RETURNING( ha.card(i), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_pfund_card()");
    }

  // or a king of trump
  for( i = 0; i < ha.cardsnumber(); i++ )
    if(    ha.card(i).value() == Card::KING
       && !ha.card(i).ishyperswine()
       && ha.card(i).istrump()
       && hi.game().type() != GAMETYPE::SOLO_KING 
       && hi.game().type() != GAMETYPE::SOLO_KING_QUEEN
       && hi.game().type() != GAMETYPE::SOLO_KING_JACK
       && hi.game().type() != GAMETYPE::SOLO_KOEHLER
      )
    {
      DEBUG_RETURNING( ha.card(i), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_pfund_card()" );
    }

  // or Queen if no trump
  for ( i = 0; i < ha.cardsnumber(); i++ )
    if(    ha.card(i).value()==Card::QUEEN 
       && !ha.card(i).istrump()
       && hi.game().type() != GAMETYPE::SOLO_QUEEN 
       && hi.game().type() != GAMETYPE::SOLO_KING_QUEEN
       && hi.game().type() != GAMETYPE::SOLO_QUEEN_JACK
       && hi.game().type() != GAMETYPE::SOLO_KOEHLER
      )
    {
      DEBUG_RETURNING( ha.card(i), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_pfund_card()" );
    }

  // or Jack if no trump
  for( i = 0; i < ha.cardsnumber(); i++ )
    if(   ha.card(i).value() == Card::JACK 
       && !ha.card(i).istrump()
       && hi.game().type() != GAMETYPE::SOLO_JACK 
       && hi.game().type() != GAMETYPE::SOLO_QUEEN_JACK
       && hi.game().type() != GAMETYPE::SOLO_KING_JACK
       && hi.game().type() != GAMETYPE::SOLO_KOEHLER
      )
    {
      DEBUG_RETURNING( ha.card(i), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_pfund_card()");
    }

  // or a nine of color
  for( i = 0; i < ha.cardsnumber(); i++ )
    if(   ha.card(i).value() == Card::NINE 
       && !ha.card(i).istrump()
      ) 
    {
      DEBUG_RETURNING( ha.card(i), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_pfund_card()" );
    }

  // or a nine of trump
  for ( i = 0; i < ha.cardsnumber(); i++ )
    if(   ha.card(i).value() == Card::NINE 
       && ha.card(i).istrump() 
       && !ha.card(i).ishyperswine() 
      )
    {
      DEBUG_RETURNING( ha.card(i), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_pfund_card()" );
    }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::choose_pfund_card()" );
}
/**
 ** checks if a solo player sits behind the player
 **/ 
bool 
soloPlayerBehind( Trick const& t, HeuristicInterface const& hi )
{
  bool soloPlayer = false;
  if( GAMETYPE::is_solo( hi.game().type() ) )
    for( Player const* player = &( t.game().player_following( t.actplayer() ) );
	player != &t.startplayer();
	player = &(t.game().player_following(*player))
       )
      if( *player ==  hi.game().soloplayer () )   
	soloPlayer = true;

  return soloPlayer; 
}
/**********************************************************************
 *
 **    Card Heuristics::choose_for_color_trick(Trick const& t, HeursticInterface const& hi)
 *
 **    Parameters:  actual palyed trick, HeuristicInterface who plays next card
 *
 **    Result: result see description, if no good card found
 **            result is a card with Card::NOCARDVALUE and Card::NOCARDCOLOR
 *
 **    Version: Beta
 *
 **    Description:
 **      play a good card for the last card in one Trick with color, which
 **      hi doesn't have anymore,
 **      if this trick is won by own team
 **      play a pfund
 **      if there are only color cards in this trick
 **      choose best_winning_card or if there is no
 **      lowest_color_card
 **      if there is already a trump try jab this trump with lowest possible
 **      trump, if there are more then a limit of points already in this trick
 *
 **********************************************************************/

Card 
Heuristics::choose_for_color_trick( Trick const& t,
				   HeuristicInterface const& hi )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::choose_for_color_trick() (long)" );

  HandCard c( hi.hand() );

  if( t.isempty() ) return c;

  HandCards ha;

  bool allmyteam = checkAllMyTeam( t, hi ) && hi.jabbedbyownteam( t.startcard().color() ) && !t.islastcard();
  bool soloPlayer = soloPlayerBehind( t, hi );

  allmyteam =   allmyteam 
    && (   hi.teamofplayer(t.winnerplayer())==hi.team() 		        		
	|| t.winnercard().less( hi.trump_card_limit() ) 
       );  	           


  // lets see if this is the last card for this trick
  // or the first run of this  color
  // and first card is no trump
  if (   !soloPlayer 
      && (   t.islastcard() 
	  || allmyteam 
	  || (   !t.isstartcard() 
	      && hi.color_runs(t.startcard().color()) == 0
	     )
	 )
      && !t.startcard().istrump()
     )
  {
    ha = hi.hand().validcards( t );

    // first check if this trick goes to my team
    bool pred = ( t.points() >= hi.value(Aiconfig::LIMITDOLLE)
		 ? hi.teamofplayer(t.winnerplayer()) == hi.team()
		 : maybe_to_team(hi.teamofplayer(t.winnerplayer())) == hi.team() );

    if( hi.game().type()==GAMETYPE::MARRIAGE ) 
    {
      pred = hi.teamofplayer(t.winnerplayer()) == maybe_to_team( hi.team() );
    } 

    pred = (   pred  
	    && (    t.islastcard() 
		|| (  (   t.winnercard().value() == Card::ACE
		       || t.winnercard().istrump()
		      )
		    && (   hi.color_runs(t.startcard().color()) == 0  
			|| !t.winnercard().less( hi.trump_card_limit() )
		       )
		   )

	       ));

    pred = pred || allmyteam;

    if( hi.game().type() == GAMETYPE::POVERTY )
    {
      pred &= (hi.no() == hi.game().povertyPartner().no()); 
    }
    if (   pred 
	|| (   GAMETYPE::is_solo( hi.game().type() )  
	    && t.winnerplayer() != hi.game().soloplayer() 
	    &&  hi.color_runs(t.startcard().color()) == 0 
	    &&  t.winnercard().less( hi.trump_card_limit() ) 
	    &&  !t.islastcard() 
	   ) 
       )
    { 
      DEBUG_RETURNING( choose_pfund_card( t, hi ),
		      INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_for_color_trick() (long)");   
    } // if own Team gets this trick


    // if there are only color cards in this trick
    bool trumpintrick = trumpInTrick( t );


    if( !trumpintrick ) {

      c = best_winning_card( t, hi );

      if( !ha.existstcolor(t.startcard().color() ) ) {
	if( c!=Card() )
	{ 
	  if( t.points() > hi.value( Aiconfig::LIMITDOLLE ) )
	    DEBUG_RETURNING(c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)" );	    
	  if(   t.points() > hi.value( Aiconfig::LIMITQUEEN )
	     && !Card( Card::CLUB, Card::QUEEN ).less( c ) 
	    )
	    DEBUG_RETURNING( c,
			    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)");

	  if( !Card( Card::CLUB, Card::JACK ).less( c ) )
	    DEBUG_RETURNING( c,
			    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)");

	}
	DEBUG_RETURNING( Card(), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			"Heuristics::choose_for_color_trick() (long)" );

      } else if( c != Card() ) {

	DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			"Heuristics::choose_for_color_trick() (long)" );
      } else {
	GameType gt = hi.game().type();

	if( gt != GAMETYPE::SOLO_MEATLESS )
	{
	  if(   hi.color_runs(t.startcard().color()) == 0 
	     && t.winnercard().value() != Card::ACE  
	     && ha.numberof( t.startcard().color(), Card::ACE )  > 0 )
	    DEBUG_RETURNING( Card( t.startcard().color(), Card::ACE ),
			    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)" );

	  if( hi.color_runs(t.startcard().color()) > 0)
	    DEBUG_RETURNING( lowest_color_card( t, hi.hand() ), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)");
	  else
	    DEBUG_RETURNING( lowest_best_trump_card(t, hi.hand(), hi ), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)" );
	} else 
	{
	  DEBUG_RETURNING( lowest_color_card_meatless( t, hi.hand() ), INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			  "Heuristics::choose_for_color_trick() (long)" );
	}
      }
      DEBUG_RETURNING(Card(),
		      INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_for_color_trick() (long)" );
    }// there was no other trumps in trick
    else // there is a trump in this trick
    {
      // if there is already a trump try jab this trump with lowest possible trump,
      // and my partner doesn't already win this trick
      // if there are more then 10 points already in this trick
      if( maybe_to_team( hi.teamofplayer( t.winnerplayer() ) ) != hi.team() )
      {

	c = best_winning_card( t, hi );
	if( c != Card() ) { 
	  if( t.points() > hi.value( Aiconfig::LIMITDOLLE ) )
	    DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)" );
	  if(   t.points() > hi.value( Aiconfig::LIMITQUEEN )
	     && !Card( Card::CLUB, Card::QUEEN ).less( c )
	    )
	    DEBUG_RETURNING( c,
			    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)");
	  if (t.points() < hi.value(Aiconfig::LIMIT_THROW_FEHL)) {
	    Card const card = lowest_color_card(t, hi.hand());
	    if (   (card.value() == Card::NINE)
		|| (card.value() == Card::KING))
	      if (better_points_optimize(c, HandCard(hi.hand(), card), hi))
		return card;
	  } // if (t.points() < 5)
	  if( !Card( Card::CLUB, Card::JACK ).less( c ) ) {
	    DEBUG_RETURNING( c,
			    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)");
	  }

	} // if (c != Card())
      }
      DEBUG_RETURNING(Card(),
		      INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_for_color_trick() (long)" );
    }

  } // check if trick is valid for this heuristic


  DEBUG_RETURNING( c,
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::choose_for_color_trick() (long)");
} // Card Heuristics::choose_for_color_trick( Trick t, HeuristicInterface hi )


/**
 ** Play a color again when not jabbed or the own team has jabbed it
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Borg Enders, Diether Knof
 **
 ** @version	0.7.2
 **/
Card
Heuristics::retry_color(Trick const& trick, HeuristicInterface const& hi)
{
  if (!trick.isstartcard())
    return Card();

  for (vector<Card::Color>::const_iterator
       c = hi.game().rule().valid_card_colors().begin();
       c != hi.game().rule().valid_card_colors().end();
       ++c) {
    if (   (hi.color_runs(*c) == 0)
	|| (hi.hand().numberof(*c) == 0))
      continue;

    if (   hi.colorjabbed(*c)
	&& !hi.jabbedbyownteam(*c))
      continue;

    if (   (hi.hand().numberof(Card(*c, Card::TEN)) > 0)
	&& hi.jabbedbyownteam(*c)
	&& (hi.teaminfo(trick.lastplayer()) == hi.team()) )
      return Card(*c, Card::TEN);
    if (hi.hand().numberof(Card(*c, Card::NINE)) > 0)
      return Card(*c, Card::NINE);
    if (hi.hand().numberof(Card(*c, Card::KING)) > 0)
      return Card(*c, Card::KING);

  } // for (c \in valid card colors)

  return Card();
} // Card Heuristics::retry_color(Trick trick, HeuristicInterface hi)

/**
 ** If the poverty partner is the last player play many points
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Borg Enders, Diether Knof
 **
 ** @version	0.7.2
 **/
Card
Heuristics::poverty_player_play_pfund(Trick const& trick,
				      HeuristicInterface const& hi)
{
  if (trick.isstartcard())
    return Card();

  if (hi.teaminfo(trick.lastplayer()) != hi.team() )
    return Card();

  Card c = Card();
  HandCards const ha = hi.hand().validcards( trick );

  // find any card that's allowed for this trick
  for( unsigned i = 0; i < ha.cardsnumber(); i++ )
    if( !ha.card(i).istrump() ) {
      c = ha.card(i);
      break;
    }

  if( c == Card() ) 
    return Card();

  // find a better card
  for( unsigned i = 0; i < ha.cardsnumber(); i++ ) {
    if(   !ha.card(i).istrump()
       && (ha.card(i).value() != Card::ACE)
       && (ha.card(i).value() > c.value())
       && (   (ha.numberof( ha.card(i).color() ) >  ha.numberof(c.color())) 
	   || (ha.numberof( ha.card(i).color(), Card::ACE ) == 0)
	  )
      )
      c = ha.card(i);
  } // for (i)

  return c;
} // Card Heuristics::poverty_player_play_pfund(Trick trick, HeuristicInterface hi)


/**
 **
 ** When a player cannot get a color trick take the card with the smallest value
 ** prerequisites:
 **   1. it must not be the first card in the trick
 **   2. the trick must be a color trick;
 **   3. one must have to serve the color
 **   4. the winnercard must be an ace or a trump or one does not have the ace;
 **   5. the winnerplayer must not be of the own team;
 **   6. the remaining players must not be of the own team.
 ** The main case is that one is the last to play and has only got to
 ** take ones lowest card.
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **
 **/
Card
Heuristics::serve_color_trick(Trick const& trick, HeuristicInterface const& hi)
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::serve_color_trick(trick, hi)");

  // it must not be the first card in the trick
  if( trick.isstartcard() )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_color_trick(trick, hi) = false");

  Card::Color const color = trick.startcard().color();

  // the trick must be a color trick
  if( trick.startcard().istrump() )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_color_trick(trick, hi) = false");

  // one has to serve the color
  if( hi.hand().numberof( color ) == 0 )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_color_trick(trick, hi) = false");

  HandCards const color_cards = hi.hand().validcards( trick );

  // the winnercard must be an ace or a trump or one does not have the ace
  if(   trick.winnercard().value() != Card::ACE
     && !trick.winnercard().istrump()
     && color_cards.numberof( Card( color, Card::ACE ) ) > 0 
    ) 
  {
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_color_trick(trick, hi) = false");
  } // if (trick.winnercard.value() != Card::ACE) && (...)

  // the winnerplayer must not be of the own team
  Player const& winnerplayer = trick.winnerplayer();
  if( hi.teaminfo( winnerplayer ) == hi.team() )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_color_trick(trick, hi) = false");

  // it must not be an undetermined marriage
  if(    hi.game().type() == GAMETYPE::MARRIAGE
     && hi.game().marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET
    )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_color_trick(trick, hi) = false");

  // the remaining players must not be of the own team.
  for( Player const* player = &(trick.game().player_following(trick.actplayer()));
      player != &trick.startplayer();
      player = &(trick.game().player_following(*player)))
    if( hi.teaminfo(*player) == hi.team() )
      DEBUG_RETURNING( Card(),
		      INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_for_color_trick(trick, hi) = false");

  // select the card with the lowest value
  HandCard lowest_card = color_cards.card( 0 );
  for (unsigned c = 1; c < color_cards.cardsnumber(); ++c )
    if( color_cards.card(c).value() < lowest_card.value() )
      lowest_card = color_cards.card( c );

  DEBUG_RETURNING( lowest_card,
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::serve_color_trick(trick, hi) = "
		  << lowest_card);
} // Card Heuristics::serve_color_trick(Trick const& trick, HeuristicInterface const& hi)


/**
 ** If a player cannot get a trump trick take the card with the smallest value
 ** prerequisites:
 **   1. it must not be the first card in the trick
 **   2. the trick must be a trump trick;
 **   3. one has to serve trump
 **   3. one cannot get the trick
 **   4. if the value of the cards are not the same:
 **      a) the winnerplayer must not be of the own team;
 **      b) the remaining players must not be of the own team
 **   5. There is a smallest card both in value and in jab order
 ** The main case is p.e. in a jack solo to play the smallest jack when the
 ** club jack is played
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Diether Knof
 **
 ** @version	0.6.8
 **
 **/
Card
Heuristics::serve_trump_trick( Trick const& trick, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::serve_trump_trick(trick, hi)");

  // it must not be the first card in the trick
  if( trick.isstartcard() )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_trump_trick(trick, hi) = false");

  // the trick must be a trump trick
  if( !trick.startcard().istrump() )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_trump(trick, hi) = false");

  // one has to serve trump
  if( !hi.hand().hastrump() )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_color_trick(trick, hi) = false");


  HandCards const trump_cards = hi.hand().validcards( trick );
  HandCard highest_card = trump_cards[ 0 ];
  HandCard lowest_card = trump_cards[ 0 ];

  Player const& player = hi.game().player( hi.no() );
  bool const swines = (  (   hi.game().swines_owner()
			  && hi.game().swines_owner() == &player
			 )
		       || hi.game().swines_announcement_valid( player )
		      );
  bool const hyperswines = (   (    hi.game().hyperswines_owner()
				&& hi.game().hyperswines_owner() == &player )
			    || hi.game().hyperswines_announcement_valid( player )
			   );

  Card const trump_ace( hi.game().trumpcolor(), Card::ACE );
  Card const trump_hyperswine( hi.game().trumpcolor(),
			      ( hi.game().rule()(Rule::WITH_NINES)
			       ? Card::NINE
			       : Card::KING ) );
  if( hyperswines )
    highest_card = HandCard( hi.hand(), trump_hyperswine );
  else 
    if( swines )
      highest_card = HandCard( hi.hand(), trump_ace );


  unsigned const points = lowest_card.points();
  bool same_points = true;
  unsigned lowest_points = lowest_card.points();
  for( HandCards::const_iterator c = trump_cards.begin() + 1;
      c != trump_cards.end();
      ++c ) 
  {
    if( c->points() != points )
      same_points = false;
    if( c->points() < lowest_points )
      lowest_points = c->points();
    if( highest_card.less(*c) )
      highest_card = *c;
    if(   c->less( lowest_card )
       && !(   swines
	    && *c == trump_ace 
	   )
       && !(   hyperswines
	    && *c == trump_hyperswine
	   )
      )
      lowest_card = *c;
  } // for (c \in trump_cards)

  // one cannot get the trick
  if(    trick.winnercard().less( highest_card )
     || swines
     || hyperswines
    )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_trump_trick(trick, hi) = false");


  if( !same_points ) 
  {
    // the winnerplayer must not be of the own team
    Player const& winnerplayer = trick.winnerplayer();
    if( hi.teaminfo( winnerplayer ) == hi.team() )
      DEBUG_RETURNING( Card(),
		      INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::serve_trump_trick(trick, hi) = false");

    // whether the highest possible card was played
    bool highest_card_played = false;
    switch( hi.game().type() ) 
    {
    case GAMETYPE::NORMAL:
    case GAMETYPE::POVERTY:
    case GAMETYPE::GENSCHER:
    case GAMETYPE::MARRIAGE:
    case GAMETYPE::MARRIAGE_SOLO:
    case GAMETYPE::MARRIAGE_SILENT:
    case GAMETYPE::SOLO_CLUB:
    case GAMETYPE::SOLO_HEART:
    case GAMETYPE::SOLO_SPADE:
    case GAMETYPE::SOLO_DIAMOND:
      // ToDo: test which possible highest cards were already played
      if(   hi.game().rule()( Rule::HYPERSWINES )
	 && (   hi.game().hyperswines_owner()
	     || !hi.game().rule()(Rule::HYPERSWINES_ANNOUNCEMENT_BEGIN)
	    )
	) 
      {
	if( trick.winnercard() == trump_hyperswine )
	  highest_card_played = true;
      } else 
	if(   hi.game().rule()(Rule::SWINES)
	   && (   hi.game().swines_owner()
	       || !hi.game().rule()(Rule::SWINES_ANNOUNCEMENT_BEGIN)
	      )
	  ) 
	{
	  if( trick.winnercard() == trump_ace )
	    highest_card_played = true;
	} else 
	  if( hi.game().rule()(Rule::DOLLEN) ) 
	  {
	    if(    !hi.game().rule()(Rule::DOLLEN_SECOND_OVER_FIRST)
	       && trick.winnercard() == Card::DOLLE
	      )
	      highest_card_played = true;
	  } else 
	    if( trick.winnercard() == Card::CLUB_QUEEN ) 
	    {
	      highest_card_played = true;
	    }
      break;
    case GAMETYPE::FIVE_NINES:
    case GAMETYPE::SIX_KINGS:
    case GAMETYPE::FOX_HIGHEST_TRUMP:
      // no game
      break;
    case GAMETYPE::SOLO_JACK:
      // ToDo: when both club jack have already been played
      if( trick.winnercard() == Card(Card::CLUB, Card::JACK) )
	highest_card_played = true;
      break;
    case GAMETYPE::SOLO_QUEEN:
    case GAMETYPE::SOLO_QUEEN_JACK:
      if( trick.winnercard() == Card(Card::CLUB, Card::QUEEN) )
	highest_card_played = true;
      break;
    case GAMETYPE::SOLO_KING:
    case GAMETYPE::SOLO_KING_JACK:
    case GAMETYPE::SOLO_KING_QUEEN:
    case GAMETYPE::SOLO_KOEHLER:
      if( trick.winnercard() == Card(Card::CLUB, Card::KING) )
	highest_card_played = true;
      break;
    case GAMETYPE::SOLO_MEATLESS:
      // no trump
      break;
    } // switch (hi.game().type())

    // the remaining players must not be of the own team
    if( !highest_card_played ) 
    {
      for( Player const* player = &( trick.game().player_following( trick.actplayer() ) );
	  player != &trick.startplayer();
	  player = &( trick.game().player_following( *player ) ) 
	 )
	if( hi.teaminfo(*player) == hi.team() )
	  DEBUG_RETURNING( Card(),
			  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			  "Heuristics::choose_for_trump_trick(trick, hi) = false");
    } // if (!highest_card_played)
  } // if (!same_points)


  // There is a smallest card both in value and in jab order
  HandCard card;
  switch( hi.game().type() ) 
  {
  case GAMETYPE::NORMAL:
    // ToDo Genscher
  case GAMETYPE::POVERTY:
  case GAMETYPE::GENSCHER:
  case GAMETYPE::MARRIAGE:
  case GAMETYPE::MARRIAGE_SOLO:
  case GAMETYPE::MARRIAGE_SILENT:
  case GAMETYPE::SOLO_CLUB:
  case GAMETYPE::SOLO_HEART:
  case GAMETYPE::SOLO_SPADE:
  case GAMETYPE::SOLO_DIAMOND:
    if(   hi.game().rule()(Rule::EXTRAPOINT_CHARLIE)
       && hi.hand().numberof(Card::CHARLIE) > 0
      )
      break;
    if( lowest_card.points() == lowest_points )
      card = lowest_card;
    break;
  case GAMETYPE::FIVE_NINES:
  case GAMETYPE::SIX_KINGS:
  case GAMETYPE::FOX_HIGHEST_TRUMP:
    // no game
    break;
  case GAMETYPE::SOLO_JACK:
  case GAMETYPE::SOLO_QUEEN:
  case GAMETYPE::SOLO_KING:
  case GAMETYPE::SOLO_QUEEN_JACK:
  case GAMETYPE::SOLO_KING_JACK:
  case GAMETYPE::SOLO_KING_QUEEN:
  case GAMETYPE::SOLO_KOEHLER:
    // simple
    card = lowest_card;
    break;
  case GAMETYPE::SOLO_MEATLESS:
    // no trump
    break;
  } // switch (hi.game().type())

  DEBUG_RETURNING( card,
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::serve_trump_trick(trick, hi) = "
		  << lowest_card);
} // Card Heuristics::serve_trump_trick(Trick const& trick, HeuristicInterface const& hi)


/**********************************************************************
 *
 **    Card Heuristics::choose_pfund(Trick const& t, HeursticInterface const& hi)
 *
 **    Parameters:  actual played trick, HeuristicInterface who plays next card
 *
 **    Result: result see description, otherwise
 **            result is a card with Card::NOCARDVALUE and Card::NOCARDCOLOR
 *
 **    Version: Beta
 *
 **    Description:
 **      for the last card in a trick, which wins your own team 
 **      or where your teammate has already a played a high enough trump
 **      play a pfund
 *
 **********************************************************************/

Card 
Heuristics::choose_pfund( Trick const& t, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::choose_pfund()");

  // I'am the start Player
  if( t.isstartcard() )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::choose_pfund()");

  bool soloPlayer =  soloPlayerBehind( t, hi );


  // if winner is own team
  if(  (   maybe_to_team(hi.teamofplayer(t.winnerplayer())) == hi.team() 
	&& !soloPlayer
	&& (    t.islastcard() 
	    || !t.winnercard().less( hi.trump_card_limit() )
	   )
       )   
     ||(   GAMETYPE::is_solo( hi.game().type() )
	&& t.winnerplayer() == hi.game().soloplayer()
	&& t.winnercard().less( hi.trump_card_limit() )
	&& !t.islastcard()
	&& choose_pfund_card(t,hi).points() > 0 
	&& t.startcard().istrump()
       )
    )
  {
    DEBUG_RETURNING( choose_pfund_card( t, hi ),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::choose_pfund()");
  }

  DEBUG_RETURNING( Card(),
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::choose_pfund()");
}

/**********************************************************************
 *
 **    Card Heuristics::jab_for_ace(Trick const& t,HeursticInterface const& hi)
 *
 **    Parameters:  actual played trick, HeuristicInterface who plays next card
 *
 **    Result: result lowest allowed Jack or Queen if they are trump
 **            and there is a ace to play, which is not already jabbed
 **            result is a card with Card::NOCARDVALUE and Card::NOCARDCOLOR
 *
 **    Version: 0.6.9
 *
 **    Description:
 *
 **********************************************************************/
Card 
Heuristics::jab_for_ace( Trick const& t, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::jab_for_ace()");

  Card c = Card();
  Card ace = choose_ace( Trick(), hi );

  // for a solo don't jab for ace, if there are players of my team behind me
  // and myself doesn't have that many trumps
  if (     GAMETYPE::is_solo( hi.game().type() )
      && t.winnerplayer() == hi.game().soloplayer()
      && t.winnercard().less( hi.trump_card_limit() )
      && !t.islastcard()
      && hi.hand().numberoftrumps() < 4  
     ) 
    DEBUG_RETURNING(c,INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::jab_for_ace()");



  if(    ace != Card()
     && hi.hand().numberof( ace ) < 2
     && hi.color_runs(ace.color()) == 0
     && (    t.winnerteam() != hi.team() 
	 || (   t.winnerteam() == hi.team() 
	     && t.winnercard().less( hi.trump_card_limit() )
	    )
	) 
    )
  {    		
    c = best_winning_card( t, hi, 
			  hi.value( Aiconfig::LIMITDOLLE ) - ( 5 - t.actcardno() ) );
  }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::jab_for_ace()" );

}


/**********************************************************************
 *
 **    Card Heuristics::play_low_high(Trick const& t,HeursticInterface const& hi)
 *
 **    Parameters:  actual played trick, HeuristicInterface who plays next card
 *
 **    Result: plays high and low trumps changing
 *
 **    Version: Beta
 *
 **    Description: 
 *
 **********************************************************************/  
Card 
Heuristics::play_low_high(Trick const& t,HeuristicInterface const& hi)
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::play_low_high");


  if(   t.isstartcard() 
     && hi.hand().numberoftrumps() > hi.game().tricks_remaining_no() / 2 
    ) 
  {
    if(  hi.next_low() )
    {
      if( hi.hand().numberof( Card::QUEEN ) < 2 * hi.hand().numberof( Card::JACK ) ) 
      {
	Card c = lowest_jack( hi.hand(), hi );

	if(   c != Card()
	   && !t.isvalid(c,hi.hand()))
	  c = Card();

	DEBUG_RETURNING( c,
			INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			"Heuristics::play_low_high()"
			" = jack");
      }
    } else 
    { // if !(hi.next_low())
      Card c = lowest_queen( hi.hand(), hi );

      if(   c!=Card()
	 && !t.isvalid( c, hi.hand() ) )
	c = Card();

      DEBUG_RETURNING( c,
		      INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::play_low_high()"
		      " = queen");
    } // if !(hi.next_low())
  } // if (t.isstartcard())

  DEBUG_RETURNING( Card(),
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::play_low_high()"
		  " = card()");
}


/**********************************************************************
 *
 **    Card Heuristics::SecondBestTrump(HeuristicInterface const& hi)
 *
 **    Parameters:  actual played trick, HeuristicInterface who plays next card
 *
 **    Result: plays the scond highest trump
 *
 **    Version: Beta
 *
 **    Description: 
 **      usefull for color soli
 *
 **********************************************************************/  
Card
Heuristics::SecondBestTrump(HeuristicInterface const& hi)
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::SecondBestTrump" );
  Hand h = hi.hand();

  Card c = Card();
  Card bestcard;

  if( hi.remaining_trumps() == 0 )
    return c;

  // first find highest trump on hand
  unsigned i;
  unsigned best = 0;
  bestcard = h.card( best );
  for( i = 1; i < h.cardsnumber();i++ )
  {
    if(   h.card(i).istrump()
       && bestcard.less( h.card( i ) )
      ) 
    {
      bestcard = h.card( i );
      best = i;
    }
  }

  // find second best card
  for (i = 0; i < h.cardsnumber(); i++)
  {
    if(   h.card(i).istrump()
       && (  h.card(i).less(bestcard)
	   || h.card(i) == bestcard 
	  )
       && c.less( h.card(i) )
       && i != best
      )
    {
      c = h.card( i );
    }
  }

  DEBUG_ASSERT( c );
  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::SecondBestTrump" );

} // Card Heuristics::SecondBestTrump(HeuristicInterface const& hi)


/**********************************************************************
 *
 **    Card Heuristics::play_for_team(Trick const& t,HeursticInterface const& hi)
 *
 **    Parameters:  actual played trick, HeuristicInterface who plays next card
 *
 **    Result: puts a nice card to a trick of a team mate
 *
 **    Version: Beta
 *
 **    Description: 
 **      result a ten (no dolle), or a Ace (no swine)
 **      or a king, or a nine in this order
 *
 **********************************************************************/  
Card 
Heuristics::play_for_team( Trick const& t, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::play_for_team");

  // I'am the start Player
  if( t.isstartcard() )
    DEBUG_RETURNING( Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::play_for_team()");

  bool soloPlayer =  soloPlayerBehind( t, hi );


  Card c = Card();
  if(    maybe_to_team(hi.teamofplayer(t.winnerplayer())) == hi.team()
     && !soloPlayer
     && !t.winnercard().less(hi.value(Aiconfig::LIMITTHROWING))
    )
  { 
    unsigned i;

    for( i = 0; i < hi.hand().cardsnumber(); i++ )
    {
      Trick tr = t;
      if( tr.isvalid( hi.hand().card( i ), hi.hand() ) )
      {
	tr += hi.hand().card( i );
	if(   tr.winnerplayer().no() != hi.no()
	   && hi.hand().card(i).value() == Card::TEN
	   && !tr.winnercard().less(hi.value(Aiconfig::LIMITHIGH))
	   && !hi.hand().card(i).isdolle()
	  )
	  c=hi.hand().card(i);     
      }           
    }

    if( c != Card() ) 
      DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::play_for_team" );

    for( i = 0; i < hi.hand().cardsnumber(); i++ )
    {
      Trick tr = t;
      if( tr.isvalid( hi.hand().card(i),hi.hand() ) )
      {
	tr += hi.hand().card( i );

	// last player after me is player with poverty
	bool lastPoverty = soloPlayer 
	  && tr.actcardno() == 3 
	  && hi.game().soloplayer() == tr.player_of_card( 3 );

	if(   tr.winnerplayer().no() != hi.no()
	   && hi.hand().card(i).value() == Card::ACE
	   && (   !tr.winnercard().less( hi.value( Aiconfig::LIMITHIGH ) )
	       || t.islastcard() 
	       || lastPoverty 
	      )
	   && !hi.hand().card( i ).isswine() 
	   && (   !hi.hand().card( i ).isfox() 
	       || t.islastcard() 
	       || lastPoverty 
	      )
	  )
	  c = hi.hand().card( i );     
      }           
    }

    if( c != Card() ) 
      DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::play_for_team" );

    for( i = 0; i < hi.hand().cardsnumber(); i++ )
    {
      Trick tr = t;
      if( tr.isvalid( hi.hand().card(i), hi.hand() ) )
      {
	tr += hi.hand().card( i );
	if(   tr.winnerplayer().no() != hi.no()
	   && hi.hand().card(i).value()==Card::KING 
	   && !hi.hand().card(i).ishyperswine()
	   && hi.game().type() != GAMETYPE::SOLO_KING 
	   && hi.game().type() != GAMETYPE::SOLO_KING_QUEEN
	   && hi.game().type() != GAMETYPE::SOLO_KING_JACK
	  )
	  c = hi.hand().card( i );     
      }           
    }

    if( c != Card() ) 
      DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::play_for_team" );

    for( i = 0; i < hi.hand().cardsnumber(); i++ )
    {
      Trick tr = t;
      if( tr.isvalid( hi.hand().card(i), hi.hand() ) )
      {
	tr += hi.hand().card( i );
	if(   tr.winnerplayer().no() != hi.no()
	   && hi.hand().card(i).value() == Card::NINE
	   && !hi.hand().card(i).ishyperswine()
	  )
	  c = hi.hand().card( i );     
      }           
    }

  }

  DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		  "Heuristics::play_for_team" );
}

/**********************************************************************
 *
 **    Card Heuristics::jab_fox(Trick const& t,HeursticInterface const& hi)
 *
 **    Parameters:  actual played trick, HeuristicInterface who plays next card
 *
 **    Result: jabs a fox if in this in the trick
 *
 **    Version: Beta
 *
 **    Description: 
 *
 **********************************************************************/  
Card 
Heuristics::jab_fox( Trick const& t, HeuristicInterface const& hi )
{
  DEBUG_CALLING(INFO_HEURISTICS&&INFO_OTHER_FUNCTION,"Heuristics::jab_fox");
  Card c=Card();

  bool fox_exists = false;
  // first lets check if there is a fox
  for( unsigned i = 0; i < t.actcardno(); i++ )
    if( t.card( i ).isfox() )
    {
      fox_exists = true;
      break;
    }

  if( fox_exists )
  {

    // if lastcard just take the best winning card
    if(   t.islastcard() 
       && hi.teamofplayer(t.winnerplayer()) != hi.team()
      )
    {
      DEBUG_RETURNING( best_winning_card(t, hi, 10 ),
		      INFO_HEURISTICS&&INFO_OTHER_FUNCTION,"Heuristics::jab_fox");
    } 
    // lastcard and this is our trick nothing to do.
    if(   t.islastcard() 
       && hi.teamofplayer(t.winnerplayer())==hi.team() )
    {
      return Card();
    }

    { // if I can jab the fox surely, take the card
      // take the best cards of all players behind me that are not in my team
      Trick const& trick = t;
      HandCard highest_card_behind( hi.hand() );
      for( Player const* player = &(trick.game().player_following(trick.actplayer()));
	  player != &trick.startplayer();
	  player = &(trick.game().player_following(*player))) 
      {
	if( hi.teamofplayer(*player) != hi.team() )
	{
	  HandCard const highest_card = player->hand().highest_card();
	  if(  !highest_card_behind
	     || highest_card_behind.less( highest_card )
	    )
	    highest_card_behind = highest_card;
	} // if (hi.teamofplayer(*player) != hi.team())
      } // for (player)
      if( highest_card_behind ) 
      {
	// the partner has the best possible card
	if(   hi.teamofplayer(trick.winnerplayer()) == hi.team() 
	   && !trick.winnercard().less( highest_card_behind )
	  )
	  DEBUG_RETURNING( Card(),
			  INFO_HEURISTICS&&INFO_VALUE,
			  "Heuristics::jab_fox = false");

	// look whether I have the best card
	Card lowest_better_card;
	HandCards const valid_cards = hi.hand().validcards( t );
	for( HandCards::const_iterator c = valid_cards.begin();
	    c != valid_cards.end();
	    ++c ) 
	{
	  if(   !c->less( highest_card_behind )
	     && (   !lowest_better_card
		 || lowest_better_card.less(*c)
		)
	    )
	    lowest_better_card = *c;
	} // for (c)
	if( lowest_better_card )
	  DEBUG_RETURNING( lowest_better_card,
			  INFO_HEURISTICS&&INFO_VALUE,
			  "Heuristics::jab_fox = false");
      } // if (highest_card_behind)
    } // if I can jab the fox surely, take the card

    // for all other we take our highest queen
    HandCard ca( hi.hand(), highest_queen( hi.hand(), hi) );
    if( !ca ) {
      DEBUG_RETURNING( ca,
		      INFO_HEURISTICS&&INFO_VALUE,
		      "Heuristics::jab_fox");
    }

    Trick tr = t;
    tr += ca;
    if(   t.isvalid( ca, hi.hand() ) 
       && tr.winnerplayer().no()==hi.no()
      ) 
    {
      DEBUG_RETURNING( ca,
		      INFO_HEURISTICS&&INFO_OTHER_FUNCTION,
		      "Heuristics::jab_fox");
    }
  }

  DEBUG_RETURNING( c, 
		  INFO_HEURISTICS&&INFO_OTHER_FUNCTION,"Heuristics::jab_fox");
}


/**
 ** -> result
 **
 ** @param	trick	trick
 ** @param	hi	heuristic interface
 **
 ** @result	card with ten points, if the heuristic matches
 **
 ** @author	Borg Enders
 ** @author	Diether Knof
 **
 ** @version	0.6.9
 **/
Card
Heuristics::try_for_doppelkopf( Trick const& trick, 
			       HeuristicInterface const& hi )
{
  // at max one player behind me
  if( trick.actcardno() < hi.game().playerno() - 2 )
    return Card();

  // each player has to give ten points
  if( trick.points() < 10 * trick.actcardno() )
    return Card();


  // ? last player opposite team ?

  Player const& player = hi.game().player( hi.no() );

  if (   !trick.islastcard()
      && player.announcement() == ANNOUNCEMENT::NOANNOUNCEMENT
      && !hi.game().announcement_valid( ANNOUNCEMENT::NO120, player )
     )
    return Card();

  // search a ten or an ace
  vector<Card> try_cards;
  try_cards.push_back( Card( hi.game().trumpcolor(), Card::TEN ) );
  try_cards.push_back( Card( hi.game().trumpcolor(), Card::ACE ) );
  try_cards.push_back( Card::DOLLE );

  for( vector<Card>::const_iterator c = try_cards.begin();
      c != try_cards.end();
      ++c )
    if(   hi.hand().numberof(*c) > 0 
       && trick.isvalid( *c, hi.hand() )
       && trick.winnercard().less( *c )
      )
      return *c;

  return Card();
} // Card Heuristics::try_for_doppelkopf(Trick const& trick,HeuristicInterface const& hi)


/**********************************************************************
 *
 **    Card choose_best_fehl(Trick const& t,HeursticInterface const& hi)
 *
 **    Parameters:  actual played trick, HeuristicInterface who plays next card
 *
 **    Result: single color nine or king to get fehl
 **            otherwise Card()
 *
 **    Version: Beta
 *
 **    Description: 
 *
 **********************************************************************/

Card 
choose_best_fehl( Trick const& t,
		 HeuristicInterface const& hi )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics choose_best_fehl()" );
  unsigned i,n;
  vector<int> numberofcards(party.rule()( Rule::NUMBER_OF_CARD_COLORS ) );
  unsigned bestcolor = 0;
  vector<int> co( ::party.rule()( Rule::NUMBER_OF_CARD_COLORS ) );
  Card c = Card();

  HandCards const h = hi.hand().validcards( t );

  for( i = 0; i < ::party.rule()( Rule::NUMBER_OF_CARD_COLORS ); i++ )
    co[ i ] = 0;

  for( i = 0; i<h.cardsnumber(); i++ ) 
  {
    if( !h.card( i ).istrump() ) 
    {
      ( co[ h.card( i ).color() ] ) += hi.hand().numberof( h.card(i) );
    }
  }

  int notrump_per_color = number_of_no_trumps_per_color();

  for ( i = 0; i < ::party.rule()( Rule::NUMBER_OF_CARD_COLORS ); i++)
    numberofcards[ i ]= 0;

  // calculate number of played no Trump cards per color
  for( i = 0;  i < 2 * ::party.rule()( Rule::NUMBER_OF_CARD_VALUES ); i += 2 )
    for( n = 0; n < ::party.rule()( Rule::NUMBER_OF_CARD_COLORS ); n++ )
      if( !Card( Card::InttoColor( n ),
		Card::InttoValue( i ) ).istrump( hi.game() ) )
	( numberofcards[ n ] ) += hi.playedcard( Card( Card::InttoColor( n ),
						      Card::InttoValue( i ) ) );


  for ( i = 0; i < h.cardsnumber(); i++ )
  {
    // cards still in game of this color
    unsigned const cn = notrump_per_color-numberofcards[ h.card( i ).color() ];
    if(   !h.card( i ).istrump() 
       && cn > bestcolor 
       && cn >= hi.game().playerno()
       && t.isvalid( h.card( i ), hi.hand() ) 
       && co[ h.card( i ).color() ]< 2 
       && (   h.card( i ).value() == Card::KING 
	   || h.card( i ).value() == Card::NINE )    
      ) 
    {
      c = h.card( i );
      bestcolor = cn;
    }
  }// for
  DEBUG_RETURNING( c, 
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics choose_best_fehl()");
}

/**********************************************************************
 *
 **    Card Heuristics::create_fehl(Trick const& t,HeursticInterface const& hi)
 *
 **    Parameters:  actual played trick, HeuristicInterface who plays next card
 *
 **    Result: result play single color nine or king to get fehl
 **            in second run of a color
 **            result is a card with Card::NOCARDVALUE and Card::NOCARDCOLOR
 *
 **    Version: Alpha
 *
 **    Description: 
 *
 **********************************************************************/

Card 
Heuristics::create_fehl( Trick const& t, HeuristicInterface const& hi )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::create_fehl()" );

  Card c = Card();

  // if this trick is still worth fehl creation
  if( hi.game().trick_current_no() < hi.value( Aiconfig::LASTFEHLCREATION ) )
  {
    // if this is the firstcard 
    if( hi.value( Aiconfig::FEHLCREATIONONFIRSTCARD ) ) 
      if (t.actcardno() == 0 )
	DEBUG_RETURNING( choose_best_fehl( t, hi ),
			INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::create_fehl()");  

    // this is not the first colorrun and there are not more then 10 points 
    if(   t.actcardno() > 0 
       && hi.color_runs( t.startcard().color() ) > 0 
       && t.points()<hi.value(Aiconfig::LIMIT_THROW_FEHL)
      )
    {  
      // the trick is jabbed high enough 
      if(   t.winnercard().istrump()
	 && !t.winnercard().less( hi.lowest_trump_card_limit() ) 
	)
	DEBUG_RETURNING( choose_best_fehl( t,hi ),
			INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			"Heuristics::create_fehl()");  

      // the winnercard is already a no trump ace 
      if(  !t.winnercard().istrump() 
	 && t.winnercard().value() >= Card::ACE )
	DEBUG_RETURNING( choose_best_fehl( t,hi ),
			INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::create_fehl()");          
    }
  }
  DEBUG_RETURNING( c,
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::create_fehl()");
}

/**********************************************************************
 *
 **    int calchandvalue(HeursticInterface const& hi)
 *
 **    Parameters:  HeuristicInterface who checks for an announcement
 *
 **    Result: value of cards for announcement
 *
 **    Version: Beta
 *
 **    Description: 
 *
 **********************************************************************/

int 
Heuristics::CalcHandValue( HeuristicInterface const& hi, const Game& g )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		"Heuristics::CalcHandValue" );
  int value = 0;
  unsigned i;

  unsigned v_queen = 0;
  unsigned v_jack = 0;
  unsigned v_highest = 0;
  unsigned v_king = 0;
  unsigned v_ace = 0;
  unsigned v_fehl = 0;

  if( g.type() == GAMETYPE::NORMAL )
  { v_queen = 2; v_jack = 1; v_highest = 3; v_king = 0; v_ace = 1; v_fehl = 2; }

  if( g.type() == GAMETYPE::POVERTY )
  { v_queen = 2; v_jack = 1; v_highest = 3; v_king = 0; v_ace = 0; v_fehl = 2;
    value = -3;
  }

  if( g.type() == GAMETYPE::MARRIAGE )
  { v_queen = 2; v_jack = 1; v_highest = 3; v_king = 0; v_ace = 1; v_fehl = 2; }

  if( g.type() == GAMETYPE::SOLO_JACK )
  { v_queen = 0; v_jack = 3; v_highest = 0; v_king = 0; v_ace = 2; v_fehl = 1;
    value = -5;
  }

  if (g.type()==GAMETYPE::SOLO_QUEEN)
  { v_queen = 3; v_jack = 0; v_highest = 0; v_king = 0; v_ace = 2; v_fehl = 1;
    value = -5;
  }  

  if( g.type() == GAMETYPE::SOLO_KING )
  { v_queen = 0; v_jack = 0; v_highest = 0; v_king = 3; v_ace = 2; v_fehl = 1;
    value = -5;
  }  

  if( g.type() == GAMETYPE::SOLO_QUEEN_JACK )
  { v_queen = 3; v_jack = 2; v_highest = 0; v_king = 0; v_ace = 1; v_fehl = 1;
    value = -6;
  }  

  if( g.type() == GAMETYPE::SOLO_KING_JACK )
  { v_queen = 0; v_jack = 2; v_highest = 0; v_king = 3; v_ace = 1; v_fehl = 1;
    value = -6; 
  }    

  if( g.type() == GAMETYPE::SOLO_KING_QUEEN )
  { v_queen = 2; v_jack = 0; v_highest = 0; v_king = 3; v_ace = 1; v_fehl = 1;
    value = -6;
  }  

  if( g.type() == GAMETYPE::SOLO_KOEHLER )
  { v_queen = 2; v_jack = 1; v_highest = 0; v_king = 3; v_ace = 0; v_fehl = 0;
    value = -5; 
  }  

  if(    g.type()==GAMETYPE::SOLO_CLUB 
     || g.type()==GAMETYPE::SOLO_HEART 
     || g.type()==GAMETYPE::SOLO_SPADE 
     || g.type()==GAMETYPE::SOLO_DIAMOND )
  { v_queen = 2; v_jack = 1; v_highest = 3; v_king = 0; v_ace = 1; v_fehl = 2;
    value = -7;
  }  

  if( g.type() == GAMETYPE::SOLO_MEATLESS )
  { v_queen = 0; v_jack = 0; v_highest = 0; v_king = 0; v_ace = 4; v_fehl = 1; // missing aces
    value = -2; 
  } 


  for( i = 0; i < hi.hand().cardsnumber(); i++ )
  {
    if(   hi.hand().card(i).isdolle()
       || hi.hand().card(i).isswine()
       || hi.hand().card(i).possible_swine()
       || hi.hand().card(i).ishyperswine()
       || hi.hand().card(i).possible_hyperswine()
      )
      value += v_highest;
    else 
      if( hi.hand().card(i).value()==Card::QUEEN )
	value += v_queen;
      else 
	if( hi.hand().card(i).value()==Card::JACK )
	  value += v_jack; 
	else 
	  if( hi.hand().card(i).value()==Card::KING )
	    value += v_king;
	  else 
	    if(    hi.hand().card(i).value()==Card::ACE
	       && !hi.hand().card(i).istrump() )
	      value += v_ace;
  }

  for( i = 0; i < ::party.rule()( Rule::NUMBER_OF_CARD_COLORS ); i++ )
  {
    if (   !Card( Card::InttoColor( i ), Card::ACE ).istrump( hi.game() ) 
	&& !hi.hand().existstcolor( Card::InttoColor( i ) )
       )    
      if( ::in_running_game() )    
      {
	if(     hi.game().trick_current().actcardno()>0
	   && hi.color_runs( Card::InttoColor(i) ) == 0
	   && hi.game().trick_current().startcard().tcolor()
	   != Card::InttoColor(i)) 
	  value += v_fehl;
      } else {
	value += v_fehl; 
      }
  }

  if( g.type() == GAMETYPE::SOLO_MEATLESS )
  {
    for( i = 0; i < ::party.rule()( Rule::NUMBER_OF_CARD_COLORS ); i++ )
    {
      if( hi.hand().existstcolor( Card::InttoColor( i ) ) )
	value -=  v_fehl
	  * ( 2 - hi.hand().numberof( Card::InttoColor( i ), Card::ACE ) );
    }
    if( hi.no() == hi.game().soloplayer().no() )
    {
      unsigned longColor = 0;
      for( i = 0; i < ::party.rule()( Rule::NUMBER_OF_CARD_COLORS ); i++ )
      {
	if(   hi.hand().numberof(Card::InttoColor(i),Card::ACE)  == 2 
	   && hi.hand().numberof(Card::InttoColor(i) ) > longColor )
	  longColor = hi.hand().numberof(Card::InttoColor(i) );
      }
      value += longColor;
    }
  } else  {
    Player const& player = hi.game().player( hi.no() );
    for( vector<Card::Color>::const_iterator 
	c = hi.game().rule().valid_card_colors().begin();
	c != hi.game().rule().valid_card_colors().end();
	++c ) 
    {
      Card::Color color = *c;

      if( *c == hi.game().trumpcolor() )
	continue;
      // including this trick the player does not have the color
      if (    !hi.hand().existstcolor( color )
	  && !(    ::in_running_game()
	       && ( hi.game().trick_current().actcardno()
		   > hi.game().trick_current().cardno_of_player( player ) )
	       && ( hi.game().trick_current().card_of_player( player ).tcolor()
		   == color )
	      )
	 )
	continue;

      if ( hi.hand().numberof( color, Card::ACE ) == 0 )
	value -= v_fehl;

      if(   hi.hand().numberof( color )
	 + hi.color_runs( color )
	 + (   ::in_running_game()
	    && hi.game().trick_current().actcardno() > 0
	    && ( hi.game().trick_current().startcard().color()
		== color
	       ) ? 1 : 0 )
	 > ( hi.game().different_cards_no( color ) - 1
	    - ( hi.game().rule()(Rule::WITH_NINES) ? 0 : 1 ) )
	)
	value -= v_fehl;

      if ( (   hi.hand().numberof( color )
	    + hi.color_runs( color )
	    + ( (   ::in_running_game()
		 && hi.game().trick_current().actcardno()>0
		 && ( hi.game().trick_current().startcard().color()
		     == color)
		) ? 1 : 0)
	    >= 3)
	  && ( hi.hand().numberof( Card( color, Card::ACE ) ) < 2 ) )
	value -= v_ace;

    } // (for color)
  } // if (...)

  if( g.type() == GAMETYPE::SOLO_KOEHLER )
    for( i = 0; i < ::party.rule()( Rule::NUMBER_OF_CARD_COLORS );i++ )
    {
      if( hi.hand().existstcolor(Card::InttoColor(i)) )
	value-=5;
    }


  if(   (   hi.game().swines_owner() != NULL 
	 && hi.game().swines_owner()->no() != hi.no() ) 
     || hi.game().swines_announcement_valid( hi.game().player( hi.no() ) )
    )
  {
    Player const& swines_player = ( hi.game().swines_owner()
				   ? *hi.game().swines_owner()
				   : hi.game().player( hi.no() ) );
    if(   hi.teamofplayer(swines_player) == hi.team() 
       || hi.game().swines_announcement_valid( swines_player )
      )
    {
      value += v_highest;
    } else
    {
      value -= 2* v_highest;
    }
  }


  if(  (   hi.game().hyperswines_owner() != NULL 
	&& hi.game().hyperswines_owner()->no() != hi.no()
       )
     || hi.game().hyperswines_announcement_valid( hi.game().player( hi.no() ) )
    )
  {
    Player const& hyperswines_player = ( hi.game().hyperswines_owner()
					? *hi.game().hyperswines_owner()
					: hi.game().player( hi.no() ) );
    if(   hi.teamofplayer(hyperswines_player) == hi.team() 
       || hi.game().hyperswines_announcement_valid( hyperswines_player ) 
      )
    {
      value += v_highest;
    } else
    {
      value -= 2* v_highest;
    }
  }

  DEBUG_RETURNING( value,
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::CalcHandValue");
}


/**********************************************************************
 *
 **    bool Heuristics::make_announcement(HeursticInterface const& hi)
 *
 **    Parameters:  HeuristicInterface who checks for an announcement
 *
 **    Result: true if hi should say re or contra
 **            false otherwise
 *
 **    Version: Beta
 *
 **    Description: 
 *
 **********************************************************************/
bool 
Heuristics::make_announcement( HeuristicInterface const& hi, const Game& g )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::make_announcement" );
  int value=0;

  value= CalcHandValue( hi, g );

  DEBUG_RETURNING( 
		  value >=   (int)hi.value(Aiconfig::ANNOUNCELIMIT) 
		  + (int)hi.value(Aiconfig::ANNOUNCECONFIG),
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::make_announcement" );
} 

///calculates all point of own team in Game g
int calcPointsOfOwnTeam( HeuristicInterface const& hi, Game const& g )
{
  int points_p = 0;

  for( unsigned int i = 0; i < g.playerno(); i++ )
  {
    if( hi.teamofplayer( g.player(i) ) == hi.team() )
    {
      points_p += g.points_of_player( g.player(i) );
    }
  }

  Trick tr = g.trick_current();
  if( tr.isfull() )
    if( tr.winnerteam() == hi.team() )
    {
      points_p += tr.points();
    } 

  return points_p;
}

///calculates all point of all players not of own team in Game g
int calcPointsOfOppositeTeam( HeuristicInterface const& hi, Game const& g)
{
  int points_p = 0;

  for( unsigned int i = 0; i < g.playerno(); i++ )
  {
    if( hi.teamofplayer( g.player(i) ) != hi.team() )
    {
      points_p += g.points_of_player( g.player(i) );
    }
  }

  Trick tr = g.trick_current();
  if( tr.isfull() )
    if( tr.winnerteam() != hi.team() )
    {
      points_p += tr.points();
    } 

  return points_p;
}
/**********************************************************************
 *
 **     bool Heuristics::say_no90(HeuristicInterface const& hi ,Game g)
 *
 **    Parameters:  HeuristicInterface who checks for an announcement
 *
 **    Result: true if hi should say No90
 **            false otherwise
 *
 **    Version: Beta
 *
 **    Description: 
 *
 **********************************************************************/
bool 
Heuristics::say_no90( HeuristicInterface const& hi ,const Game& g )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		"Heuristics::say_no90" );
  int value = 0;

  value = CalcHandValue( hi, g );

  int own_p = calcPointsOfOwnTeam( hi, g );
  int opp_p = calcPointsOfOppositeTeam( hi, g );

  if( own_p > 150 )
    DEBUG_RETURNING( true, 
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::say_no90");


  opp_p+= 5 * g.tricks_remaining_no();
  own_p+= 6 * g.tricks_remaining_no();

  own_p += (int)(1.4 * hi.hand().numberoftrumps()
		 * hi.hand().numberofswines());
  own_p += (int)(1.4 * hi.hand().numberoftrumps()
		 * hi.hand().numberofhyperswines());

  if( GAMETYPE::is_solo( hi.game().type() ) )
  {
    own_p += 20 * hi.hand().numberoftrumps();
    opp_p += 20 * ( hi.remaining_trumps() - hi.hand().numberoftrumps() );
  }



  DEBUG_RETURNING(
		  (   (    value + 3 * (int)hi.value(Aiconfig::ANNOUNCELIMITDEC )
		       >     (int)hi.value(Aiconfig::ANNOUNCELIMIT)
		       + (int)hi.value(Aiconfig::ANNOUNCECONFIG)
		      )

		   && own_p > 2 * opp_p  
		   && opp_p < 90 
		   && (   opp_p > 0  
		       || g.rule().remaining_tricks(ANNOUNCEMENT::NO90) == g.tricks_remaining_no() 
		      )
		  ), 
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::say_no90");
}

/**********************************************************************
 *
 **    bool Heuristics::say_no60(HeuristicInterface const& hi ,Game g)
 *
 **    Parameters:  HeuristicInterface who checks for an announcement
 *
 **    Result: true if hi should No60
 **            false otherwise
 *
 **    Version: Beta
 *
 **    Description: 
 *
 **********************************************************************/
bool 
Heuristics::say_no60( HeuristicInterface const& hi, const Game& g )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		"Heuristics::say_no60" );
  int value = 0;

  value = CalcHandValue( hi, g );

  int own_p = calcPointsOfOwnTeam( hi, g );
  int opp_p = calcPointsOfOppositeTeam( hi, g );

  if( own_p > 180 )
    DEBUG_RETURNING( true, 
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::say_no60");


  opp_p+= 5 * g.tricks_remaining_no();
  own_p+= 5 * g.tricks_remaining_no();

  own_p += 3 * hi.hand().numberoftrumps() * hi.hand().numberofswines();
  own_p += 3 * hi.hand().numberoftrumps() * hi.hand().numberofhyperswines();

  DEBUG_RETURNING(
		  (   (  value + 5 * (int)hi.value(Aiconfig::ANNOUNCELIMITDEC)
		       >   (int)hi.value(Aiconfig::ANNOUNCELIMIT)
		       + (int)hi.value(Aiconfig::ANNOUNCECONFIG)
		      )
		   && own_p > 3 * opp_p  
		   && opp_p < 60 
		   && (   opp_p > 0  
		       || g.rule().remaining_tricks(ANNOUNCEMENT::NO60) == g.tricks_remaining_no()
		      )
		  ),
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::say_no60"); 
}


/**********************************************************************
 *
 **    bool Heuristics::say_no30(HeuristicInterface const& hi ,Game g)
 *
 **    Parameters:  HeuristicInterface who checks for an announcement
 *
 **    Result: true if hi should say no30
 **            false otherwise
 *
 **    Version: Beta
 *
 **    Description: 
 *
 **********************************************************************/
bool 
Heuristics::say_no30( HeuristicInterface const& hi, const Game& g )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		"Heuristics::say_no30" );
  int value = 0;

  value = CalcHandValue( hi, g );

  int own_p = calcPointsOfOwnTeam( hi, g );
  int opp_p = calcPointsOfOppositeTeam( hi, g );

  if( own_p > 210 )
    DEBUG_RETURNING( true, 
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::say_no30");


  opp_p+= 4 * g.tricks_remaining_no();
  own_p+= 5 * g.tricks_remaining_no();


  DEBUG_RETURNING(
		  (  (   value + 6 * (int)hi.value(Aiconfig::ANNOUNCELIMITDEC)
		      >   (int)hi.value(Aiconfig::ANNOUNCELIMIT)
		      + (int)hi.value(Aiconfig::ANNOUNCECONFIG)
		     )
		   && own_p > 7 * opp_p  
		   && opp_p < 30   
		   && (    opp_p > 0   
		       || g.rule().remaining_tricks(ANNOUNCEMENT::NO30) == g.tricks_remaining_no()
		      )
		  ), 
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::say_no60");                  
}



/**********************************************************************
 *
 **     static bool Heuristics::say_no0(HeuristicInterface const& hi ,Game g)
 *
 **    Parameters:  HeuristicInterface who checks for an announcement
 *
 **    Result: true if hi should say No0
 **            false otherwise
 *
 **    Version: Beta
 *
 **    Description: 
 *
 **********************************************************************/
bool 
Heuristics::say_no0( HeuristicInterface const& hi, const Game& g )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
		"Heuristics::say_no0" );
  int value = 0;

  value = CalcHandValue( hi, g );

  bool has_trick = false;

  for( unsigned int i = 0; i < g.playerno(); i++ )
  {
    if( hi.teamofplayer( g.player(i) ) != hi.team() )
    {
      has_trick = has_trick || ( g.numberoftricks_of_player( g.player(i) ) != 0 );
    } 
  }  

  DEBUG_RETURNING(
		  (  (  value + 6 * (int)hi.value(Aiconfig::ANNOUNCELIMITDEC)
		      >   (int)hi.value(Aiconfig::ANNOUNCELIMIT)
		      + (int)hi.value(Aiconfig::ANNOUNCECONFIG)
		     ) 
		   && !has_trick 
		   && g.rule().remaining_tricks(ANNOUNCEMENT::NO0) == g.tricks_remaining_no()
		  ),
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::say_no0"); 


}

/**********************************************************************
 *
 **    Card play_highest_color(Trick const& t,AI& hi,Card::COLOR c)
 *
 **    Parameters:
 *
 **    Result: finds highest card of color c
 **            If Card() then you should calculate a new best color
 *
 **    Version: Beta
 *
 **    Description:
 *
 **********************************************************************/

Card 
Heuristics::play_highest_color( Trick const& t, 
			       HeuristicInterface const& hi,
			       Card::Color co )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::play_highest_color" );

  Hand h = hi.hand();
  HandCard c( h );
  unsigned i;

  for( i = 0; i < h.cardsnumber(); i++ )
  {
    // find any card
    if( h.card( i ).color() == co )
    {
      c = h.card( i );
      break;
    }
  }

  if( c == Card() ) 
  {
    DEBUG_RETURNING( c, 
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::play_highest_color");
  }

  for( i = 0; i < h.cardsnumber(); i++ )
  {
    // find highest card
    if(   h.card(i).color() == co 
       && c.less( h.card(i) ) 
      )
      c = h.card(i);
  }

  if(   hi.no() == hi.game().soloplayer().no()  
     || hi.hand().numberof( Card::ACE ) != 0 ) 
    for( unsigned value = 0 ;  value < ::party.rule()(Rule::NUMBER_OF_CARD_VALUES); value++ )
    {
      Card h = Card( c.color(), Card::InttoValue( value ) );
      if( hi.playedcard( h ) < 1 && c.less( h ) )
	c = Card();
    }	


  DEBUG_RETURNING( c,
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::play_highest_color");
}


/**********************************************************************
 *
 **    Card drawTrumps(Trick const& t,AI& hi )
 *
 **    Parameters:
 *
 **    Result: play trumps to draw the last trumps
 *
 **    Version: 0.6.1
 *
 **    Description:
 *
 **********************************************************************/
Card 
Heuristics::draw_trump( Trick const& t, HeuristicInterface const& hi )
{
  Card c = Card();
  if( !t.isstartcard() )
    return c;

  if(    hi.hand().numberoftrumps() > hi.remaining_trumps() 
     && hi.remaining_trumps() > 0 )
  {	
    HandCards const h = hi.hand().validcards( t );
    unsigned i;

    for( i = 0; i < h.cardsnumber(); i++ )
    {
      // find any trump
      if( h.card( i ).istrump() )
      {
	c = h.card(i);
	break;
      }
    }

    if( c == Card() ) 
    {
      return c;
    }

    for( i = 0; i < h.cardsnumber(); i++ )
    {
      // find highest card
      if(   h.card( i ).istrump() 
	 && !h.card( i ).less( c )
	)
	c = h.card( i );
    }
  }
  return c;
}

/**********************************************************************
 *
 **    Card getTrickForKeepingAnnouncement(Trick const& t, HeuristicInterface const& hi )
 *
 **    Parameters:
 *
 **    Result: 
 *
 **    Version: 0.7.0
 *
 **    Description:
 *
 **********************************************************************/
Card 
Heuristics::getTrickForKeepingAnnouncement( Trick const& t, 
					   HeuristicInterface const& hi )
{
  // ToDo: Gegenansagen
  Card c = Card();
  if( hi.game().announcement_of_team(  hi.team() ).announcement != ANNOUNCEMENT::NOANNOUNCEMENT )
  {
    unsigned oppositeToGet = 120;
    for( int a = (int)ANNOUNCEMENT::NO90;
	a <= hi.game().announcement_of_team(  hi.team() ).announcement;
	a++ )
      oppositeToGet -= 30; 

    if(    hi.game().points_of_team( opposite( hi.team() ) ) + t.points() + 10 
       >= oppositeToGet
       && t.winnerteam() == opposite( hi.team() )
      )
      c = best_winning_card( t, hi, 30 );
  }

#ifdef POSTPHONED
  if( hi.game().announcement_of_team(  opposite( hi.team() ) ).announcement != ANNOUNCEMENT::NOANNOUNCEMENT )
  {
    unsigned ownToGet = 120;
    for( int a = (int)ANNOUNCEMENT::NO90;
	a <= hi.game().announcement_of_team(  opposite( hi.team() ) ).announcement;
	a++ )
      ownToGet -= 30; 

    if(    hi.game().points_of_team( hi.team() ) + t.points() + 10 >= ownToGet
       && t.winnerteam() == opposite( hi.team() )
      )
      c = best_winning_card( t, hi, 30 );
  }
#endif
  return c;
}

/**********************************************************************
 *
 **    Announcement make_reply(HeuristicInterface const& hi ,const Game& g)
 *
 **    Parameters:
 *
 **    Result: Announcment::Reply if wanted
 *
 **    Version: 0.6.3
 *
 **    Description:
 *
 **********************************************************************/
Announcement 
Heuristics::make_reply( HeuristicInterface const& hi, const Game& g )
{
  DEBUG_CALLING( INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::make_reply" );

  if( g.announcement_of_team( opposite( hi.team() ) ).announcement 
     != ANNOUNCEMENT::NOANNOUNCEMENT )
  {
    int value = 0;

    value = CalcHandValue( hi, g ); 

    for( int a = (int)ANNOUNCEMENT::NO90;
	a <= g.announcement_of_team( opposite( hi.team() ) ).announcement;
	a++ ) 
      value -= (int)hi.value(Aiconfig::ANNOUNCECONFIGREPLY);

    if( value >=  (int)hi.value(Aiconfig::ANNOUNCELIMITREPLY) )
      DEBUG_RETURNING( ANNOUNCEMENT::REPLY,
		      INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::make_reply" );
  }
  DEBUG_RETURNING( ANNOUNCEMENT::NOANNOUNCEMENT,
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		  "Heuristics::make_reply" );

} 

/**
 ** -> result
 **
 ** @param	old_card	best card till now
 ** @param	test_card	card to test
 ** @param	hi		hi (with the configuration)
 **
 ** @return	whether the card 'test_card' is to prefered to 'old_card'
 **
 ** @author	Borg Enders
 **
 ** @version	0.6.9
 **/
bool
better_points_optimize(HandCard const& old_card,
		       HandCard const& test_card,
		       HeuristicInterface const& hi)
{
  return (  (  hi.game().trick_current_no()
	     < hi.value(Aiconfig::FIRST_TRICK_FOR_TRUMP_POINTS_OPTIMIZATION)
	    )
	  || ( old_card.value() <= test_card.value() )
	 );
} // bool better_points_optimize(HandCard old_card, HandCard test_card, Ai hi)
