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

#include "constants.h"

#include "heuristics.h"

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

unsigned number_of_no_trumps_per_color();
Card lowest_card(Hand const& hand, Card::Value const cv,
		 HeuristicInterface const& hi);
Card lowest_jack(Hand const& hand, HeuristicInterface const& hi);
Card lowest_queen(Hand const& hand, HeuristicInterface const& hi);
Card highest_queen(Hand const& hand, HeuristicInterface const& hi);
Card highest_card(Hand const& hand, Card::Value const cv,
		  HeuristicInterface const& hi);

/**********************************************************************
 *
 **    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())
  {
    for ( i=0;  i < 2 * ::party.rule()(Rule::NUMBER_OF_CARD_VALUES); i+=2)
      if (!Card(Card::InttoColor(Card::CLUB),Card::InttoValue(i)).istrump())
	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())
	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()

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

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

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

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


  int notrump_per_color=number_of_no_trumps_per_color();


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

  Hand 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 && ha.numberof( ha.card(i) ) < ha.numberof( c )) 
	       ||   c == Card() 
	     )
	  &&( !hi.colorjabbed(ha.card(i).color())
	      || (    hi.jabbedbyownteam(ha.card(i).color()) 
		      && !hi.jabbedbyotherteam(ha.card(i).color() ) 
		 )
	    )
       )
    {
      bestcolor=h;
      c=ha.card(i);
    }
  }

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

}

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

  Card c=Card();

  unsigned i;

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

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

  for ( i=0; i < h.cardsnumber(); i++)
  {
    // find lowest card
    if (h.card(i).value()==cv &&
	h.card(i).less(c, hi.game()))
      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, hi.game()))
      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()");
}

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

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


  if ( t.isstartcard() ) DEBUG_RETURNING(Card(),INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()");
  ;
  Trick tr;
  Card c=Card();
  Hand ha=hi.hand().validcards(t);
  unsigned i;
  Game const& game = t.game();

  bool allmyteam=true;

  for (i=t.actcardno(); i<hi.game().playerno(); i++)
    allmyteam=allmyteam && (hi.team()==hi.teaminfo(t.player_of_card(i)));

  // 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), game) || allmyteam )  )
    {

      c=ha.card(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=false;

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

  // 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), game) || t.islastcard() || allmyteam)
	    && (ha.card(i).less(c, game)))
	{

	  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)
      /// 2do heart ten non trump, than also for heart tricks
     )
  {

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


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


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

	  break;
	} 

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

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

      }
    }
  }

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


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

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


  if ( t.isstartcard() ) DEBUG_RETURNING(Card(),INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()");
  ;
  Trick tr;
  Card c=Card();
  Hand ha=hi.hand().validcards(t);
  unsigned i;
  Game const& game = t.game();

  bool allmyteam=true;

  for (i=t.actcardno(); i<hi.game().playerno(); i++)
    allmyteam=allmyteam && (hi.team()==hi.teaminfo(t.player_of_card(i)));

  // 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), game) || allmyteam ))
    {

      c=ha.card(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=false;

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

  // 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), game) || t.islastcard() || allmyteam )
	    && (ha.card(i).less(c, game)))
	{

	  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)
      /// 2do heart ten non trump, than also for heart tricks
     )
  {

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


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


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

	  break;
	} 

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

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

      }
    }
  }

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

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

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


  if ( t.isstartcard() ) DEBUG_RETURNING(Card(),INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()");
  ;
  Trick tr;
  Card c=Card();
  Hand ha=hi.hand().validcards(t);
  unsigned i;
  Game const& game = t.game();

  bool allmyteam=true;

  for (i=t.actcardno(); i<hi.game().playerno(); i++)
    allmyteam=allmyteam && (hi.team()==hi.teaminfo(t.player_of_card(i)));

  // 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), game) || allmyteam ))
    {

      c=ha.card(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=false;

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

  // 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), game) || t.islastcard() || allmyteam )
	    && (ha.card(i).less(c, game)))
	{

	  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)
      /// 2do heart ten non trump, than also for heart tricks
     )
  {

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


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


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

	  break;
	} 

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

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

      }
    }
  }

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


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

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

  GameType gt = hi.game().type();
  if (gt==GAMETYPE::SOLO_JACK) 
    DEBUG_RETURNING(best_winning_card_single_picture( t, Card::JACK, hi, pointmodi ),INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()");
  if (gt==GAMETYPE::SOLO_QUEEN) 
    DEBUG_RETURNING(best_winning_card_single_picture( t, Card::QUEEN, hi, pointmodi ),INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()");
  if (gt==GAMETYPE::SOLO_KING)
    DEBUG_RETURNING(best_winning_card_single_picture( t, Card::KING, hi, pointmodi ),INFO_HEURISTICS && INFO_OTHER_FUNCTION, "Heuristics::best_winning_card()");

  if (gt==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()");

  if (gt==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()");

  if (gt==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()");

  if (gt==GAMETYPE::SOLO_KOEHLER) 
    DEBUG_RETURNING(best_winning_card_triple_picture( t, hi, pointmodi ),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();
  Hand ha=hi.hand().validcards(t);
  unsigned i;
  Game const& game = t.game();

  bool allmyteam=true;

  for (i=t.actcardno(); i<hi.game().playerno(); i++)
    allmyteam=allmyteam && (hi.team()==hi.teaminfo(t.player_of_card(i)));


  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), game) || allmyteam ))
    {

      if (ha.card(i).value()==Card::QUEEN && tpoints>hi.value(Aiconfig::LIMITQUEEN))
      {
	c=ha.card(i);
	break;
      } 
      if (ha.card(i).value()==Card::QUEEN)
	continue;


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

      if ( ha.card(i).isdolle() ||
	   ha.card(i).isswine() ||
	   ha.card(i).ishyperswine()      )
	continue;

      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=false;

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

  // 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 
				    || !Card( 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, game)) 
	   )
	{ 
	  c=ha.card(i);
	  continue;
	}

	if ( ha.card(i).value()>=Card::JACK )
	{
	  if (!t.isstartcard() && ha.card(i).value()<Card::QUEEN
	      && ( !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())< 1 )
		    || ha.card(i).less(c, game) ) )
	  {
	    c=ha.card(i);
	    continue;
	  }


	  if ((ha.card(i).isdolle() ||
	       ha.card(i).isswine() ||
	       ha.card(i).ishyperswine())
	      && tpoints>hi.value(Aiconfig::LIMITDOLLE) &&
	      ( (    c.value() < Card::QUEEN 
		     && !hi.color_runs(t.startcard().color())< 1 
		) 
		|| ha.card(i).less(c, game) 
	      ))
	  {

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


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

    } // if winneris
  }

  // if last card or first_color_run of trick let's check for a better card
  // Ace, ten, king, nine
  if (t.islastcard() || allmyteam
      ||( !t.isstartcard()
	  && !t.startcard().istrump()
	  && hi.color_runs(t.startcard().color())<1 && 
	  ( t.startcard().color()!= Card::HEART) || !Card( 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() && 
	     ( ha.cardsnumber() < 4 && 
	       hi.game().rule()(Rule::EXTRAPOINT_DOLLE_JABS_DOLLE) )
	     || ( ((    hi.game().swines_owner() != NULL 
			&& hi.teaminfo( *hi.game().swines_owner() ) != hi.team() )
		   || (   hi.game().hyperswines_owner() != NULL 
			  && hi.teaminfo( *hi.game().hyperswines_owner() ) != hi.team() ))
		  && hi.hand().numberoftrumps() < 5         
		)
	   ) 
	{    
	  c=ha.card(i);
	  break;
	} 	 

	// 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::KING && game.type()!=GAMETYPE::SOLO_KOEHLER)
	{        
	  if (c.value()!=Card::TEN || c.isdolle() )
	  {
	    c=ha.card(i);             
	  }
	  continue;
	}
	if ((ha.card(i).value()==Card::NINE) &&
	    (!ha.card(i).ishyperswine()))
	{
	  if ( (c.value()!=Card::TEN && c.value()!=Card::KING ) || c.isdolle() )
	  {

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

	if ( c.istrump() && ha.card(i).istrump() && ha.card(i).less(c, game )  )
	{
	  if ( ( c.value()!=Card::TEN && c.value()!=Card::KING ) 
	       || c.isdolle() || c.ishyperswine() )
	  { 
	    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();
  Hand 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).value() <c.value() &&
	!ha.card(i).istrump())
      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();
  Hand 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).value() < c.value() &&
	!ha.card(i).istrump() &&
	( 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()");

  Game const& game = t.game();
  Card c=Card();
  Hand 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, game) &&
	ha.card(i).istrump())
      c=ha.card(i);
  }

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


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

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

  Card c=Card();
  Hand ha=h.validcards(t);
  unsigned i;
  Game const& game = t.game();

  // 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, game) &&
	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(), game)))
    // maybe a diamond ten isn't all that bad
  {
    for ( i=0; i < ha.cardsnumber(); i++)
    {
      if (ha.card(i).less(c, game) &&
	  ha.card(i).istrump() &&
	  !ha.card(i).isfox())
	c=ha.card(i);
    }
  }

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

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

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

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

  Trick t = trick;

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

  unsigned i;

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

  for (i=t.actcardno(); i<hi.game().playerno(); i++)
    allmyteam =   allmyteam 
      && (hi.team()==hi.teaminfo(t.player_of_card(i)));


  Card c=Card();

  Hand ha=hi.hand().validcards(t);

  int notrump_per_color=number_of_no_trumps_per_color();

  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())
      numberofcards[c->color()] += hi.playedcard( *c );


  bool solo_check = 
    (   GAMETYPE::is_solo( hi.game().type() )  
	&& hi.color_runs(t.startcard().color()) == 0 
	&& t.winnerplayer() == hi.game().soloplayer() 
	&& t.winnercard().less( hi.trump_card_limit(), hi.game() ) 
	&& !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 ) &&
	(!ha.card(i).isswine()) )
    {
      t+=ha.card(i);
      if (hi.teamofplayer(t.winnerplayer())==hi.team())
	DEBUG_RETURNING(ha.card(i),
			INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			"Heuristics::choose_pfund()");
    }

  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) ) )
	 && !local_ace.istrump()
	 && 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()");

  // 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()");
    }

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

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

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

       )
    {
      DEBUG_RETURNING(ha.card(i),INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::choose_pfund()");
    }
  }
  // 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()");
    }

  // 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()");
    }

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

  // 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
       )
    {
      DEBUG_RETURNING(ha.card(i),INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::choose_pfund()");
    }

  // 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
       )
    {
      DEBUG_RETURNING(ha.card(i),INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::choose_pfund()");
    }

  // 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()");
    }

  // 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()");
    }

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


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

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

  Card c=Card();

  if ( t.isempty() ) return c;

  unsigned i;

  Hand ha;

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

  for (i=t.actcardno(); i<hi.game().playerno(); i++)
    allmyteam =   allmyteam 
      && (hi.team()==hi.teaminfo(t.player_of_card(i)));
  allmyteam = allmyteam && (hi.teamofplayer(t.winnerplayer())==hi.team() 
			    ||  t.winnercard().less( hi.trump_card_limit(), hi.game() ) );  	           

  // 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 ( ( 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 = (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(), hi.game() )
			 ) );

    pred = pred || allmyteam;

    if( hi.game().type()== GAMETYPE::POVERTY )
      pred = pred && ( hi.no() == hi.game().povertyPartner().no() ); 

    if ( pred || 
	 (      GAMETYPE::is_solo( hi.game().type() )  
		&& t.winnerplayer() != hi.game().soloplayer() 
		&&  hi.color_runs(t.startcard().color()) == 0 
		&&  t.winnercard().less( hi.trump_card_limit(), hi.game() ) 
		&&  !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=false;

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

    if (!trumpintrick )
    {

      c=best_winning_card(t,hi);
#if 0 // I don't know the purpose of thsi code!!!
      if(    hi.color_runs(t.startcard().color()) == 0 
	     && t.actcardno() >= 2 )
      {
	Card jack = highest_card( ha, Card::JACK, hi );
	if( jack ) c = jack;
      }
#endif	
      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, hi.game()))
	    DEBUG_RETURNING(c,
			    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)");

	  if (!c.less(Card(Card::DIAMOND,Card::QUEEN), hi.game()))
	    DEBUG_RETURNING(c,
			    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)");
	  }

	}

    }// 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, hi.game()))
	    DEBUG_RETURNING(c,
			    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
			    "Heuristics::choose_for_color_trick() (long)");
	  if (!c.less(Card(Card::DIAMOND,Card::QUEEN), hi.game()))
	    DEBUG_RETURNING(c,
			    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)");
}

/**
 **
 ** in the first run of the color play the lowest card
 ** prerequisites:
 **   1. the game must be a normal game or a determined marriage or a genscher
 **   2. it must not be the first card in the trick
 **   3. the trick must be a color trick;
 **   4. it must be the first run;
 **   5. the winnercard must be an ace or one does not have the ace;
 **   6. the winnerplayer must not be of the own team;
 **   7. the remaining players must not be of the own team.
 ** The main case is that one is the last to play and has only got to
 ** take ones lowest card.
 ** Remark: Perhaps 4. can be dropped.
 ** Remark: Perhaps 5. can be dropped if one does not have many cards of
 **		the color and no other has dropped the color earlier
 **
 ** @param	trick	current trick
 ** @param	hi	heuristic interface
 **
 ** @return	card to play, 'Card()' if the heuristic does not match
 ** 
 ** @author	Diether Knof
 **
 ** @version	0.6.6
 **
 **/
