/**********************************************************************
 *
 *   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 "cards_information.of_player.h"
#include "../../game/game.h"
#include "../../party/party.h"
#include "../../party/rule.h"
#include "../../card/trick.h"

/* Documentation
 *
 * Each heuristic should contain the following documentation for automatic
 * generation of the description.
 * Note that the text shall not be split into multiple lines.
 * The name must be the first entry and the action the last.
 * For a real example look at 'Heuristics::serve_color_trick()'.
 *
 * @heuristic::name        the name of the heuristic (as in '::name(Heuristic)')
 * @heuristic::idea        the main idea for the heuristic
 * @heuristic::comment     a simple comment
 * @heuristic::condition   a condition for the heuristic
 * @heuristic::condition   a second condition for the heuristic
 * @heuristic::action      which card is taken
 */



// missing

// @heuristic::name   choose pfund poverty
// @heuristic::idea   ???

// @heuristic::name   meatless: play highest color
// @heuristic::idea   ???

// @heuristic::name   picture: second best trump
// @heuristic::idea   ???

// @heuristic::name   color: choose ace
// @heuristic::idea   ???

// @heuristic::name   color: best winning
// @heuristic::idea   ???

// @heuristic::name   color: jab for ace
// @heuristic::idea   ???

// @heuristic::name   color: play low high
// @heuristic::idea   ???




// special heuristics

// @heuristic::name      no heuristic
// @heuristic::idea      internal use: no heuristic was used, yet

// @heuristic::name      manual
// @heuristic::idea      The card is played manual (from the human).
//
// @heuristic::name      bug report
// @heuristic::idea      Play the card from the bug report.
// @heuristic::comment   This heuristic is used so that in a bug report replay
// @heuristic::comment   the card suggestion is the card from the bug report.

// @heuristic::name      network
// @heuristic::idea      The card is played by the network player.

// @heuristic::name      only valid card
// @heuristic::idea      If there is only one valid card, play it.


/*
 Ideen

 normales Spiel
 * Partner sitzt hinten:
   Farbe anspielen, die er nicht hat und von der noch mindestens 1 draußen ist
 * Startspieler aber kein Farb-As:
   Re: Karo König, Karo Neun, Karo Bube (damit Partner mit Kreuz-Dame rausrückt)
   Kontra: Lange Farbe anspielen (>= 4)
 * Partner sitzt hinter einem, beide Gegner vorne (Trumpfstich):
   - Pfunden (wenn Fuchs / Karo Zehn da)
   - rübergehen, damit Partner Sorgen los wird
 * Wenn Partner direkt nach mir sitzt und noch nicht dran war und kein As gespielt hat:
   Spiele Farbe an, damit er mit potentiellen As übernehmen kann

 Armut (z.B. seed 27350 (3) )
 * Erster Kontra Spieler nach Re:
 Wenn Partner noch übertrumpfen kann (trump limit?): Punkte/drunter bleiben.
 (damit er hinten sitzt)
 wenn Partner nicht übertrumpfen kann (-> cards information): selber übertrumpfen oder kleinen rein.
 * Armutler nach Partner:
 Gegner haben Stich: wenig Punkte geben
 */

