/********************************************************************** * * FreeDoko a Doppelkopf-Game * * Copyright (C) 2001-2007 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 "team_information.heuristics.h" #include "ai.h" #include "team_information.h" #include "cards_information.of_player.h" #include "../../card/trick.h" // For configuration values search for '*Value*' namespace TeamInformationHeuristic { /* Further ideas * * - if the last player has a known team and he pfunds, than the winnerplayer * could be in the same team * (this assumes, that the last player knows more then I do) * * - opposite of 'choose pfund for partner' * if a player has played his fox as startcard: * o teams unknown: the player is re * o teams known: the partner is the one with the highest card * */ // the value according to the team static int team_value(int const value, Team const& team); // how to determine the team information //#define TEAMINFO(player) ai.teaminfo(player) //#define TEAMINFO(player) ai.game().teaminfo(player) // big macro :-( // returns the team information of the player given by 'ai' // Since the team information is used in order to interpret the play of // the other players, the team of the ai itself cannot be assumed to be // known in general. So I take into account the team points the ai has // counted for itself! #define TEAMINFO(player) ( ( ((player).no() == ai.no()) \ && !::is_real(ai.game().teaminfo(ai)) ) \ ? ( (ai.team_information().team_value(ai) \ >= (ai.value(Aiconfig::TRUSTING) \ ? 10 : 20) ) \ ? TEAM::RE \ : ( (ai.team_information().team_value(ai) \ <= -(ai.value(Aiconfig::TRUSTING) \ ? 10 : 20) ) \ ? TEAM::CONTRA \ : TEAM::UNKNOWN ) \ ) \ : ai.teaminfo(player) ) // *Value* /** ** the value according to the team ** RE: +value ** CONTRA: -value ** else: 0 ** ** @param value value ** @param team team ** ** @return the value according to the team ** ** @author Diether Knof ** ** @version 0.7.1 ** ** @todo other function name **/ int team_value(int const value, Team const& team) { switch (team) { case TEAM::RE: return value; case TEAM::CONTRA: return -value; default: return 0; } } // static int team_value(int value, Team team); /** ** the startplayer in a trick ** if the startplayer plays a color which has already been played ** the team of the last player gets a value ** ** @param player the player to look at ** @param card the card played by the player ** @param trick the current trick ** @param ai the ai that analyses ** ** @return value according to this heuristic ** ** @author Diether Knof ** ** @version 0.7.1 ** ** @todo other function name **/ int startplayer_color(Player const& player, HandCard const& card, Trick const& trick, Ai const& ai) { /* Idea: * If a player starts with a color another player is known not to have, * it is likely the two players play in the same team. */ // startplayer if (trick.startplayerno() != player.no()) return 0; // color trick if (card.istrump()) return 0; if (ai.color_runs(card.color()) == 0) { // first color run and no ace: add some points for the second player if (card.value() != Card::ACE) return team_value(2, TEAMINFO(player.game().player_following(player))); // *Value* return 0; } else { // if !(first run) // * not first color run: add some points for the last player who has // jabbed the color so far for (Player const* p = &player.game().player_previous(player); p != &player; p = &player.game().player_previous(*p)) if (ai.cards_information().of_player(*p).does_not_have(card.color())) return team_value(3, TEAMINFO(*p)); // *Value* } // if !(first run) return 0; } // static int startplayer_color(...) /** ** the penultimate (second last) player has pfund ** either the player has pfund for the player behind ** or the winnerplayer so far ** ** @param player the player to look at ** @param card the card played by the player ** @param trick the current trick ** @param ai the ai that analyses ** ** @return value according to this heuristic ** ** @author Diether Knof ** ** @version 0.7.1 ** ** @todo improve (use hand information of the last player) **/ int penultimate_pfund(Player const& player, HandCard const& card, Trick const& trick, Ai const& ai) { /* Idea: * The second last player pfunds. * If the winnercard so far is low, the last player is in the same team. * If the winnercard so far is high, the winnerplayer is in the same team. */ // the player must have played the penultimate card if (trick.actcardno() != player.game().playerno() - 1) return 0; // the player must not be the winner if (trick.winnerplayerno() == player.no()) return 0; // the player must have pfund at least ten points if (card.points() < 10) return 0; // it must be a trump trick (but it need not be a trump pfund) // or it is a color trick but the player has not served the color if (!( trick.startcard().istrump() || (card.tcolor() != trick.startcard().tcolor()))) return 0; // if the winnercard is small assume that the last player jabs // else assume that the last player cannot jab // ToDo: check whether there are cards above the winner card // ToDo: check fox if (trick.winnercard().less(Card(Card::HEART, Card::QUEEN))) // *Value* return team_value(10, TEAMINFO(player.game().player_following(player))); // *Value* else return team_value(10, TEAMINFO(trick.winnerplayer())); // *Value* return 0; } // static int penultimate_pfund(...) /** ** the last player in a trick ** if the player jabs, the value is -winnerplayer ** if the player pfunds, the value is +winnerplayer ** ** @param player the player to look at ** @param card the card played by the player ** @param trick the current trick ** @param ai the ai that analyses ** ** @return value according to this heuristic ** ** @author Diether Knof ** ** @version 0.7.1 **/ int last_player(Player const& player, HandCard const& card, Trick const& trick, Ai const& ai) { /* Idea: * If the last player in a trick jabs the trick, he does play against * the winnerplayer so far, * else if he even makes a pfund, he plays with the winnerplayer. * Giving a card for a special point gives a much higher credit. */ // the player must have played the last card of the trick if (!trick.isfull()) return 0; // it must not be a color trick // or the player must not have played the same color if ( (trick.startcard().tcolor() != Card::TRUMP) && (card.tcolor() == trick.startcard().tcolor()) ) return 0; if (trick.winnerplayerno() == player.no()) { // the player has jabbed, so check who was the winnerplayer before HandCard const* winnercard = &trick.card(0); for (unsigned p = 1; p < trick.actcardno() - 1; ++p) if (winnercard->less(trick.card(p))) winnercard = &trick.card(p); // ToDo: check whether the player has jabbed in order to play // the ace of a color // the value depends on the heigh of the card if (winnercard->value() == Card::JACK) return team_value(-15, TEAMINFO(winnercard->player())); // *Value* if (winnercard->value() == Card::QUEEN) return team_value(-25, TEAMINFO(winnercard->player())); // *Value* if ( (*winnercard == Card::DOLLE) || winnercard->isswine() ) return team_value(-35, TEAMINFO(winnercard->player())); // *Value* } else { // if !(trick.winnerplayerno() == player.no()) // the player has not jabbed // check whether the player could jab the trick // or if the player has even made a pfund // or, better, added a card for a special point HandCard const& card = trick.card_of_player(player); // check whether the player has added a card for a special point // especially a fox is taken into account if (!trick.specialpoints().empty()) { int value = 0; Specialpointsvector const specialpoints = trick.specialpoints(); for (Specialpointsvector::const_iterator sp = specialpoints.begin(); sp != specialpoints.end(); ++sp) { if (sp->player_of_no == player.no()) value += 50 * sp->value(); // *Value* else if (sp->type == SPECIALPOINT::DOPPELKOPF) value += 50; // *Value* } return team_value(value, TEAMINFO(trick.winnerplayer())); } // if (!trick.specialpoints().empty()) // check whether the player has made a pfund if (card.points() >= 10) { return team_value(card.points() // *Value* + (card.isfox() ? 50 : 0) // *Value* + static_cast(trick.points()) / 4, // *Value* TEAMINFO(trick.winnerplayer())); } // if (trick.card(player).value() >= 10) // check whether the player could jab the trick Card const limit_card = Card(Card::HEART, Card::QUEEN); // *Value* if (trick.winnercard().less(limit_card)) return team_value(static_cast(trick.points()) / 3, // *Value* TEAMINFO(trick.winnerplayer())); } // if !(trick.winnerplayerno() == player.no()) // ToDo // * player jabs with card > club queen ==> contra return 0; } // static int last_player(...) /** ** the player does not have the color ** when an ace is the winnercard: jab -> opposite, no jab -> same ** perhaps only for the first color run ** ** @param player the player to look at ** @param card the card played by the player ** @param trick the current trick ** @param ai the ai that analyses ** ** @return value according to this heuristic ** ** @author Diether Knof ** ** @version 0.7.1 ** ** @todo improve (check color run, search ToDo in code) **/ int does_not_have_color(Player const& player, HandCard const& card, Trick const& trick, Ai const& ai) { /* Idea: * A player does not have a color and the winnercard of the color is * an ace. * If he jabs, he does not play with the winnerplayer * (different behaviour for Fox, trump ten because the player may * want to bring home the points) * If the playe does not jab, he plays with the winnerplayer. */ // not the first player if ( (trick.actcardno() != 1) // a color trick && (trick.startcard().tcolor() != Card::TRUMP) // the player does not have the color && (card.tcolor() != trick.startcard().tcolor()) ) { // search the winnercard but without the last player HandCard const* winnercard = &trick.card(0); for (unsigned c = 1; c < trick.actcardno() - 1; ++c) if (winnercard->less(trick.card(c))) winnercard = &trick.card(c); Team const winnerteam = TEAMINFO(winnercard->player()); // the winner card must be an ace if (winnercard->value() == Card::ACE) { if (card.isfox()) return team_value(-0, winnerteam); // *Value* else if ( card.istrump() && (card.value() == Card::TEN) && !card.isdolle()) return team_value(-1, winnerteam); // *Value* else if (card.istrump()) // ToDo // The player could have jabbed in order to play a color ace // so check whether all colors have already been run // and check in the next trick whether the player does play a color ace return team_value(-5, winnerteam); // *Value* else return team_value(trick.points() / 2 + card.value() + (trick.remainingcardno() * 2), // *Value* winnerteam); } // if (conditions met) } // if (conditions met) return 0; } // static int does_not_have_color(...) /** ** the player pfunds in a color trick (in the same color) ** ** @param player the player to look at ** @param card the card played by the player ** @param trick the current trick ** @param ai the ai that analyses ** ** @return value according to this heuristic ** ** @author Diether Knof ** ** @version 0.7.1 **/ int pfund_in_color_trick(Player const& player, HandCard const& card, Trick const& trick, Ai const& ai) { /* Idea: * In the first run of a color trick: * If the player pfunds (p.e. ten), he plays with the winnerplayer * (t.i. the one who has played the ace), * else (p.e. nine) he plays against the player. * * Note: In order to return something != 0 the team of the winnerplayer * must be known. So the normal case will be that a player has announced * 're' and played a color ace. */ // not the first player if (trick.actcardno() == 1) return 0; // a color trick if (trick.startcard().tcolor() == Card::TRUMP) return 0; // first run of the color trick if (ai.color_runs(trick.startcard().tcolor()) != 0) return 0; // the player does serve the color if (card.tcolor() != trick.startcard().tcolor()) return 0; // search the winnercard but without the last player HandCard const* winnercard = &trick.card(0); for (unsigned c = 1; c < trick.actcardno() - 1; ++c) if (winnercard->less(trick.card(c))) winnercard = &trick.card(c); Team const winnerteam = TEAMINFO(winnercard->player()); // the winner card must be an ace or a trump if ( (winnercard->value() != Card::ACE) && !winnercard->istrump() ) return 0; // ToDo: search whether a player behind does not have the color // there are negative values because a small card shows that the player // does not wish to pfund // ToDo: test with nines switch (card.value()) { case Card::ACE: return team_value(11, winnerteam); // *Value* case Card::TEN: return team_value( 7, winnerteam); // *Value* case Card::KING: return team_value( 0, winnerteam); // *Value* case Card::NINE: return team_value(-5, winnerteam); // *Value* case Card::NOCARDVALUE: case Card::JACK: case Card::QUEEN: // not possible since they are trump DEBUG_ASSERTION(false, "TeamInformationHeuristic::pfund_in_color_trick(...)\n" " card should be a color card (" << trick.startcard().tcolor() << ")" " but is '" << card << "'" ); return 0; } // switch (card.value()) return 0; } // static int pfund_in_color_trick(...) #undef TEAMINFO } // namespace TeamInformationHeuristic