/********************************************************************** * * 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 "constants.h" #include "cards_information.h" #include "cards_information.of_player.h" #include "cards_information.of_player.weighting.heuristics.h" #include "ai.h" #include "../../card/trick.h" #include "../../party/party.h" #include "../../party/rule.h" #ifdef CHECK_RUNTIME #include "../../runtime.h" #endif // whether to make self checks // local macro #ifndef DEBUG_NO #ifndef RELEASE #define SELF_CHECK #endif #endif /* * CardsInformation contains the information a player has of the distribution * of the cards in a game. The player does not necessarily be an active player, * The 'user' of this class has to call the methods corresponding to the * gameplay. * Information can be contained from the methods 'possible_hand(Player)', * 'estimated_hand(Player) and with 'of_player(Player)' specific information * about a player. * * According to the number of remembered tricks the first played cards are * forgotten (and with it that a player does not have the color -- * nearly just as if the first tricks did not exists at all. * In virtual games there is no additional trick lost when calculating one * trick further. * * The idea is to take all information from the gameplay to tell which cards * - are played * - a specific player must have * - a specific player cannot have * Merging the informations between the players (see CardsInformation::OfPlayer) * more data can be created (if par example three players do not have spade, * then the last one must have all remaining spade cards). * * The collected informations are * - the hand of the ai is set * the other players cannot have the cards * (in virtual games a bit more complicated, since the hand can contain more * cards than the ai has to play) * - a card is played * the card does not longer remain, it is marked as played by the player * - a player has not served a color * the player has no more card of the color * - announcement re (normal game) * the player has at least one club queen (can be already played) * - announcement contra (normal game) * the player has no club queen * - marriage * the player has both club queens * - poverty * the poverty player has as many trumps as he has gotten back * - announcement swines / hyperswines / genscher * the player has both trump aces / nines / kings * * When more information was collected the information about a player is * updated as good as I could program it (see 'OfPlayer'). * * 'CardsInformation' gets noted about changed cards ('queue_update()') * Then the 'of_player's are informed about the changes ('do_update()') till no * changed card remains (the 'of_player's can add cards again). * */ /* * Implemention remark * the class cannot be an 'OS' because then the functions are not called in * virtual games */ /** ** constructor ** ** @param player corresponding player ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ CardsInformation::CardsInformation(Ai const& player) : player_(&player), of_player_(), color_runs_(), played_(), in_recalcing_(false), possible_hands_(), estimated_hands_(), auto_update_(true) { this->reset(); } /** ** copy constructor ** ** @param cards_information object to be copied ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ CardsInformation::CardsInformation(CardsInformation const& cards_information) : player_(cards_information.player_), of_player_(cards_information.of_player_), color_runs_(cards_information.color_runs_), played_(cards_information.played_), in_recalcing_(cards_information.in_recalcing_), possible_hands_(cards_information.possible_hands_), estimated_hands_(cards_information.estimated_hands_), auto_update_(cards_information.auto_update_) { for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) p->set_cards_information(*this); #ifdef SELF_CHECK this->self_check(); #endif } // CardsInformation::CardsInformation(CardsInformation cards_information) /** ** copy operator ** ** @param cards_information object to be copied ** ** @return object ** ** @author Diether Knof ** ** @version 0.6.9 **/ CardsInformation& CardsInformation::operator=(CardsInformation const& cards_information) { DEBUG_ASSERTION((!this->in_recalcing() && !cards_information.in_recalcing()), "CardsInformation::operator=(CardInformation)\n" " 'this' or 'cards_information' is in recalcing"); this->player_ = cards_information.player_; this->of_player_ = cards_information.of_player_; this->color_runs_ = cards_information.color_runs_; for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) p->set_cards_information(*this); this->played_ = cards_information.played_; this->possible_hands_ = cards_information.possible_hands_; this->estimated_hands_ = cards_information.estimated_hands_; this->auto_update_ = cards_information.auto_update_; #ifdef SELF_CHECK this->self_check(); #endif return *this; } // CardsInformation& CardsInformation::operator=(CardsInformation cards_information) /** ** destructor ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ CardsInformation::~CardsInformation() { } /** ** writes 'cards_information' in 'ostr' ** ** @param ostr output stream ** @param cards_information object to write into 'ostr' ** ** @return the output stream ** ** @author Diether Knof ** ** @version 0.6.9 **/ ostream& operator<<(ostream& ostr, CardsInformation const& cards_information) { cards_information.write(ostr); return ostr; } // ostream& operator<<(ostream& ostr, CardsInformation cards_information) /** ** writes 'cards_information' in 'ostr' ** ** @param ostr output stream ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void CardsInformation::write(ostream& ostr) const { ostr << "cards information:\n" << "{\n"; ostr << "played:\n" << "{\n" << this->played_ << "}\n" << '\n'; for (vector::const_iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) ostr << *p << '\n'; ostr << "}\n"; return ; } // void CardsInformation::write(ostream& ostr) const /** ** resets all information ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.4 **/ bool CardsInformation::is_virtual() const { return ( this->game().isvirtual() || (this != &this->player().cards_information())); } // bool CardsInformation::is_virtual() const /** ** resets all information ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void CardsInformation::reset() { this->played_.clear(); this->of_player_.clear(); for (unsigned p = 0; p < this->game().playerno(); ++p) this->of_player_.push_back(OfPlayer(*this, p)); this->color_runs_ = vector(this->game().rule()(Rule::NUMBER_OF_CARD_TCOLORS), 0); this->possible_hands_.clear(); this->possible_hands_.resize(this->game().playerno()); this->estimated_hands_.clear(); this->estimated_hands_.resize(this->game().playerno()); return ; } // void CardsInformation::reset() /** ** recalcs all information ** takes 'remembertricksno' into account ** does remember all color runs and all 'important' cards ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void CardsInformation::recalc() { #ifdef CHECK_RUNTIME bool const stop_watch_was_running = runtime.ai_cards_information.running(); if (!stop_watch_was_running) ::runtime.ai_cards_information.start(); #endif this->in_recalcing_ = true; this->reset(); if (::game_status >= GAMESTATUS::GAME_PLAY) { // the first trick to take into account unsigned const trick_begin = ((::party.game().trick_current_no() <= this->player().value(Aiconfig::REMEMBERTRICKS)) ? 0 : (::party.game().trick_current_no() - this->player().value(Aiconfig::REMEMBERTRICKS))); { // remember own cards and important cards list important_cards; switch (this->game().type()) { case GAMETYPE::THROWN_NINES: case GAMETYPE::THROWN_KINGS: case GAMETYPE::THROWN_NINES_AND_KINGS: case GAMETYPE::FOX_HIGHEST_TRUMP: break; case GAMETYPE::NORMAL: case GAMETYPE::POVERTY: case GAMETYPE::GENSCHER: case GAMETYPE::MARRIAGE: case GAMETYPE::MARRIAGE_SOLO: // remember fox, club queens and special cards important_cards.push_back(Card::FOX); important_cards.push_back(Card::CLUB_QUEEN); case GAMETYPE::MARRIAGE_SILENT: case GAMETYPE::SOLO_CLUB: case GAMETYPE::SOLO_HEART: case GAMETYPE::SOLO_SPADE: case GAMETYPE::SOLO_DIAMOND: // remember special cards if (this->game().rule()(Rule::DOLLEN)) important_cards.push_back(this->game().dolle()); if (this->game().swines_announced()) important_cards.push_back(this->game().swine()); if (this->game().hyperswines_announced()) important_cards.push_back(this->game().hyperswine()); break; case GAMETYPE::SOLO_JACK: case GAMETYPE::SOLO_QUEEN: case GAMETYPE::SOLO_KING: case GAMETYPE::SOLO_QUEEN_JACK: case GAMETYPE::SOLO_KING_JACK: case GAMETYPE::SOLO_KING_QUEEN: case GAMETYPE::SOLO_KOEHLER: important_cards = this->game().trumps(); // remember trumps and aces case GAMETYPE::SOLO_MEATLESS: // remember aces for (vector::const_iterator c = this->game().rule().card_colors().begin(); c != this->game().rule().card_colors().end(); ++c) important_cards.push_back(Card(*c, Card::ACE)); break; } // switch (this->game().type()) for (unsigned t = 0; t < trick_begin; ++t) { Trick const& trick = this->game().trick(t); Trick trick2(trick.startplayer()); for (unsigned c = 0; c < trick.actcardno(); ++c) { HandCard const& card = trick.card(c); trick2 += card; if ( (c == trick.cardno_of_player(this->player())) || (std::find(important_cards.begin(), important_cards.end(), card) != important_cards.end()) ) this->card_played(card, trick2); } // for (c < trick.actcardno()) this->color_runs_[trick.startcard().tcolor()] += 1; } // for (t < trick_begin) } // remember own cards and important cards { // mark the played cards for (unsigned t = trick_begin; t < this->game().trick_current_no(); ++t) { Trick const& trick = this->game().trick(t); Trick trick2(trick.startplayer()); for (unsigned c = 0; c < trick.actcardno(); ++c) { HandCard const& card = trick.card(c); trick2 += card; this->card_played(card, trick2); } // for (c < trick.actcardno()) this->color_runs_[trick.startcard().tcolor()] += 1; } // for (t < this->game().trick_current_no()) if ( !this->game().trick_current().isfull() && !this->game().trick_current().isempty()) this->color_runs_[this->game().trick_current().startcard().tcolor()] -= 1; } // mark the played cards { // test for colors the players do not have for (unsigned t = trick_begin; t < this->game().trick_current_no(); ++t) { Card::TColor const tcolor = this->game().trick(t).startcard().tcolor(); Trick const& trick = this->game().trick(t); for (unsigned c = 1; c < trick.actcardno(); ++c) { if (trick.card(c).tcolor() != tcolor) this->of_player_[trick.card(c).player().no() ].add_does_not_have(tcolor); } // for (c < trick.actcardno()) } // for (t < trick_current_no) } // test for colors the players do not have { // add announcements info for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) { if (this->game().announcement_of_player(p->player())) this->announcement_made(this->game().announcement_of_player(p->player()), p->player()); } // for (p \in this->of_player_) } // add announcements info } // if (::game_status >= GAMESTATUS::GAME_PLAY) { // add marriage info if (GAMETYPE::is_marriage(this->game().type())) { for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) if (p->playerno() == this->game().soloplayer().no()) p->add_must_have(Card::CLUB_QUEEN, this->game().rule()(Rule::NUMBER_OF_SAME_CARDS) - this->played(Card::CLUB_QUEEN)); else p->add_cannot_have(Card::CLUB_QUEEN); } // if (is marriage) } // add marriage info { // add swines info if (this->game().swines_owner()) { for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) if (p->playerno() != this->game().swines_owner()->no()) p->add_cannot_have(this->game().swine()); if (this->game().hyperswines_owner()) { for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) if (p->playerno() != this->game().hyperswines_owner()->no()) p->add_cannot_have(this->game().hyperswine()); } // if (this->game().hyperswines_owner()) } // if (this->game().swines_owner()) } // add swines info { // add genscher info if (this->game().type() == GAMETYPE::GENSCHER) { Card const genscher_card(this->game().trumpcolor(), Card::KING); for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) if (p->playerno() != this->game().soloplayer().no()) p->add_cannot_have(genscher_card); } } // add genscher info if (this->player_) this->set_hand(this->player(), this->player().hand()); this->in_recalcing_ = false; // this must be called here because it is skipped being in recalcing this->update_remaining_cards(); this->queue_update_all(); if (this->auto_update()) this->do_update(); #ifdef CHECK_RUNTIME if (!stop_watch_was_running) ::runtime.ai_cards_information.stop(); #endif return ; } // void CardsInformation::recalc() /** ** -> result ** ** @param player the player whose information is asked for ** ** @return the information of 'player' ** ** @author Diether Knof ** ** @version 0.6.9 **/ CardsInformation::OfPlayer const& CardsInformation::of_player(Player const& player) const { return this->of_player(player.no()); } // CardsInformation::OfPlayer const& CardsInformation::of_player(Player player) const /** ** -> result ** ** @param playerno the player whose information is asked for ** ** @return the information of player 'playerno' ** ** @author Diether Knof ** ** @version 0.6.9 **/ CardsInformation::OfPlayer const& CardsInformation::of_player(unsigned const playerno) const { return this->of_player_[playerno]; } // CardsInformation::OfPlayer CardsInformation::of_player(unsigned playerno) const /** ** -> result ** ** @param tcolor trump color ** ** @return the number of runs of tcolor (without the current trick) ** ** @author Diether Knof ** ** @version 0.7.4 **/ unsigned CardsInformation::color_runs(Card::TColor const& tcolor) const { return this->color_runs_[tcolor]; } // CardsInformation::OfPlayer CardsInformation::color_runs(Card::TColor tcolor) const /** ** -> result ** ** @param player the player whose hand is asked for ** ** @return hand with all cards the player can have ** ** @author Diether Knof ** ** @version 0.6.9 **/ Hand const& CardsInformation::possible_hand(Player const& player) const { DEBUG_ASSERTION((&this->game() == &player.game()), "CardsInformation::possible_hand(player):\n" " the game of the player is not the game of the player"); return this->possible_hand(player.no()); } // Hand CardsInformation::possible_hand(Player player) const /** ** -> result ** ** @param playerno the number of the player ** ** @return hand with all cards the player can have ** ** @author Diether Knof ** ** @version 0.7.4 **/ Hand const& CardsInformation::possible_hand(unsigned const playerno) const { return this->possible_hands_[playerno]; } // Hand CardsInformation::possible_hand(unsigned playerno) const /** ** -> result ** ** @param player the player whose hand is asked for ** ** @return the estimated hand for the player ** ** @author Diether Knof ** ** @version 0.7.3 **/ Hand const& CardsInformation::estimated_hand(Player const& player) const { DEBUG_ASSERTION((&this->game() == &player.game()), "CardsInformation::estimated_hand(player):\n" " the game of the player is not the game of the player"); return this->estimated_hand(player.no()); } // Hand CardsInformation::estimated_hand(Player player) const /** ** -> result ** ** @param playerno the number of the player ** ** @return hand with all cards the player can have ** ** @author Diether Knof ** ** @version 0.7.4 **/ Hand const& CardsInformation::estimated_hand(unsigned const playerno) const { return this->estimated_hands_[playerno]; } // Hand CardsInformation::estimated_hand(unsigned playerno) const /** ** -> result ** ** @param - ** ** @return the highest remaining trump in the game ** ** @author Diether Knof ** ** @version 0.7.3 **/ Card CardsInformation::highest_remaining_trump() const { HandCard card; for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { // note: this->possible_hand() is a temporary hand, // so 'c' needs another hand HandCard const c = HandCard((*p)->hand(), this->possible_hand(**p).highest_card(Card::TRUMP)); if ( c && ( !card || card.less(c) ) ) card = c; } return card; } // Card CardsInformation::highest_remaining_trump() const /** ** -> result ** ** @param - ** ** @return the highest remaining trump of the other players in the game ** ** @author Diether Knof ** ** @version 0.7.3 **/ Card CardsInformation::highest_remaining_trump_of_others() const { Card card; for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { if (*p == &this->player()) continue; // note: this->possible_hand() is a temporary hand, // so 'c' needs another hand HandCard const c = HandCard((*p)->hand(), this->possible_hand(**p).highest_card(Card::TRUMP)); if ( c && ( !card || card.less(c) ) ) card = c; } return card; } // Card CardsInformation::highest_remaining_trump_of_others() const /** ** -> result ** ** @param card card to compare with ** ** @return whether there exists a higher card than 'card' on the other hands ** ** @author Diether Knof ** ** @version 0.7.3 **/ bool CardsInformation::higher_card_exists(HandCard const& card) const { for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { if (*p == &card.player()) continue; if (this->possible_hand(**p).higher_card_exists(card)) return true; } return false; } // bool CardsInformation::higher_card_exists(HandCard card) const /** ** -> result ** ** @param - ** ** @return the corresponding game ** ** @author Diether Knof ** ** @version 0.7.1 **/ Game const& CardsInformation::game() const { return this->player().game(); } /** ** sets (changes) the player ** ** @param player new player ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.1 **/ void CardsInformation::set_player(Ai const& player) { this->player_ = &player; return ; } // void CardsInformation::set_player(Ai player) /** ** sets the hand of the player ** ** @param player player whose hand is set ** @param hand the hand of the player ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void CardsInformation::set_hand(Player const& player, Hand const& hand) { #ifdef CHECK_RUNTIME bool const stop_watch_was_running = runtime.ai_cards_information.running(); if (!stop_watch_was_running) ::runtime.ai_cards_information.start(); #endif if (::game_status == GAMESTATUS::GAME_POVERTY_SHIFT) return ; this->of_player_[player.no()].add_can_only_have(hand.cards()); if (this->auto_update()) this->do_update(); #ifdef CHECK_RUNTIME if (!stop_watch_was_running) ::runtime.ai_cards_information.stop(); #endif return ; } // void CardsInformation::set_hand(Player player, Hand hand) /** ** updates the information of the card ** ** @param card card to update the information of ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void CardsInformation::queue_update(Card const& card) { this->cards_to_update.insert(card); return ; } // void CardsInformation::queue_update(Card card) /** ** do the update of all cards in 'cards_to_update' ** ** @param card card to update the information of ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void CardsInformation::do_update() { while (!this->cards_to_update.empty()) { while (!this->cards_to_update.empty()) { Card const card = *this->cards_to_update.begin(); this->cards_to_update.erase(this->cards_to_update.begin()); for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) p->update_information(card); } // while (!this->cards_to_update().empty()) for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) { p->check_can_is_must(); p->check_must_is_can(); } } // while (!this->cards_to_update().empty()) #ifdef SELF_CHECK this->self_check(); #endif this->recalc_hands(); return ; } // void CardsInformation::do_update() /** ** updates the information of all cards ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.0 **/ void CardsInformation::queue_update_all() { for (vector::const_iterator c = this->game().rule().cards().begin(); c != this->game().rule().cards().end(); ++c) this->queue_update(*c); return ; } // void CardsInformation::queue_update_all() /** ** updates 'can have' according to 'remaining cards' ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.1 **/ void CardsInformation::update_remaining_cards() { for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) p->update_remaining_cards(); return ; } // void CardsInformation::update_remaining_cards() /** ** recalcs the (possible and estimated) hands ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.4 **/ void CardsInformation::recalc_hands() { this->recalc_possible_hands(); this->recalc_estimated_hands(); return ; } // void CardsInformation::recalc_hands() /** ** updates the possible hands ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.4 **/ void CardsInformation::recalc_possible_hands() { for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) this->possible_hands_[p->playerno()] = p->possible_hand(); return ; } // void CardsInformation::recalc_possible_hands() /** ** updates the estimated hands ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.4 **/ void CardsInformation::recalc_estimated_hands() { // overview // 1) estimated for each player the hand, according to the weighting // 2) make sure, that all cards are distributed // 3) if a hand is full known, remove its cards from the other hands // (this uses recursion!) // 4) check, that the estimated hands do not differ if the hands are set // (this uses recursion till depth 1) // If no valid estimation is found, the possible hands are used. { // 1) estimated for each player the hand, according to the weighting for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) this->estimated_hands_[p->playerno()] = p->estimated_hand(); } // 1) estimated for each player the hand, according to the weighting { // 2) make sure, that all cards are distributed for (vector::const_iterator card = this->game().rule().cards().begin(); card != this->game().rule().cards().end(); ++card) { unsigned sum = this->played(*card); for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) sum += this->estimated_hand(**p).numberof(*card); while (sum < this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) { // maximal weighting int max_weighting = INT_MIN; // player with the maximum weighting Player* max_player = NULL; for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { if (this->estimated_hand(**p).numberof(*card) < this->of_player(**p).can_have(*card)) { if ( (this->of_player(**p).weighting(*card) > max_weighting) && (this->cardno_to_play(**p) > this->estimated_hand(**p).numberof(*card)) ) { max_weighting = this->of_player(**p).weighting(*card); max_player = *p; } } // if (player can have another card) } // for (p \in game.players) // This can happen when tricks are forgotten. // P.e. if only one player still has card and still has one card to play // he cannot have both forgotten club aces. if (!max_player) break; this->estimated_hands_[max_player->no()].add(*card); sum += 1; } // while (sum < this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) } // for (card \in rule.cards()) } // 2) make sure, that all cards are distributed try { { // 3) check for full estimated hands // if all cards of a player are estimated, // the other players cannot have the cards // the number of known distributed cards map known_cards; for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { Hand const& hand = this->estimated_hand(**p); if (hand.cardsnumber() == this->cardno_to_play(**p)) { for (HandCards::const_iterator c = hand.cards().begin(); c != hand.cards().end(); ++c) known_cards[*c] += 1; } // if (full hand estimated } // for (p \in this->game().players) // whether the estimated hands will change bool changed = false; // cards information which takes the full estimated hands into account CardsInformation cards_information(*this); cards_information.set_auto_update(false); // check, that a card is not in two full estimated hands for (map::const_iterator c = known_cards.begin(); c != known_cards.end(); ++c) { Card const& card = c->first; // number of known distributed cards of 'c' unsigned const n = (this->played(card) + c->second); if (n > this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) { changed = true; // decrease the weighting for the card for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) cards_information.of_player(**p).cards_weighting_[card] -= 1; } // if (n > this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) } // for (c \in known_cards) if (!changed) { // remove the known cards in the new cards information for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { Hand const& hand = this->estimated_hand(**p); if (hand.cardsnumber() != this->cardno_to_play(**p)) { for (map::const_iterator c = known_cards.begin(); c != known_cards.end(); ++c) { Card const& card = c->first; // number of known distributed cards of 'c' unsigned const n = (this->played(card) + c->second); cards_information.of_player_[(*p)->no()].add_can_have(card, this->game().rule()(Rule::NUMBER_OF_SAME_CARDS) - n + this->of_player(**p).must_have(card)); if (hand.numberof(card) + n > this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) changed = true; } // for (c \in known_cards) } // if (full hand not known) } // if (!changed) } // for (p \in this->game().players) if (changed) { // recursion // only take one recursion depth if (this != &this->player().cards_information()) DEBUG_THROW(InvalidGameException, InvalidGameException()); // use the estimated hands from the modified cards information cards_information.do_update(); for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { this->estimated_hands_[(*p)->no()] = cards_information.estimated_hand(**p); } // for (p \in this->game().players) return ; } // if (changed) } // 3) check for full estimated hands { // 4) check, that the estimated hands do not differ if the hands are set CardsInformation cards_information(*this); cards_information.set_auto_update(false); for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { cards_information.set_hand(**p, this->estimated_hand(**p)); } // estimate the hand (only in first recursion depth) /// @todo if the do_update throws an exception, normalize the weightings if (this == &this->player().cards_information()) cards_information.do_update(); // whether the estimated hands differ bool differ = false; for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { if (cards_information.of_player(**p).estimated_hand() != this->estimated_hand(**p)) { differ = true; break; } } if (differ) { // recursion // only take one recursion depth if (this != &this->player().cards_information()) DEBUG_THROW(InvalidGameException, InvalidGameException()); // use the estimated hands from the modified cards information cards_information.do_update(); for (vector::const_iterator p = this->game().players_begin(); p != this->game().players_end(); ++p) { this->estimated_hands_[(*p)->no()] = cards_information.estimated_hand(**p); } // for (p \in this->game().players) } // if (differ) } // 4) check, that the estimated hands do not differ if the hands are set } catch(InvalidGameException const& exception) { // could not create estimated hands -- do no estimation this->estimated_hands_ = this->possible_hands_; } // try return ; } // void CardsInformation::recalc_estimated_hands() /** ** -> result ** ** @param - ** ** @return the number of forgotten tricks ** ** @author Diether Knof ** ** @version 0.7.0 **/ unsigned CardsInformation::forgotten_tricks_no() const { if (this->game().trick_current_no() == UINT_MAX) return 0; return ( (this->player().value(Aiconfig::REMEMBERTRICKS) < ::party.game().real_trick_current_no()) ? (::party.game().real_trick_current_no() - this->player().value(Aiconfig::REMEMBERTRICKS)) : 0 ); } // unsigned CardsInformation::forgotten_tricks_no() const /** ** -> result ** ** @param - ** ** @return the number of forgotten cards ** ** @author Diether Knof ** ** @version 0.7.0 **/ unsigned CardsInformation::forgotten_cards_no() const { return (this->game().playerno() * this->forgotten_tricks_no()); } // unsigned CardsInformation::forgotten_cards_no() const /** ** -> result ** ** @param - ** ** @return how many cards have been played in total in the game ** ** @author Diether Knof ** ** @version 0.6.9 **/ unsigned CardsInformation::played_cards_no() const { return this->played_.cards_no(); } // unsigned CardsInformation::played_cards_no() const /** ** -> result ** ** @param card card to look at ** ** @return how many of 'card' have been played ** ** @author Diether Knof ** ** @version 0.6.9 **/ unsigned CardsInformation::played(Card const& card) const { return (this->played_)[card]; } // unsigned CardsInformation::played(Card card) const /** ** -> result ** ** @param tcolor tcolor to look at ** ** @return how many cards of 'tcolor' have been played ** ** @author Diether Knof ** ** @version 0.7.3 **/ unsigned CardsInformation::played(Card::TColor const& tcolor) const { if (tcolor == Card::TRUMP) { unsigned n = 0; for (vector::const_iterator c = this->game().rule().cards().begin(); c != this->game().rule().cards().end(); ++c) if (c->istrump(this->game())) n += this->played(*c); return n; } else { // if !(tcolor == Card::TRUMP) unsigned n = 0; for (vector::const_iterator v = this->game().rule().card_values().begin(); v != this->game().rule().card_values().end(); ++v) if (!Card(tcolor, *v).istrump(this->game())) n += this->played(Card(tcolor, *v)); return n; } // if !(tcolor == Card::TRUMP) } // unsigned CardsInformation::played(Card::TColor tcolor) const /** ** -> result ** ** @param player player ** ** @return how many cards the player still has to play ** ** @author Diether Knof ** ** @version 0.7.4 **/ unsigned CardsInformation::cardno_to_play(Player const& player) const { if (this->game().tricks().empty()) return this->game().trickno(); return (this->game().trickno() - this->game().real_trick_current_no() - (this->game().trick_current().has_played(player) ? 1 : 0)); } // unsigned CardsInformation::cardno_to_play(Player player) const /** ** -> result ** ** @param - ** ** @return how many cards are still to be played ** ** @author Diether Knof ** ** @version 0.7.4 **/ unsigned CardsInformation::remaining_cards_no() const { return (this->game().trickno() * this->game().playerno() - this->played_cards_no()); } // unsigned CardsInformation::remaining_cards_no() const /** ** -> result ** ** @param card card to check ** ** @return how many of 'card' are still to be played ** ** @author Diether Knof ** ** @version 0.7.4 **/ unsigned CardsInformation::remaining(Card const& card) const { return (this->game().rule()(Rule::NUMBER_OF_SAME_CARDS) - this->played(card)); } // unsigned CardsInformation::remaining(Card card) const /** ** -> result ** ** @param - ** ** @return how many trumps are still in the game ** ** @author Diether Knof ** ** @version 0.7.4 **/ unsigned CardsInformation::remaining_trumps() const { return this->remaining(Card::TRUMP); } // unsigned CardsInformation::remaining_trumps() const /** ** -> result ** ** @param tcolor tcolor to check ** ** @return how many cards of 'tcolor' are still to be played ** ** @author Diether Knof ** ** @version 0.7.4 **/ unsigned CardsInformation::remaining(Card::TColor const& tcolor) const { return ( (this->game().different_cards_no(tcolor) * this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) - this->played(tcolor)); } // unsigned CardsInformation::remaining(Card::TColor tcolor) const /** ** -> result ** ** @param - ** ** @return how many cards the other players do have ** ** @author Diether Knof ** ** @version 0.7.4 **/ unsigned CardsInformation::remaining_others_cards_no() const { if (this->cardno_to_play(this->player()) == this->player().hand().cardsnumber()) return (this->remaining_cards_no() - this->player().hand().cardsnumber()); else return this->remaining_cards_no(); } // unsigned CardsInformation::remaining_others_cards_no() const /** ** -> result ** ** @param - ** ** @return how many trumps the other players do still have ** ** @author Diether Knof ** ** @version 0.7.4 **/ unsigned CardsInformation::remaining_trumps_others() const { return remaining_others(Card::TRUMP); } // unsigned CardsInformation::remaining_trumps_others() const /** ** -> result ** ** @param card card to check ** ** @return how many of 'card' the other players do have ** ** @author Diether Knof ** ** @version 0.7.4 **/ unsigned CardsInformation::remaining_others(Card const& card) const { if (this->cardno_to_play(this->player()) == this->player().hand().cardsnumber()) return (this->remaining(card) - this->player().hand().numberof(card)); else return this->remaining(card); } // unsigned CardsInformation::remaining_others(Card card) const /** ** -> result ** ** @param tcolor tcolor to check ** ** @return how many cards of 'tcolor' the other players do have ** ** @author Diether Knof ** ** @version 0.7.4 **/ unsigned CardsInformation::remaining_others(Card::TColor const& tcolor) const { if (this->cardno_to_play(this->player()) == this->player().hand().cardsnumber()) return (this->remaining(tcolor) - this->player().hand().numberof(tcolor)); else return this->remaining(tcolor); } // unsigned CardsInformation::remaining_others(Card::TColor tcolor) const /** ** the game starts ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void CardsInformation::game_start() { #ifdef CHECK_RUNTIME bool const stop_watch_was_running = runtime.ai_cards_information.running(); if (!stop_watch_was_running) ::runtime.ai_cards_information.start(); #endif // reset (needed in poverty: swines owner can change) this->reset(); if (this->player_) this->set_hand(this->player(), this->player().hand()); // Take care of swines when the game is started, // else I get problems with shifted swines. if (this->game().swines_announced()) this->swines_announced(*this->game().swines_owner()); if (this->game().hyperswines_announced()) this->hyperswines_announced(*this->game().hyperswines_owner()); for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) p->game_start(); if (this->auto_update()) this->do_update(); #ifdef CHECK_RUNTIME if (!stop_watch_was_running) ::runtime.ai_cards_information.stop(); #endif return ; } // void CardsInformation::game_start() /** ** the trick is opened ** ** @param trick opened trick ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.1 **/ void CardsInformation::trick_open(Trick const& trick) { #ifdef CHECK_RUNTIME bool const stop_watch_was_running = runtime.ai_cards_information.running(); if (!stop_watch_was_running) ::runtime.ai_cards_information.start(); #endif // In a virtual game do *not* recalc! // The problem is, that additional information get lost // (t.i. the hand of the player that has started the virtual game), // so there is a discrepance between the hand of the player and the cards // he can have. if ( (this->game().trick_current_no() > this->player().value(Aiconfig::REMEMBERTRICKS)) && !this->game().isvirtual() ) this->recalc(); #ifdef CHECK_RUNTIME if (!stop_watch_was_running) ::runtime.ai_cards_information.stop(); #endif return ; } // void CardsInformation::trick_open(Trick const& trick) /** ** the trick is full ** update the color runs ** ** @param trick full trick ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.4 **/ void CardsInformation::trick_full(Trick const& trick) { this->color_runs_[trick.startcard().tcolor()] += 1; return ; } // void CardsInformation::trick_full(Trick trick) /** ** 'card' has been played - update the information ** ** @param card played card ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void CardsInformation::card_played(HandCard const& card) { this->card_played(card, card.game().trick_current()); return ; } // void CardsInformation::card_played(HandCard card) /** ** 'card' has been played - update the information ** ** @param card played card ** @param trick current trick ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.3 **/ void CardsInformation::card_played(HandCard const& card, Trick const& trick) { #ifdef CHECK_RUNTIME bool const stop_watch_was_running = runtime.ai_cards_information.running(); if (!stop_watch_was_running) ::runtime.ai_cards_information.start(); #endif this->played_.inc(card); DEBUG_ASSERTION(this->played(card) <= this->game().rule()(Rule::NUMBER_OF_SAME_CARDS), "CardsInformation::card_played(" << card << ")\n" " card is played more often than it is in the game"); this->of_player_[card.player().no()].card_played(card, trick); if (this->in_recalcing()) return ; if (card.tcolor() != this->game().trick_current().startcard().tcolor()) { this->of_player_[card.player().no()].add_does_not_have(this->game().trick_current().startcard().tcolor()); } // check whether the player has played as many trump as he has got back if ( (this->game().type() == GAMETYPE::POVERTY) && (card.player().no() == this->game().soloplayer().no()) && !this->of_player(this->game().soloplayer().no() ).does_not_have(Card::TRUMP) && card.istrump() && (this->of_player(this->game().soloplayer().no()).played(Card::TRUMP) == this->game().poverty_cardno())) { this->of_player_[this->game().soloplayer().no()].add_does_not_have(Card::TRUMP); } this->queue_update(card); if (this->auto_update()) this->do_update(); #ifdef CHECK_RUNTIME if (!stop_watch_was_running) ::runtime.ai_cards_information.stop(); #endif return ; } // void CardsInformation::card_played(HandCard card, Trick trick) /** ** an announcement has been made - update the information ** ** @param announcement made announcement ** @param player player who has made the announcement ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void CardsInformation::announcement_made(Announcement const& announcement, Player const& player) { #ifdef CHECK_RUNTIME bool const stop_watch_was_running = runtime.ai_cards_information.running(); if (!stop_watch_was_running) ::runtime.ai_cards_information.start(); #endif // in a virtual game the distribution of the remaining club queens does not // depend on the team, so we can get no more information if (this->game().isvirtual()) return ; if (this->game().type() != GAMETYPE::NORMAL) return ; if (this->game().teaminfo(player) == TEAM::RE) { if ( (this->of_player_[player.no()].played(Card::CLUB_QUEEN) == 0) && (this->forgotten_tricks_no() == 0) ) this->of_player_[player.no()].add_must_have(Card::CLUB_QUEEN); } else if (this->game().teaminfo(player) == TEAM::CONTRA) { this->of_player_[player.no()].add_cannot_have(Card::CLUB_QUEEN); } else { DEBUG_ASSERTION(false, "CardsInformation::announcement_made(" << announcement << ", " << player.no() << "):\n" " team of the player not known in the game: " << this->game().teaminfo(player)); } this->queue_update(Card::CLUB_QUEEN); if (this->auto_update()) this->do_update(); #ifdef CHECK_RUNTIME if (!stop_watch_was_running) ::runtime.ai_cards_information.stop(); #endif return ; } // void CardsInformation::announcement_made(Announcement announcement, Player player) /** ** a player has announced swines - update the information ** ** @param player player who has swines ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void CardsInformation::swines_announced(Player const& player) { #ifdef CHECK_RUNTIME bool const stop_watch_was_running = runtime.ai_cards_information.running(); if (!stop_watch_was_running) ::runtime.ai_cards_information.start(); #endif Card const swine(this->game().swine()); if (::game_status == GAMESTATUS::GAME_POVERTY_SHIFT) // this is needed in the case that the swines are shifted this->recalc(); if (this->game().rule()(Rule::SWINE_ONLY_SECOND)) this->of_player_[player.no()].add_must_have(swine, 1); else this->of_player_[player.no()].add_must_have(swine, 2); for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) if (p->playerno() != player.no()) this->of_player_[p->playerno()].add_cannot_have(swine); if (this->auto_update()) this->do_update(); #ifdef CHECK_RUNTIME if (!stop_watch_was_running) ::runtime.ai_cards_information.stop(); #endif return ; } // void CardsInformation::swines_announced(Player player) /** ** a player has announced hyperswines - update the information ** ** @param player player who has hyperswines ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void CardsInformation::hyperswines_announced(Player const& player) { // Wait till the game is started, else I get problems with shifted swines. if (::game_status <= GAMESTATUS::GAME_RESERVATION) return ; #ifdef CHECK_RUNTIME bool const stop_watch_was_running = runtime.ai_cards_information.running(); if (!stop_watch_was_running) ::runtime.ai_cards_information.start(); #endif Card const hyperswine(this->game().hyperswine()); this->of_player_[player.no()].add_must_have(hyperswine, 2); for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) if (p->playerno() != player.no()) this->of_player_[p->playerno()].add_cannot_have(hyperswine); if (this->auto_update()) this->do_update(); #ifdef CHECK_RUNTIME if (!stop_watch_was_running) ::runtime.ai_cards_information.stop(); #endif return ; } // void CardsInformation::hyperswines_announced(Player player) /** ** player 'genscher' has a genscher ** ** @param genscher player with the genscher ** @param partner selected partner ** ** @return - ** ** @author Diether Knof ** ** @version 0.6.9 **/ void CardsInformation::genscher(Player const& genscher, Player const& partner) { #ifdef CHECK_RUNTIME bool const stop_watch_was_running = runtime.ai_cards_information.running(); if (!stop_watch_was_running) ::runtime.ai_cards_information.start(); #endif Card const genscher_card(this->game().trumpcolor(), Card::KING); this->of_player_[genscher.no()].add_must_have(genscher_card, 2); for (vector::iterator p = this->of_player_.begin(); p != this->of_player_.end(); ++p) if (p->playerno() != genscher.no()) this->of_player_[p->playerno()].add_cannot_have(genscher_card); if (this->auto_update()) this->do_update(); #ifdef CHECK_RUNTIME if (!stop_watch_was_running) ::runtime.ai_cards_information.stop(); #endif return ; } // void CardsInformation::genscher(Player genscher, Player partner) /** ** self check ** when an error is found, an ASSERTION is created ** ** @param - ** ** @return whether the self-check was successful (no error) ** ** @author Diether Knof ** ** @version 0.6.9 **/ bool CardsInformation::self_check() const { if (this->in_recalcing()) return true; #ifdef WORKAROUND // We get problems with 'forgotten cards no' // because the last trick is closed but no further cards are forgotten. if (this->game().finished()) return true; #endif { // the number of played cards must not be // greater than the number of played cards in the game and // less than the number of played cards in the game // minus the number of forgotten cards if ( (this->played_.cards_no() > this->game().played_cards_no()) || (this->played_.cards_no() + this->forgotten_cards_no() < this->game().played_cards_no()) ) { DEBUG_ASSERTION(false, "CardsInformation::self_check():\n" " played cards no + forgotten cards no" << " = " << this->played_.cards_no() << " + " << this->forgotten_cards_no() << " != " << this->game().played_cards_no() << " = game.played_cards_no()\n" << " trick = " << this->game().real_trick_current_no()); return false; } } // the number of a card is played does not exceeds // the number of it is played in the game for (CardCounter::const_iterator p = this->played_.begin(); p != this->played_.end(); ++p) { { // every card is at max played as many times as it is in the game if (p->second > this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) { DEBUG_ASSERTION(false, "CardsInformation::self_check():\n" " card '" << p->first << "' has been played" << p->second << "times.\n" ); return false; } } // every card is at max played as many times as it is in the game { // the number of played in total is the sum of played over the players unsigned n = 0; for (vector::const_iterator f = this->of_player_.begin(); f != this->of_player_.end(); ++f) n += f->played(p->first); if (p->second != n) { DEBUG_ASSERTION(false, "CardsInformation::self_check():\n" " card '" << p->first << "' has been played" << p->second << "times, but the players say " << n << " times.\n" ); return false; } } // the number of played in total is the sum of played over the players } // for (p \in this->played_) { // every card is either played or a player can have it or it is forgotten vector const& cards = this->game().rule().cards(); for (vector::const_iterator c = cards.begin(); c != cards.end(); ++c) { unsigned n = this->played(*c); for (vector::const_iterator f = this->of_player_.begin(); f != this->of_player_.end(); ++f) n += f->can_have(*c); n += this->forgotten_cards_no(); #ifdef DEBUG_ASSERT if (n < this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)) { if ( (::game_status & GAMESTATUS::GAME) && (this->game().trick_current_no() != UINT_MAX)) { cerr << "current trick\n"; cerr << this->game().trick_current() << endl; cerr << *this << endl; } } #endif DEBUG_ASSERTION((n >= this->game().rule()(Rule::NUMBER_OF_SAME_CARDS)), "CardsInformation::self_check():\n" " card '" << *c << "' is only distributed " << n << " times, must be " << this->game().rule()(Rule::NUMBER_OF_SAME_CARDS) << " times.\n" << " played: " << this->played(*c) << ", sum can_have = " << (n - this->played(*c))); } // for (c \in cards) } // every card is either played or a player can have it // ToDo: check there are at most as many cards missing as are forgotten { // the information of the single players is valid for (vector::const_iterator f = this->of_player_.begin(); f != this->of_player_.end(); ++f) if (!f->self_check()) return false; } // the information of the single players is valid return true; } // bool CardsInformation::self_check() const /** ** -> result ** ** @param cards_information_a first object ** @param cards_information_b second object ** ** @return whether the two objects are equal (the player may differ) ** ** @author Diether Knof ** ** @version 0.6.9 **/ bool operator==(CardsInformation const& cards_information_a, CardsInformation const& cards_information_b) { return ( (cards_information_a.player().no() == cards_information_a.player().no()) && (cards_information_a.of_player_ == cards_information_b.of_player_) ); } // bool operator==(CardsInformation cards_information_a, CardsInformation cards_information_b) /** ** -> result ** ** @param cards_information_a first object ** @param cards_information_b second object ** ** @return whether the two objects are different ** ** @author Diether Knof ** ** @version 0.6.9 **/ bool operator!=(CardsInformation const& cards_information_a, CardsInformation const& cards_information_b) { return !(cards_information_a == cards_information_b); } // bool operator!=(CardsInformation cards_information_a, CardsInformation cards_information_b) #ifdef SELF_CHECK #undef SELF_CHECK #endif