// ToDo: own trumplimit value for poverty

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 )
{
  // @heuristic::name   play to marry
  // @heuristic::idea   play card to marry
  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 )
{
  // @heuristic::name   choose ace
  // @heuristic::idea   for the first card in a trick choose the best color Ace to play
  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);
    }
  } // for (i < ha.cardsnumber())

  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)
{
 // @heuristic::name   ?  best winning card single picture  ?
 // @heuristic::idea   Only valid for soli with only one picture as trump: Tries to find the best card to win a trick depending on different values like Aiconfig::LIMITQUEEN    
  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 )
{
 // @heuristic::name   ?  best winning card double picture  ?
 // @heuristic::idea   Only valid for soli with only tow picutes as trump: Tries to find the best card to win a trick depending on different values like Aiconfig::LIMITQUEEN    
  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 )
{
 // @heuristic::name   ?  best winning card triple picture  ?
 // @heuristic::idea Only valid for soli with only three pictures as trump: Tries to find the best card to win a trick depending on different values like Aiconfig::LIMITQUEEN    
  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 )
{
  // @heuristic::name   best winning card
  // @heuristic::idea   Tries to find the best card to win a trick depending on different values like Aiconfig::LIMITQUEEN    

  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 );
	if (!t.islastcard())
	{
	  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 );
	     if (!t.islastcard())
	     {
	       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()
	     && ha.card(i).less( c ) 
	     && (   !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 
		              || (   hi.color_runs( t.startcard().color() ) == 0 
		                  && hi.game().type() == GAMETYPE::POVERTY 
    				     ) 
		             )     
		         && !t.islastcard()
		       )
		    || (    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 (hi.game().type() == GAMETYPE::POVERTY && hi.no() != hi.game().soloplayer().no() && !t.islastcard() ) //in a poverty game no further optimation
  {
    DEBUG_RETURNING( c, INFO_HEURISTICS && INFO_OTHER_FUNCTION, 
	  	  "Heuristics::best_winning_card()" );
  }


  // 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() 
	   && c.value() != Card::ACE
	   && (   t.islastcard() 
	       || allmyteam 
	       || (   hi.color_runs( t.startcard().color() ) == 0
	       && hi.game().type() != GAMETYPE::POVERTY 
		   && !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 
		&& c.value() != Card::ACE
		&& 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 
		&& c.value() != Card::ACE 
		&& 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 )
{
  // @heuristic::name   ?  lowest best trump  ?
  // @heuristic::idea   find smallest best trump card (avoiding for example a fox in most situations)	
  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 )
{
  // @heuristic::name   ?  choose pfund card  ?
  // @heuristic::idea   find a pfund card	
  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() );


  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()
	      )
	   || (   ! hi.cards_information().higher_card_exists( t.winnercard())
	   			//!t.winnercard().less( Card( Card::CLUB, Card::QUEEN ) )
	        && hi.teamofplayer( t.winnerplayer() )== hi.team()  
	       )
	   || (    hi.game().type() == GAMETYPE::MARRIAGE 
			&& hi.game().soloplayer().no() == hi.no()
			&& hi.game().marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET
			&& hi.game().marriage_selector() == MARRIAGE_SELECTOR::FIRST_FOREIGN 
		  )  
	  ) 
       && !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 
	        )
	    ) || (    hi.game().type() == GAMETYPE::MARRIAGE 
			&& hi.game().soloplayer().no() == hi.no()
			&& hi.game().marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET
			&& hi.game().marriage_selector() == MARRIAGE_SELECTOR::FIRST_FOREIGN 
		  ) 
	)
      { 
	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 );
      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)
		    || hi.hand().numberof(local_ace.color() ) == 1 
		    )
		) )
	 && !local_ace.istrump( hi.game() )
	 && t.isvalid(local_ace, hi.hand())
	 && (   hi.teamofplayer(t.winnerplayer()) == hi.team() 
	     || solo_check 
	     || allmyteam 
	    ) 
	)
      {
	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
       && (  hi.color_runs(ha.card(i).color()) > 0  
           || (   t.winnercard().istrump() 
                && allmyteam )
           )
       && 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 )
{
  // @heuristic::name   choose for color trick
  // @heuristic::idea   choose a card for a color trick either a pfund for the own team or a small card for the opponents
  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()) 
              && (  (   !hi.game().rule()(Rule::DOLLEN) 
                     || t.startcard().color() != Card::HEART)                      
                     || allmyteam); 
    }
    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)
{
  // @heuristic::name   retry color
  // @heuristic::idea   serve a color again, which was not jabbed or jabbed by the own team	
  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;
    
    // check that the trick could get through
    if (hi.cards_information().played(*c)
	+ (hi.game().playerno() - 1)
	+ hi.hand().numberof(*c)
	> hi.game().numberof(*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)

/**
 ** 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.
 **      or they can have the color
 ** 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)
{
  // @heuristic::name   serve color trick
  // @heuristic::idea   When a player cannot get a color trick take the card with the smallest value. The main case is that one is the last to play and has only got to take ones lowest card.

  // @heuristic::condition   it must not be the first card in the trick
  if (trick.isstartcard())
    return Card();

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

  // @heuristic::condition   the trick must be a color trick
  if (trick.startcard().istrump())
    return Card();

  // @heuristic::condition   one has to serve the color
  if (hi.hand().numberof(color) == 0)
    return Card();

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

  // @heuristic::condition   the winnercard must be an ace or a trump or one does not have the ace in the non-trump case
  if (!(   (trick.winnercard().value() == Card::ACE)
	|| trick.winnercard().istrump()
	|| (   !trick.winnercard().istrump()
	    && (color_cards.numberof(Card(color, Card::ACE)) == 0) )
       ) ) {
    return Card();
  } // if (trick.winnercard.value() != Card::ACE) && (...)

  // @heuristic::condition   the winnerplayer must not be of the own team
  Player const& winnerplayer = trick.winnerplayer();
  if (hi.teaminfo( winnerplayer ) == hi.team())
    return Card();

  // @heuristic::condition   it must not be an undetermined marriage
  if (   (hi.game().type() == GAMETYPE::MARRIAGE)
      && (hi.game().marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET)
     )
    return Card();

  // @heuristic::condition   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())
      return Card();

  // @heuristic::action   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);

  return lowest_card;
} // Card Heuristics::serve_color_trick(Trick trick, HeuristicInterface 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
 **      or diamond king against spade jack or higher
 ** 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
 ** @author	Borg Enders
 **
 ** @version	0.6.8
 **/
Card
Heuristics::serve_trump_trick(Trick const& trick,
			      HeuristicInterface const& hi)
{
  // @heuristic::name   serve trump trick
  // @heuristic::idea   When a player cannot get a trump trick take the card with the smallest value. The main case is that one is the last to play and has only got to take ones lowest card.
  // 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::THROWN_NINES:
    case GAMETYPE::THROWN_KINGS:
    case GAMETYPE::THROWN_NINES_AND_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;

    // special case: diamond king
    // play it if the alternatives are diamond ten, diamond ace or >= spade jack
    if (lowest_card == Card(hi.game().trumpcolor(), Card::KING)) {
      card = lowest_card;
      for( HandCards::const_iterator c = trump_cards.begin() + 1;
	  c != trump_cards.end();
	  ++c ) {
	if (  (c->value() == Card::NINE)
	    || (   (c->value() == Card::JACK)
		&& (c->color() <= Card::HEART)
	       ) ) {
	  card = Card();
	  break;
	}
      } // for (c \in trump_cards)
    } // if (lowest_card == trump king

    if( lowest_card.points() == lowest_points )
      card = lowest_card;
    break;
  case GAMETYPE::THROWN_NINES:
  case GAMETYPE::THROWN_KINGS:
  case GAMETYPE::THROWN_NINES_AND_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())

  return 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 )
{
  // @heuristic::name   choose pfund
  // @heuristic::idea   choose for trick won by the own team a pfund	
  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()");
}

/**
 ** return lowest allowed Jack or Queen if they are trump
 ** and there is a ace to play, which is not already jabbed,
 ** else return empty card
 **
 ** @param     t    current trick
 ** @param     hi   heuristic interface
 **
 ** @result    -> description
 **
 ** @author    Borg Enders
 **
 ** @version 0.6.9
 **/
Card 
Heuristics::jab_for_ace( Trick const& t, HeuristicInterface const& hi )
{
  // @heuristic::name   jab for ace
  // @heuristic::idea   jab a trick to serve next trick a ace
  Card const ace = Heuristics::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)
     ) 
    return Card();


  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() )
	    )
	) 
    )
    return best_winning_card( t, hi, 
			     hi.value( Aiconfig::LIMITDOLLE ) - ( 5 - t.actcardno() ) );

  return Card();
} // Card Heuristics::jab_for_ace( Trick t, HeuristicInterface hi )