Card
Heuristics::serve_color_trick(Trick const& trick, HeuristicInterface const& hi)
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		"Heuristics::serve_color_trick(trick, hi)");

  // the game must be a normal game or a determined marriage or a genscher
  if ( (hi.game().type() != GAMETYPE::NORMAL)
       && (hi.game().type() != GAMETYPE::GENSCHER)
       && ( (hi.game().type() != GAMETYPE::MARRIAGE)
	    || (hi.game().marriage_determination_trickno() == 0) ) )
    DEBUG_RETURNING(Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_color_trick(trick, hi) = false");

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

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

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

  // it must be the first run of the color
  if (hi.color_runs(color) > 0)
    DEBUG_RETURNING(Card(),
		    INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		    "Heuristics::serve_color_trick(trick, hi) = false");

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

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

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

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

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

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


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

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

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

  bool soloPlayer = false;
  if (GAMETYPE::is_solo(hi.game().type()))
    for( unsigned i = t.actcardno(); i < hi.game().playerno(); i++ )
      if ( t.player_of_card( i ) == hi.game().soloplayer () )
	soloPlayer = true;
  // if winner is own team
  if (maybe_to_team(hi.teamofplayer(t.winnerplayer()))
      == hi.team() && ! soloPlayer)
    // if this is the last card or there is already a good trump
    if (t.islastcard() 
	|| (!t.winnercard().less(hi.trump_card_limit(), hi.game())))
    {
      DEBUG_RETURNING(choose_pfund_card(t,hi),
		      INFO_HEURISTICS && INFO_OTHER_FUNCTION,
		      "Heuristics::choose_pfund()");
    }

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

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


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

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

  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(), hi.game())))
     )

  {    		
    c = best_winning_card( t, hi, hi.value( Aiconfig::LIMITDOLLE ) );
  }

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

}

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


  if (t.isstartcard() 
      && (hi.hand().numberoftrumps() > (hi.game().tricks_remaining_no() / 2 ))) 
  {
    if (hi.next_low()) {
      Card c=lowest_jack(hi.hand(), hi);

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

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

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

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


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

  Card c=Card();
  Card bestcard;

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

  // first find highest trump on hand
  unsigned i;
  unsigned best=0;
  bestcard = h.card( best );
  for (i = 1; i < h.cardsnumber();i++)
  {
    if (h.card(i).istrump()
	&& bestcard.less(h.card(i), hi.game())) {
      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, hi.game())
	    || (h.card(i) == bestcard))
	&& (c.less(h.card(i), hi.game()))
	&& (i != best))
    {
      c=h.card(i);
    }
  }

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

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

  DEBUG_ASSERTION(false,
		  "Heuristics::SecondBestTrump(HeuristicInterface):\n"
		  "  control is after 'DEBUG_RETURNING'");

  return c; // ***
} // Card Heuristics::SecondBestTrump(HeuristicInterface const& hi)


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

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

  bool soloPlayer = false;
  if (GAMETYPE::is_solo(hi.game().type()))
    for( unsigned i = t.actcardno(); i < hi.game().playerno(); i++ )
      if ( t.player_of_card( i ) == hi.game().soloplayer () )
	soloPlayer = true;

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

    for(i=0; i<hi.hand().cardsnumber();i++)
    {
      Trick tr=t;
      if ((tr.isvalid(hi.hand().card(i),hi.hand())) )
      {
	tr+=hi.hand().card(i);
	if (tr.winnerplayer().no()!=hi.no()
	    && hi.hand().card(i).value()==Card::TEN
	    && !(tr.winnercard().less(hi.value(Aiconfig::LIMITHIGH),
				      hi.game()))
	    && !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
	// DK: 'soloPlayer &&' hinzugefuegt: ansonsten gibt es einen assert bei 'hi.game().soloplayer()'
	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),
					hi.game())) || 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.game().type() != GAMETYPE::SOLO_KING 
	    && hi.game().type() != GAMETYPE::SOLO_KING_QUEEN
	    && hi.game().type() != GAMETYPE::SOLO_KING_JACK)
	  c=hi.hand().card(i);     
      }           
    }

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

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


  }

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

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

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

  if (fox_exists)
  {

    // if lastcard just take the best winning card
    if (t.islastcard() && hi.teamofplayer(t.winnerplayer())!=hi.team())
    {

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

    { // if I can jab the fox surely, take the card
      // take the best cards of all players behind me that are not in my team
      Trick const& trick = t;
      Card highest_card_behind;
      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()) {
	  Card const highest_card = player->hand().highest_card(hi.game());
	  if (!highest_card_behind
	      || highest_card_behind.less(highest_card, hi.game()))
	    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, hi.game())
	   )
	  DEBUG_RETURNING(Card(),
			  INFO_HEURISTICS&&INFO_VALUE,
			  "Heuristics::jab_fox = false");

	// look whether I have the best card
	Card lowest_better_card;
	vector<Card> const valid_cards = hi.hand().validcards(t).cards();
	for (vector<Card>::const_iterator c = valid_cards.begin();
	     c != valid_cards.end();
	     ++c) {
	  if (!c->less(highest_card_behind, hi.game())
	      && (!lowest_better_card
		  || lowest_better_card.less(*c, hi.game())))
	    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
    Card ca=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");
}

/**********************************************************************
 *
 **    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();

  Hand 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())
	(numberofcards[n])+=hi.playedcard(Card(Card::InttoColor(n),
					       Card::InttoValue(i)));


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

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

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


  Card c=Card();

  // if this trick is still worth fehl creation
  if (hi.game().trick_current_no() < hi.value(Aiconfig::LASTFEHLCREATION))
  {

    // if this is the firstcard 
    if( hi.value(Aiconfig::FEHLCREATIONONFIRSTCARD) ) 
      if (t.actcardno() == 0)
	DEBUG_RETURNING(choose_best_fehl(t,hi),INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::create_fehl()");  

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  }  

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

  for ( i = 0; i< hi.hand().cardsnumber(); i++)
  {
    if (hi.hand().card(i).isdolle() || hi.hand().card(i).isswine() ||
	hi.hand().card(i).ishyperswine()) value+=v_highest;

    if (hi.hand().card(i).value()==Card::QUEEN) value+=v_queen;

    if (hi.hand().card(i).value()==Card::JACK) value+=v_jack; 

    if (hi.hand().card(i).value()==Card::KING) value+=v_king;

    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.hand().existstcolor(Card::InttoColor(i))
	    && ::in_running_game()
	    && (hi.color_runs( Card::InttoColor(i) ) == 0) 
	    && hi.game().trick_current().actcardno()>0
	    && (hi.game().trick_current().startcard().color()
		!= Card::InttoColor(i) )
       )
      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
  {
    for ( i=0; i < ::party.rule()(Rule::NUMBER_OF_CARD_COLORS);i++)
    {
      if ( hi.hand().existstcolor(Card::InttoColor(i)) && !Card(Card::InttoColor(i),Card::ACE).istrump() )
      {
	if ( hi.hand().numberof(Card::InttoColor(i),Card::ACE) == 0 )
	  value-=v_fehl;
	if( hi.hand().numberof(Card::InttoColor(i) )+hi.color_runs(Card::InttoColor(i) ) > 3 )
	  value-=v_fehl;
	if( hi.hand().numberof(Card::InttoColor(i))+hi.color_runs(Card::InttoColor(i) )
	    + ((::in_running_game() && hi.game().trick_current().actcardno()>0
		&& hi.game().trick_current().startcard().color()
		== Card::InttoColor(i))? 1 : 0)  >= 3 && 
	    hi.hand().numberof(Card::InttoColor(i),Card::ACE) < 2 )
	  value -= v_ace;
      }
    }
  }


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


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


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



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

/**********************************************************************
 *
 **    bool Heuristics::make_announcement(HeursticInterface const& hi)
 *
 **    Parameters:  HeuristicInterface who checks for an announcement
 *
 **    Result: true if hi should say re or contra
 **            false otherwise
 *
 **    Version: Beta
 *
 **    Description: 
 *
 **********************************************************************/

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

  value=CalcHandValue(hi,g);


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


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

  value=CalcHandValue(hi,g);

  int own_p = 0;
  int opp_p = 0;

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

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

  if( hi.game().swines_owner() != NULL && 
      hi.game().swines_owner()->no() == hi.no())
  {
    own_p += (int)(1.4*hi.hand().numberoftrumps() * hi.hand().numberoftrumpaces());
  }
  if( hi.game().hyperswines_owner() != NULL && 
      hi.game().hyperswines_owner()->no() == hi.no())
  {
    own_p += (int)(1.4*hi.hand().numberoftrumps() * hi.hand().numberoftrumpnines());
  }

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



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

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

  value=CalcHandValue(hi,g);

  int own_p = 0;
  int opp_p = 0;

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

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

  if( hi.game().swines_owner() != NULL && 
      hi.game().swines_owner()->no() == hi.no())
  {
    own_p += 3 * hi.hand().numberoftrumps() * hi.hand().numberoftrumpaces();
  }
  if( hi.game().hyperswines_owner() != NULL && 
      hi.game().hyperswines_owner()->no() == hi.no())
  {
    own_p += 3 * hi.hand().numberoftrumps() * hi.hand().numberoftrumpnines();
  }

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


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

  value=CalcHandValue(hi,g);

  int own_p = 0;
  int opp_p = 0;

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

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


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


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


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

  value=CalcHandValue(hi,g);

  bool has_trick = false;

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

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


}

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

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

  Card c=Card();
  Hand h=hi.hand();
  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), hi.game()))
      c=h.card(i);
  }

  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 ) < 2 && c.less( h, hi.game() ) )
      c = Card();
  }	


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


/**********************************************************************
 *
 **    Card drawTrumps(Trick const& t,AI& hi )
 *
 **    Parameters:
 *
 **    Result: play trumps to draw the last trumps
 *
 **    Version: 0.6.1
 *
 **    Description:
 *
 **********************************************************************/

Card Heuristics::draw_trump(Trick const& t, HeuristicInterface const& hi )
{
  Card c = Card();
  if( !t.isstartcard() )
    return c;

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

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

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

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

/**********************************************************************
 *
 **    Announcement make_reply(HeuristicInterface const& hi ,const Game& g)
 *
 **    Parameters:
 *
 **    Result: Announcment::Reply if wanted
 *
 **    Version: 0.6.3
 *
 **    Description:
 *
 **********************************************************************/
Announcement Heuristics::make_reply(HeuristicInterface const& hi,const Game& g)
{
  DEBUG_CALLING(INFO_HEURISTICS && INFO_OTHER_FUNCTION,"Heuristics::make_reply");
  if( g.announcement_of_team( opposite( hi.team() ) ).announcement != ANNOUNCEMENT::NOANNOUNCEMENT )
  {
    int value=0;

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

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

} 