/**********************************************************************
 *
 **    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)
{
  // @heuristic::name   play low high
  // @heuristic::idea   play changing low and high trumps to force opponent to jab or either draw his trumps	
  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())) // ToDo: Hier wird doch die erste Karte im Trumpf betrachtet, daher dürfte die Bedingung nie greifen (siehe auch unten)
	     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)
{
  // @heuristic::name   ?  second best trump  ?
  // @heuristic::idea   play the seconmd highest trump (especially useful for solo player in picture soli)	
  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 )
{
  // @heuristic::name   play for team
  // @heuristic::idea put a good card in a trick of the team mate
  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;

    // ToDo: Reihenfolge: Erst versuchen, Fuchs loszuwerden, dann die Zehn

    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" );

    // ToDo: Wieso ist eine Neun ein Spiel 'für' das eigene Team? Da gibt doch ein Bube mehr Punkte
    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 )
{
  // @heuristic::name   jab fox
  // @heuristic::idea   jab a fox if played in this trick
  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 )
{
  // @heuristic::name   try for doppelkopf
  // @heuristic::idea   try to get a doppelkopf for the own team	
  // 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 ?
  if (   !trick.islastcard()
      && hi.teamofplayer( trick.lastplayer() ) != hi.team() )
    return Card();

  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 )
{
  // @heuristic::name   create fehl
  // @heuristic::idea   create a missing color (only allowed for first card depending on Aiconfig::FEHLCREATIONONFIRSTCARD)	
  // if this trick is still worth fehl creation
  if( !(hi.game().trick_current_no() < hi.value(Aiconfig::LASTFEHLCREATION) ) )
    return Card();

  // if this is the firstcard 
  if( hi.value( Aiconfig::FEHLCREATIONONFIRSTCARD ) ) 
    if (t.actcardno() == 0 )
      return choose_best_fehl( t, hi );

  // 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() ) 
      )
      return choose_best_fehl( t,hi );

    // the winnercard is already a no trump ace 
    if(  !t.winnercard().istrump() 
       && (t.winnercard().value() >= Card::ACE) )
      return choose_best_fehl( t,hi );
  }

  return Card();
} // Card Heuristics::create_fehl( Trick t, HeuristicInterface hi )

/**********************************************************************
 *
 **    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 )
{
  // @heuristic::name   ?  calc hand value  ?
  // @heuristic::idea calculates the value of a hand for decision making of announcements, soli decisions,...
  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");
}

/**
 ** 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
 **
 ** @todo       if the opposite team has played a high card (p.e. dolle)
 **             do _not_ pfund although the own teammate is last player.
 ** @todo       if an opposite player is behind, check, that the winnercard
 **             of the teammate is high enough
 **		(-> play_pfund?)
 **/
Card
Heuristics::poverty_special_play_pfund(Trick const& trick,
				       HeuristicInterface const& hi)
{
 // @heuristic::name        poverty: special: play pfund
 // @heuristic::idea        If the trick goes or will go to the own team, play a pfund. This heuristic should only be used by the poverty player.
 
  if (trick.isstartcard())
    return Card();

 // @heuristic::condition   the last player must be of the own team (oneself or the partner)
  if (hi.teaminfo(trick.lastplayer()) != hi.team() )
    return Card();

  // the teammate
  vector<Player*>::const_iterator teammate;
  for (teammate = hi.game().players_begin();
       teammate != hi.game().players_end();
       ++teammate)
    if (   ((*teammate)->no() != hi.no())
	&& (hi.teamofplayer(**teammate) == TEAM::RE) )
      break;

 // @heuristic::condition   the own team has already the trick or the partner is behind
  if (   (trick.winnerteam() != hi.team())
      && (trick.cardno_of_player(**teammate) < trick.actcardno()) )
    return Card();

 // @heuristic::action      play a pfund
  // ToDo: Borg: use 'play_pfund'
  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_special_play_pfund(Trick trick, HeuristicInterface hi)

/**
 ** 1) poverty player
 ** 2) contra players get trick
 ** give as few points as possible
 **
 ** reason: do not give points to the opposite team
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **
 ** @todo      choose a 'good' color (to keep as much freedom as possible)
 ** @todo      'anfüttern' of a high re-card with a king
 ** @todo      not in the first color run, if the re has or can get the trick
 **/
Card
Heuristics::poverty_special_give_no_points(Trick const& trick,
					   HeuristicInterface const& hi)
{

 // @heuristic::name        poverty: special: give no points
 // @heuristic::idea        If the contra team gets the trick, give as few points as possible.


 // @heuristic::condition   the player must be the poverty player
  if (hi.game().type() != GAMETYPE::POVERTY)
    return Card();
  if (hi.no() != hi.game().soloplayer().no())
    return Card();

  // the accepter of the poverty
  Player const* re_player = NULL;
  for (vector<Player*>::const_iterator p = hi.game().players_begin();
       p != hi.game().players_end();
       ++p) {
    if (   ((*p)->team() == TEAM::RE)
	&& ((*p)->no() != hi.no()) ) {
      re_player = *p;
      break ;
    }
  } // for (p \in hi.game().players())

  DEBUG_ASSERTION(re_player,
		  "Heuristics::poverty_special_give_no_points()\n"
		  "  could not find 're player'");

  // check that the contra team gets the trick:
  // @heuristic::condition   the partner has played already
  // @heuristic::condition   contra already has the trick or the re player can be overjabbed
  if (trick.cardno_of_player(*re_player) > trick.actcardno())
    return Card();
  if (   trick.islastcard()
      && (trick.winnerplayerno() == re_player->no()) )
    return Card();
  // ToDo: Use 'cards information' for checking that the contra team can jab.
  if (   (trick.winnerplayerno() == re_player->no())
      && !trick.winnercard().less(hi.value(Aiconfig::TRUMPLIMIT_NORMAL)))
    return Card();

  // @heuristic::action   play the lowest card
  // search the lowest card
  // ToDo: check for a 'good' color
  // ToDo: create and use 'best_loosing_card'
  HandCards const cards = hi.hand().cards_single();
  HandCard card; // the card to play
  for (HandCards::const_iterator c = cards.begin();
       c != cards.end();
       ++c) {
    if (!trick.isvalid(*c))
      continue ;
    if (!card) {
      card = *c;
      continue;
    }
    if (c->value() < card.value())
      card = *c;
  } // for (c \in cards)

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

/**
 ** 1) re player in a poverty
 ** 2) color trick, cannot serve it
 ** play high trump (small queen or so)
 **
 ** reason: jab for the points
 **
 ** note: Should be called after 'serve color trick'
 **       because this is overreacted for the first color run.
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **
 ** @todo      for more points take a higher card
 **/
Card
Heuristics::poverty_re_trump_color_trick_high(Trick const& trick,
					      HeuristicInterface const& hi)
{
  // @heuristic::name   poverty: re: trump color trick high
  // @heuristic::idea   Jab the trick with a small queen in order to get the points

  // @heuristic::condition   re player (not poverty player) in a poverty
  if (hi.game().type() != GAMETYPE::POVERTY)
    return Card();
  if (   (hi.team() != TEAM::RE)
      || (hi.no() == hi.game().soloplayer().no()) )
    return Card();

  // @heuristic::condition   neither first nor last card in the trick
  if (trick.isstartcard())
    return Card();
  if (trick.islastcard())
    return Card();
  // @heuristic::condition   color trick which must not be served
  if (   trick.startcard().istrump()
      || hi.hand().existstcolor(trick.startcard().tcolor()))
    return Card();

  // @heuristic::action   play high trump (small queen)
  // ToDo: Borg: use 'choose_best_card'
  // search in the order one of the following cards to play
  list<Card> cards_to_play;
  cards_to_play.push_back(Card(Card::HEART,   Card::QUEEN));
  cards_to_play.push_back(Card(Card::DIAMOND, Card::QUEEN));
  cards_to_play.push_back(Card(Card::CLUB,    Card::JACK));
  cards_to_play.push_back(Card(Card::SPADE,   Card::JACK));
  cards_to_play.push_back(Card(Card::SPADE,   Card::QUEEN));
  cards_to_play.push_back(Card(Card::HEART,   Card::JACK));
  cards_to_play.push_back(Card(Card::DIAMOND, Card::JACK));

  for (list<Card>::const_iterator c = cards_to_play.begin();
       c != cards_to_play.end();
       ++c)
    if (hi.hand().numberof(*c) > 0)
      return *c;

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

/**
 ** 1) re player in a poverty
 ** 2) first player
 ** play a trump (diamond jack, ..., club queen)
 **
 ** reason: draw trump from the contra players
 **
 ** note: this heuristic shall be called after 'draw trump'
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
Card
Heuristics::poverty_re_play_trump(Trick const& trick,
				  HeuristicInterface const& hi)
{
  // @heuristic::name   poverty: re: play trump
  // @heuristic::idea   As first player draw trump fro the contra players.

  // @heuristic::condition   re player (not poverty player) in a poverty
  if (hi.game().type() != GAMETYPE::POVERTY)
    return Card();
  if (   (hi.team() != TEAM::RE)
      || (hi.no() == hi.game().soloplayer().no()) )
    return Card();

  // @heuristic::condition   first player
  if (!trick.isstartcard())
    return Card();

  // @heuristic::condition   partner has no trump
  if (!hi.cards_information().of_player(hi.game().soloplayer()).does_not_have(Card::TRUMP))
    return Card();


  // @heuristic::action   play a small jack
  list<Card> cards_to_play;
  cards_to_play.push_back(Card(Card::DIAMOND, Card::JACK));
  cards_to_play.push_back(Card(Card::HEART,   Card::JACK));
  cards_to_play.push_back(Card(Card::SPADE,   Card::JACK));
  cards_to_play.push_back(Card(Card::CLUB,    Card::JACK));
  cards_to_play.push_back(Card(Card::DIAMOND, Card::QUEEN));
  cards_to_play.push_back(Card(Card::HEART,   Card::QUEEN));
  cards_to_play.push_back(Card(Card::SPADE,   Card::QUEEN));
  cards_to_play.push_back(Card(Card::CLUB,    Card::QUEEN));

  for (list<Card>::const_iterator c = cards_to_play.begin();
       c != cards_to_play.end();
       ++c)
    if (hi.hand().numberof(*c) > 0)
      return *c;

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

/**
 ** 1) contra player in a poverty
 ** 2) first player
 ** play a color
 **
 ** reason: do not draw trump
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
Card
Heuristics::poverty_contra_play_color(Trick const& trick,
				      HeuristicInterface const& hi)
{
 // @heuristic::name   poverty: contra: play color
 // @heuristic::idea   Start with a color.

 // @heuristic::condition   contra player in a poverty
  if (hi.game().type() != GAMETYPE::POVERTY)
    return Card();
  if (hi.team() != TEAM::CONTRA)
    return Card();

 // @heuristic::condition   first player
  if (!trick.isstartcard())
    return Card();


  // the partner
  Player const* partner = NULL;
  // the player with the poverty
  Player const* poverty_player = &hi.game().soloplayer();
  // the accepter of the poverty
  Player const* re_player = NULL;
  for (vector<Player*>::const_iterator p = hi.game().players_begin();
       p != hi.game().players_end();
       ++p) {
    if ((*p)->no() == hi.no())
      continue;
    if ((*p)->no() == poverty_player->no())
      continue;
    if ((*p)->team() == TEAM::RE)
      re_player = *p;
    else
      partner = *p;
  } // for (p \in hi.game().players())

  DEBUG_ASSERTION((partner && poverty_player && re_player),
		  "Heuristics::poverty_contra_play_color()\n"
		  "  could not find 'partner' (" << partner << "), "
		  "'poverty_player' (" << poverty_player << ") or "
		  "'re_player' (" << re_player);

  // @heuristic::action   Play a color the partner does not have; if the partner is behind the re player, take a high card, else take a low one. Else take the longest color.
  { // search a color to play
    vector<Card::Color> const& colors = hi.game().rule().valid_card_colors();
    vector<Card::Color>::const_iterator c;
    // first search for a color the partner does not have

    for (c = colors.begin();
	 c != colors.end();
	 ++c)
      if (   hi.cards_information().of_player(*partner).does_not_have(*c)
	  && hi.hand().existstcolor(*c))
	break;

    if (c != colors.end()) {
      // There is a color the partner does not have.
      HandCards const cards = hi.hand().cards_single();
      HandCard card; // the card to play
      for (HandCards::const_iterator c = cards.begin();
	   c != cards.end();
	   ++c) {
	if (c->istrump())
	  continue;
	if (!hi.cards_information().of_player(*partner).does_not_have(c->tcolor()))
	  continue;
	if (!card) {
	  card = *c;
	  continue;
	}
	// If the partner is behind the re player, take the highest card
	// else take the lowest
	if (   (c->value() > card.value())
	    == (trick.cardno_of_player(*partner)
		> trick.cardno_of_player(*re_player)) )
	  card = *c;
      } // for (c \in cards)

      return card;

    } else { // if !(c != colors.end())
      // There is no color the partner does not have.
      // Play the color I have the most number of.
      Card::TColor color = Card::NOCARDCOLOR;
      unsigned max_num = 0;
      for (vector<Card::Color>::const_iterator c = colors.begin();
	   c != colors.end();
	   ++c)
	if (hi.hand().numberof(*c) > max_num)
	  color = *c, max_num = hi.hand().numberof(*c);

      // play the smallest card of the color
      HandCards const cards = hi.hand().cards_single();
      HandCard card; // the card to play
      for (HandCards::const_iterator c = cards.begin();
	   c != cards.end();
	   ++c) {
	if (c->tcolor() != color)
	  continue;
	if (c->value() < card.value())
	  card = *c;
      } // for (c \in cards)

      return card;
    } // if !(c != colors.end())

  } // search a color to play

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

/**
 ** 1) contra player in a poverty
 ** 2) color trick, cannot serve it
 ** 3) re player is behind
 ** 4) a) partner before:
 **       - partner has played low
 **       - there are some points in the trick
 **    b) partner behind:
 **       - partner can have the color
 ** play high trump (small queen or so)
 **
 ** reason: The re player does in most cases not have the color
 **         so make the trick expensive for him.
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **
 ** @todo       use 'best_winning_card()'
 **/
Card
Heuristics::poverty_contra_trump_color_trick_high(Trick const& trick,
						  HeuristicInterface const& hi)
{
  // @heuristic::name   poverty: contra: trump color trick high
  // @heuristic::idea   Play a high trump (small queen) in color tricks, so that the trick is expensive for the re player.

  // @heuristic::condition   contra player in a poverty
  if (hi.game().type() != GAMETYPE::POVERTY)
    return Card();
  if (hi.team() != TEAM::CONTRA)
    return Card();

  // @heuristic::condition   a color trck the player must not serve
  if (trick.isstartcard())
    return Card();
  if (   trick.startcard().istrump()
      || hi.hand().existstcolor(trick.startcard().tcolor()))
    return Card();

  // the partner
  Player const* partner = NULL;
  // the player with the poverty
  Player const* poverty_player = &hi.game().soloplayer();
  // the accepter of the poverty
  Player const* re_player = NULL;
  for (vector<Player*>::const_iterator p = hi.game().players_begin();
       p != hi.game().players_end();
       ++p) {
    if ((*p)->no() == hi.no())
      continue;
    if ((*p)->no() == poverty_player->no())
      continue;
    if ((*p)->team() == TEAM::RE)
      re_player = *p;
    else
      partner = *p;
  } // for (p \in hi.game().players())

  DEBUG_ASSERTION((partner && poverty_player && re_player),
		  "Heuristics::poverty_trump_color_trick_high()\n"
		  "  could not find 'partner' (" << partner << "), "
		  "'poverty_player' (" << poverty_player << ") or "
		  "'re_player' (" << re_player);


  // @heuristic::condition   the re player is behind
  if (trick.cardno_of_player(*re_player) < trick.actcardno())
    return Card();

  if (trick.cardno_of_player(*partner) < trick.actcardno()) {
    // @heuristic::condition   a) the partner has played low (trump limit)
    if (!(   !trick.card_of_player(*partner).istrump()
	  || trick.card_of_player(*partner).less(hi.value(Aiconfig::TRUMPLIMIT_NORMAL)))
       )
      return Card();

    // @heuristic::condition   a) there are some points (>= 8) in the trick
    if (trick.points() < 8) // *Value*
      return Card();
  } else { // if !(partner before)
    // @heuristic::condition   b) the partner is behind
    // @heuristic::condition   b) the partner can have the color
    if (hi.cards_information().of_player(*partner).does_not_have(trick.startcard().tcolor()))
      return Card();
  } // if !(partner before)
  // the partner was before and has played low

  // @heuristic::action   play high trump (small queen)
  // ToDo: Borg: use 'best_winning_card'
  // search in the order one of the following cards to play
  list<Card> cards_to_play;
  cards_to_play.push_back(Card(Card::HEART,   Card::QUEEN));
  cards_to_play.push_back(Card(Card::DIAMOND, Card::QUEEN));
  cards_to_play.push_back(Card(Card::CLUB,    Card::JACK));
  cards_to_play.push_back(Card(Card::SPADE,   Card::JACK));
  cards_to_play.push_back(Card(Card::SPADE,   Card::QUEEN));
  cards_to_play.push_back(Card(Card::HEART,   Card::JACK));
  cards_to_play.push_back(Card(Card::DIAMOND, Card::JACK));

  for (list<Card>::const_iterator c = cards_to_play.begin();
       c != cards_to_play.end();
       ++c)
    if (hi.hand().numberof(*c) > 0)
      return *c;

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

/**
 ** 1) contra player in a poverty
 ** 2) re has played trump
 ** 3) partner is behind and can jab the trick
 ** play a small trump so that the partner can get the trick
 **
 ** reason: make myself be behind the re player
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
Card
Heuristics::poverty_leave_to_partner(Trick const& trick,
				     HeuristicInterface const& hi)
{
  // @heuristic::name   poverty: contra: leave to partner
  // @heuristic::idea   Make myself be behind the re player.

  // @heuristic::condition   contra player in a poverty
  if (hi.game().type() != GAMETYPE::POVERTY)
    return Card();
  if (   (hi.team() != TEAM::CONTRA)
      && (hi.no() != hi.game().soloplayer().no()) )
    return Card();

  // the partner
  Player const* partner = NULL;
  // the player with the poverty
  Player const* poverty_player = &hi.game().soloplayer();
  // the accepter of the poverty
  Player const* re_player = NULL;
  for (vector<Player*>::const_iterator p = hi.game().players_begin();
       p != hi.game().players_end();
       ++p) {
    if ((*p)->no() == hi.no())
      continue;
    if ((*p)->no() == poverty_player->no())
      continue;
    if ((*p)->team() == TEAM::RE)
      re_player = *p;
    else
      partner = *p;
  } // for (p \in hi.game().players())

  DEBUG_ASSERTION((partner && poverty_player && re_player),
		  "Heuristics::poverty_contra_play_color()\n"
		  "  could not find 'partner' (" << partner << "), "
		  "'poverty_player' (" << poverty_player << ") or "
		  "'re_player' (" << re_player);

  // @heuristic::condition   the re player has played a trump
  // more precise: it has been started with trump and re has already played
  // (contains the case that the poverty player has started with trump)
  if (trick.isstartcard())
    return Card();
  if (!trick.startcard().istrump())
    return Card();
  if (trick.cardno_of_player(*re_player) > trick.actcardno())
    return Card();

  // @heuristic::condition   the partner is behind and can jab the trick
  if (trick.cardno_of_player(*partner) < trick.actcardno())
    return Card();
  // ToDo: use cards information
  if (!trick.winnercard().less(hi.value(Aiconfig::TRUMPLIMIT_NORMAL)))
    return Card();

  // @heuristic::action   play a low trump
  // ToDo: Borg: use 'play_pfund'?
  list<Card> cards_to_play;
  if (!hi.hand().has_swines())
    cards_to_play.push_back(Card(Card::DIAMOND, Card::ACE));
  cards_to_play.push_back(Card(Card::DIAMOND, Card::TEN));
  cards_to_play.push_back(Card(Card::DIAMOND, Card::KING));
  cards_to_play.push_back(Card(Card::DIAMOND, Card::NINE));
  // ToDo: Hand function 'next greater card'
  cards_to_play.push_back(Card(Card::DIAMOND, Card::JACK));
  cards_to_play.push_back(Card(Card::HEART,   Card::JACK));
  cards_to_play.push_back(Card(Card::SPADE,   Card::JACK));
  cards_to_play.push_back(Card(Card::CLUB,    Card::JACK));
  cards_to_play.push_back(Card(Card::DIAMOND, Card::QUEEN));
  cards_to_play.push_back(Card(Card::HEART,   Card::QUEEN));
  cards_to_play.push_back(Card(Card::SPADE,   Card::QUEEN));
  cards_to_play.push_back(Card(Card::CLUB,    Card::QUEEN));

  for (list<Card>::const_iterator c = cards_to_play.begin();
       c != cards_to_play.end();
       ++c)
    if (hi.hand().numberof(*c) > 0)
      return *c;

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

/**
 ** 1) contra player in a poverty
 ** 2) re player has won with a trump
 ** 3) can play a better card than the re player
 ** play next higher card
 **
 ** reason: If you can, overjab the re player in order to get points.
 **
 ** note: Should be called after 'leave to partner'
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Diether Knof
 **
 ** @version	0.7.3
 **/
Card
Heuristics::poverty_overjab_re(Trick const& trick,
			       HeuristicInterface const& hi)
{
  // @heuristic::name   poverty: contra: overjab re
  // @heuristic::idea   Overjab the re player in order to get the points.

  // @heuristic::condition   contra player in a poverty
  if (hi.game().type() != GAMETYPE::POVERTY)
    return Card();
  if (hi.team() != TEAM::CONTRA)
    return Card();

  // the accepter of the poverty
  Player const* re_player = NULL;
  for (vector<Player*>::const_iterator p = hi.game().players_begin();
       p != hi.game().players_end();
       ++p)
    if (   ((*p)->team() == TEAM::RE)
	&& ((*p)->no() != hi.game().soloplayer().no()) ) {
      re_player = *p;
      break;
    }

  DEBUG_ASSERTION(re_player,
		  "Heuristics::poverty_overjab_re()\n"
		  "  could not find 're player'");

  // @heuristic::condition   the re player has won so far with a trump
  if (trick.winnerplayer().no() != re_player->no())
    return Card();
  if (!trick.winnercard().istrump())
    return Card();

  // @heuristic::condition   the player can play a better card than the re player
  if (!(   trick.startcard().istrump()
	|| !hi.hand().existstcolor(trick.startcard().tcolor()) ) )
    return Card();

  // @heuristic::action   Take the best winning card.
  return Heuristics::best_winning_card(trick, hi);
} // Card Heuristics::poverty_overjab_re(Trick trick, HeuristicInterface hi)


/**
 ** best winning card for the last 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.7.3
 **/
Card
Heuristics::poverty_best_winning_card(Trick const& trick,
				      HeuristicInterface const& hi)
{
  // @heuristic::name   poverty: best winning card
  // @heuristic::idea   As last player in a trick take the lowest card to win the trick.

  // @heuristic::condition   only for the not poverty player in a poverty
  if (hi.game().type() != GAMETYPE::POVERTY)
    return Card();
  if (   (hi.team() != TEAM::RE)
      || (hi.no() == hi.game().soloplayer().no()) )
    return Card();

  // @heuristic::condition   last player in the trick
  if (!trick.islastcard() )
    return Card();

  // @heuristic::action   Play the best winning card.
  return Heuristics::best_winning_card( trick, hi );
} // Card Heuristics::poverty_best_winning_card(Trick trick, HeuristicInterface hi)

/**********************************************************************
 *
 **    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 )
{
  // @heuristic::name   ?  make announcement  ?
  // @heuristic::idea announcement decision bases on Heuristics::CalcHandValue
  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 )
{
  // @heuristic::name   ?  say no 90  ?
  // @heuristic::idea decision to make the annoncement no 90 depending on Heuristics::CalcHandValue	
  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.last_chance_to_announce(ANNOUNCEMENT::NO90,
						    g.player(hi.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 )
{
  // @heuristic::name   ?  say no 60  ?
 // @heuristic::idea decision to make the annoncement no 60 depending on Heuristics::CalcHandValue	
 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.last_chance_to_announce(ANNOUNCEMENT::NO60,
						    g.player(hi.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 )
{
  // @heuristic::name   ?  say no 30  ?
  // @heuristic::idea decision to make the annoncement no 30 depending on Heuristics::CalcHandValue	
  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.last_chance_to_announce(ANNOUNCEMENT::NO30,
						    g.player(hi.no()))
		      )
		  ), 
		  INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::say_no60");                  
}

/**
 ** test whether a 'no 0' could be announced
 ** checks, that the other team does not have a trick
 ** and that the hand value is big enoug.
 **
 ** @param     hi     heuristic interface
 ** @param     game   current gaem
 **
 ** @return    whether to announce 'no 0'
 **
 ** @author    Borg Enders
 ** @author    Diether Knof
 **
 ** @version   0.7.3
 **/
bool 
Heuristics::say_no0( HeuristicInterface const& hi, Game const& game )
{
  // @heuristic::name   ?  say no 0  ?
  // @heuristic::idea decision to make the annoncement no 0 depending on Heuristics::CalcHandValue	

  // make sure, that the other team does not have a trick
  if (game.hastrick(::opposite(hi.team())))
    return false;
  for (unsigned p = 0; p < game.playerno(); ++p)
    if (   (hi.teamofplayer(game.player(p)) != hi.team())
	&& game.hastrick(game.player(p)))
      return false;

  // as long as the current trick is not closed, it has to be taken into account
  if (   game.trick_current().isfull()
      && (hi.teamofplayer(game.trick_current().winnerplayer())
	  != hi.team()) )
    return false;

  // wait for the last possible moment
  if (!game.last_chance_to_announce(ANNOUNCEMENT::NO0,
				    game.player(hi.no())))
    return false;

  // ToDo: check that no opposite player has the highest trump card
  // Test: force a player by 'ANNOUNCELIMITDEC' high

  int const value = CalcHandValue( hi, game );

  return (value + 6 * static_cast<int>(hi.value(Aiconfig::ANNOUNCELIMITDEC))
	  > static_cast<int>(hi.value(Aiconfig::ANNOUNCELIMIT)
			     + hi.value(Aiconfig::ANNOUNCECONFIG))
	 );
} // bool Heuristics::say_no0( HeuristicInterface hi, Game game )

/**********************************************************************
 *
 **    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 )
{
  // @heuristic::name   meatless: play highest color
  // @heuristic::idea   Heuristic for solo::MEATLESS to play the longest color or follow previous color
  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 )
{
  // @heuristic::name   draw trump
  // @heuristic::idea play trumps to force other players to play also trumps	
  if( !t.isstartcard() )
    return Card();

  if (! (   (hi.hand().numberoftrumps() > hi.remaining_trumps()) 
	 && (hi.remaining_trumps() > 0) ) )
    return Card();

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

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

  if (!c)
    return Card();

  for(; 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 )
{
  // @heuristic::name   get trick for announcement
  // @heuristic::idea   If this trick would open the possibility for the other team to win there announcement try to win this trick for the own team	
  // 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 )
{
  // @heuristic::name   ?  make reply  ?
 // @heuristic::idea announcement reply decision bases on Heuristics::CalcHandValue
  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 preferred to 'old_card'
 **		that is has more points
 **
 ** @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)
