/********************************************************************** * * 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 "game.h" #include "gameplay_actions.h" #include "../party/party.h" #include "../party/rule.h" #include "../player/playersDb.h" #include "../player/ai/ai.h" #include "../card/trick.h" #include "../ui/ui.h" #include "../misc/setting.h" #include "../os/bug_report_replay.h" extern OS_NS::BugReportReplay* bug_report_replay; /** ** ** plays a game (get reservation, make the tricks, give the result) ** ** @param - ** ** @return - ** ** @version 0.6.7 ** ** @author Borg Enders ** @author Diether Knof ** */ void Game::play() { using namespace GAMESTATUS; if (::game_status != GAME_NEW) return ; for (vector::const_iterator p = this->players().begin(); p != this->players().end(); ++p) (*p)->game_open(*this); if (!this->isvirtual()) ::ui->game_open(*this); redistribute: // if the cards have to be redistributed if (::game_status != GAME_NEW) { if (!this->isvirtual()) { ::ui->game_close(); } return ; } ::game_status = GAME_INIT; this->init(); if (SEED_OUT) return ; if ( (::game_status != GAME_RESERVATION) && (::game_status != GAME_REDISTRIBUTE)) return ; if (::game_status == GAME_RESERVATION) ::game_status = GAME_START; // set the startplayer if (this->rule()(Rule::LUSTSOLO_PLAYER_LEADS) && GAMETYPE::is_solo(this->type())) this->player_current_ = &(this->soloplayer()); else this->player_current_ = const_cast(&(this->startplayer())); if (!this->isvirtual()) ::ui->game_start(); for (vector::iterator player = this->players().begin(); player != this->players().end(); player++) (*player)->game_start(); if (::game_status == GAME_REDISTRIBUTE) { delete this->poverty_cards_; this->poverty_cards_ = NULL; this->poverty_shifted_ = false; this->set_seed(this->party().seed_next()); ::game_status = GAMESTATUS::GAME_NEW; this->set_type(GAMETYPE::NORMAL); if (!this->isvirtual()) ::ui->game_redistribute(); goto redistribute; } // if (::game_status == GAME_REDISTRIBUTE) // testing swines this->test_swines_from_reservations(); if (::game_status != GAME_START) return ; ::game_status = GAME_PLAY; // the reserve is needed because the players keep pointers to the tricks // in their trickpiles for (unsigned t = 0; t < this->trickno(); t++) { try { this->tricks_.push_back(new Trick(this->player_current())); for (vector::iterator player = this->players().begin(); player != this->players().end(); player++) (*player)->trick_open(this->trick_current()); if (!this->isvirtual()) { ::ui->trick_open(this->trick_current()); } // request announcement of the players this->announcements_request(); while(!this->trick_current().isfull()) { this->nextplayer(); if (::game_status != GAMESTATUS::GAME_PLAY) throw(::game_status); } // while(!this->trick_current().isfull()) ::game_status = GAMESTATUS::GAME_FULL_TRICK; this->evaluatetrick(); ::game_status = GAME_PLAY; } catch (...) { if (!this->isvirtual()) { ::ui->trick_close(); } throw; } // try if (!this->isvirtual()) { ::ui->trick_close(); } } // for (t < this->trickno()) return ; } // void Game::play() /** ** ** initializes a new game -- get reservations and set the game type ** ** @param - ** ** @return - ** ** @version 0.4.4 ** ** @author Borg Enders ** @author Diether Knof ** */ void Game::init() { using namespace GAMESTATUS; // reset some variables this->soloplayer_ = NULL; this->reservation_ = vector(this->playerno()); this->marriage_selector_ = MARRIAGE_SELECTOR::TEAM_SET; this->marriage_determination_trickno_ = 0; this->poverty_cards_ = NULL; this->poverty_cardno_ = 0; this->poverty_shifted_ = false; this->announcement_ = vector >(this->playerno(), vector(1)); #ifdef USE_NETWORK this->announcement_over_network_ = vector(this->playerno(), ANNOUNCEMENT::NOANNOUNCEMENT); #endif this->swines_owner_ = NULL; this->first_fox_catcher_ = NULL; this->swines_announced_ = false; this->hyperswines_owner_ = NULL; this->hyperswines_announced_ = false; this->finished_ = false; this->player_current_ = const_cast(&(this->startplayer())); this->set_type(GAMETYPE::NORMAL); // initialise the players for (vector::iterator player = this->players().begin(); player != this->players().end(); player++) { (*player)->new_game(*this); } // distribute the cards this->distributecards(); if (game_status != GAME_INIT) return ; ::game_status = GAME_RESERVATION; ::ui->game_cards_distributed(); // get the reservation for (unsigned i = 0; i < this->playerno(); i++, this->player_current_ = &(this->player_following(this->player_current()))) { ::ui->reservation_ask(this->player_current()); this->reservation_[this->player_current().no()] = this->player_current().reservation_get(); ::ui->gameplay_action(GameplayAction::Reservation(this->player_current().no(), this->reservation_[this->player_current().no()])); ::ui->reservation_got(this->reservation_[this->player_current().no()], this->player_current()); if (game_status != GAME_RESERVATION) return ; } // for (this->player_current_) if (SEED_OUT) { this->print_seed_statistics(); return ; } // testing a solo game for (unsigned i = 0; i < this->playerno(); i++, this->player_current_ = &(this->player_following(this->player_current()))) { if (GAMETYPE::is_solo(this->reservation(this->player_current()).game_type) && this->rule()(this->reservation(this->player_current()).game_type)) { // the player 'p' is playing a solo this->set_type(this->reservation(this->player_current()).game_type); this->soloplayer_ = &this->player_current(); // no other player can play a solo break; } } if (this->type() == GAMETYPE::NORMAL) { // testing fox highest trump if (this->rule()(Rule::THROW_WHEN_FOX_HIGHEST_TRUMP)) { for (unsigned i = 0; i < this->playerno(); i++, this->player_current_ = &(this->player_following(this->player_current()))) { if ( (this->reservation(this->player_current()).game_type == GAMETYPE::FOX_HIGHEST_TRUMP)) { // look, whether there is a card that is higher than the fox unsigned c; for (c = 0; c < this->player_current().hand().cardsnumber(); c++) // check with diamond ten because of swines! if ( Card(Card::DIAMOND, Card::TEN).less(this->player_current().hand().card(c)) && !this->player_current().hand().card(c).istrumpace()) break; if (c == this->player_current().hand().cardsnumber()) { this->set_type(GAMETYPE::FOX_HIGHEST_TRUMP); ::game_status = GAMESTATUS::GAME_REDISTRIBUTE; this->soloplayer_ = &this->player_current(); break; } // if (c < this->player(p).hand().cardsnumber()) } // if (reservation) } // for (p < playerno()) } // if (rule()(Rule::THROW_WHEN_FOX_HIGHEST_TRUMP)) // testing thrown nines if (this->rule()(Rule::THROW_WITH_NINES)) { for (unsigned i = 0; i < this->playerno(); i++, this->player_current_ = &(this->player_following(this->player_current()))) { if ( ((this->reservation(this->player_current()).game_type == GAMETYPE::THROWN_NINES) ) && (this->player_current().hand().numberofnines() >= this->rule()(Rule::MIN_NUMBER_OF_THROWING_NINES))) { this->set_type(GAMETYPE::THROWN_NINES); ::game_status = GAMESTATUS::GAME_REDISTRIBUTE; this->soloplayer_ = &this->player_current(); break; } // if (reservation) } // for (p < playerno()) } // if (rule()(Rule::THROW_WITH_NINES)) // testing thrown kings if (this->rule()(Rule::THROW_WITH_KINGS)) { for (unsigned i = 0; i < this->playerno(); i++, this->player_current_ = &(this->player_following(this->player_current()))) { if ( ((this->reservation(this->player_current()).game_type == GAMETYPE::THROWN_KINGS) ) && (this->player_current().hand().numberofkings() >= this->rule()(Rule::MIN_NUMBER_OF_THROWING_KINGS))) { this->set_type(GAMETYPE::THROWN_KINGS); ::game_status = GAMESTATUS::GAME_REDISTRIBUTE; this->soloplayer_ = &this->player_current(); break; } // if (reservation) } // for (p < playerno()) } // if (rule()(Rule::THROW_WITH_KINGS)) // testing thrown nines and kings if (this->rule()(Rule::THROW_WITH_NINES_AND_KINGS)) { for (unsigned i = 0; i < this->playerno(); i++, this->player_current_ = &(this->player_following(this->player_current()))) { if ( ((this->reservation(this->player_current()).game_type == GAMETYPE::THROWN_NINES_AND_KINGS) ) && (this->player_current().hand().numberofnines() + this->player_current().hand().numberofkings() >= this->rule()(Rule::MIN_NUMBER_OF_THROWING_NINES_AND_KINGS))) { this->set_type(GAMETYPE::THROWN_NINES_AND_KINGS); ::game_status = GAMESTATUS::GAME_REDISTRIBUTE; this->soloplayer_ = &this->player_current(); break; } // if (reservation) } // for (p < playerno()) } // if (rule()(Rule::THROW_WITH_NINES_AND_KINGS)) } // if (this->type() == GAMETYPE::NORMAL) if (this->type() == GAMETYPE::NORMAL) { // testing a poverty if (this->rule()(Rule::POVERTY)) { for (unsigned i = 0; i < this->playerno(); i++, this->player_current_ = &(this->player_following(this->player_current()))) { if ( ( (this->reservation(this->player_current()).game_type == GAMETYPE::POVERTY) ) && this->player_current().hand().has_poverty() ) { this->poverty_cardno_ = 0; // just make sure, that the value is valid this->soloplayer_ = &this->player_current(); this->set_type(GAMETYPE::POVERTY); this->test_swines_from_reservations(); this->poverty_shift(); if (::game_status != GAMESTATUS::GAME_RESERVATION) return ; break; } // if (reservation) } // for (p < playerno()) } // if (rule()(Rule::POVERTY)) } // if (type() == GAMETYPE::NORMAL) if (this->type() == GAMETYPE::NORMAL) { // testing a marriage for (unsigned i = 0; i < this->playerno(); i++, this->player_current_ = &(this->player_following(this->player_current()))) { if ( ((this->reservation(this->player_current()).game_type == GAMETYPE::MARRIAGE) ) && (this->player_current().hand().numberofclubqueens() == 2)) { this->set_type(GAMETYPE::MARRIAGE); this->soloplayer_ = &this->player_current(); this->marriage_selector_ = this->reservation(this->player_current()).marriage_selector; DEBUG_ASSERTION(this->rule()(this->marriage_selector()), "Game::init():\n" " marriage selector '" << this->marriage_selector_ << "' is not valid.\n"); } } // for (p < playerno()) } // if (type() == GAMETYPE::NORMAL) this->check_for_silent_marriage(); this->players_hands_sort(); // update the team info in 'game' and in the players this->teaminfo_set_at_gamestart(); return ; } // void Game::init() /** ** -> result ** ** @param player the player ** ** @return whether the player has made a reservation ** ** @author Diether Knof ** ** @version 0.7.4 **/ bool Game::has_made_reservation(Player const& player) const { if (::game_status < GAMESTATUS::GAME_RESERVATION) return false; if (::game_status > GAMESTATUS::GAME_RESERVATION) return true; return ( (player.no() + this->playerno() - this->startplayer().no() ) % this->playerno() < (this->player_current().no() + this->playerno() - this->startplayer().no() ) % this->playerno() ); } // bool Game::has_made_reservation(Player player) const /** ** -> result ** ** @param player the player ** ** @return the reservation of 'player' ** ** @author Diether Knof ** ** @version 0.7.4 **/ Reservation const& Game::reservation(Player const& player) const { return this->reservation_[player.no()]; } // Reservation const& Game::reservation(Player const& player) const /** ** ** make the shifting of the poverty cards ** distribute the cards randomly at the players ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.4.5 ** **/ void Game::poverty_shift() { if ((this->type() != GAMETYPE::POVERTY) || !this->rule()(Rule::POVERTY_SHIFT) || ((this->soloplayer().hand().numberoftrumps() <= 1) && this->rule()(Rule::THROW_WITH_ONE_TRUMP) ) ) { this->player_current_ = &(this->soloplayer()); ::game_status = GAMESTATUS::GAME_REDISTRIBUTE; return ; } // if (not poverty shift) ::game_status = GAMESTATUS::GAME_POVERTY_SHIFT; { // test for swines and hyperswines if (this->rule()(Rule::SWINES) && this->rule()(Rule::SWINES_ANNOUNCEMENT_BEGIN) && this->reservation(this->soloplayer()).swines) this->swines_announce(this->soloplayer()); if (this->rule()(Rule::HYPERSWINES) && this->rule()(Rule::HYPERSWINES_ANNOUNCEMENT_BEGIN) && this->reservation(this->soloplayer()).hyperswines) this->hyperswines_announce(this->soloplayer()); } // test for swines and hyperswines if (!this->isvirtual()) ::ui->poverty_shifting(this->soloplayer()); this->poverty_cardno_ = this->soloplayer().hand().numberofpovertycards(); HandCards cards_to_shift = this->soloplayer().poverty_shift(); this->poverty_cards_ = &cards_to_shift; DEBUG_ASSERTION((this->poverty_cards()->player() == this->soloplayer()), "Game::poverty_shift():\n" " the soloplayer '" << this->soloplayer().no() << "' shifts cards from player '" << this->poverty_cards_->player().no() << "'"); DEBUG_ASSERTION((this->soloplayer().hand().numberoftrumps() == 0), "Game::poverty_shift():\n" " Poverty: the soloplayer still has trump on the hand.\n" "remaining trumps: " << this->soloplayer().hand().numberoftrumps() << '\n' << "Hand:\n" << this->soloplayer().hand() << "shifted cards:\n" << cards_to_shift); DEBUG_ASSERTION( (cards_to_shift.size() + this->soloplayer().hand().cardsnumber() == this->rule()(Rule::NUMBER_OF_TRICKS_IN_GAME)), "Game::poverty_shift():\n" " wrong number of cards shifted: " << cards_to_shift.size()); #ifdef DEBUG if (this->rule()(Rule::POVERTY_SHIFT_ONLY_TRUMP)) DEBUG_ASSERTION( (this->poverty_cards()->numberoftrumps() == this->poverty_cards()->cardsnumber()), "Game::poverty_shift():\n" " wrong number of cards shifted: " << "trumps = " << this->poverty_cards()->numberoftrumps() << " != " << cards_to_shift.size() << " = cards shifted"); else if (!this->rule()(Rule::POVERTY_FOX_DO_NOT_COUNT) || this->swines_announced() ) // normal case: shift at max 3 cards DEBUG_ASSERTION( (this->poverty_cards()->cardsnumber() == Rule::MAX_NUMBER_OF_POVERTY_TRUMPS), "Game::poverty_shift():\n" " wrong number of cards shifted: " << "poverty cards = " << Rule::MAX_NUMBER_OF_POVERTY_TRUMPS << " != " << cards_to_shift.size() << " = cards shifted"); else if (!this->rule()(Rule::POVERTY_FOX_SHIFT_EXTRA)) // at max 3 cards or number of trumps DEBUG_ASSERTION( ( (this->poverty_cards()->numberoftrumps() == this->poverty_cards()->cardsnumber()) || (this->poverty_cards()->cardsnumber() == Rule::MAX_NUMBER_OF_POVERTY_TRUMPS) ), "Game::poverty_shift():\n" " wrong number of cards shifted: " << "trumps or " << "poverty cards = " << Rule::MAX_NUMBER_OF_POVERTY_TRUMPS << " != " << cards_to_shift.size() << " = cards shifted"); else // fox are shifted extra DEBUG_ASSERTION( (this->poverty_cards()->cardsnumber() - this->poverty_cards()->numberoftrumpaces() == Rule::MAX_NUMBER_OF_POVERTY_TRUMPS), "Game::poverty_shift():\n" " wrong number of cards shifted: " << "poverty cards = " << Rule::MAX_NUMBER_OF_POVERTY_TRUMPS << " != " << cards_to_shift.size() << " - " << this->poverty_cards()->numberoftrumpaces() << " = cards shifted - fox"); #endif // #ifdef DEBUG #ifndef WORKAROUND DEBUG_ASSERTION((this->poverty_cardno() == cards_to_shift.size()), "Game::poverty_shift():\n" " wrong number of shifted cards:\n" " calculated = " << this->poverty_cardno() << " != " "shifted = " << cards_to_shift.size()); #else this->poverty_cardno_ = cards_to_shift.size(); #endif for (vector::iterator p = this->players().begin(); p != this->players().end(); p++) (*p)->poverty_shift(this->soloplayer(), this->poverty_cardno()); // this->soloplayer().hand().remove(cards_to_shift); if (!this->isvirtual()) { ::ui->poverty_shift(this->soloplayer(), this->poverty_cardno()); ::ui->gameplay_action(GameplayAction::PovertyShift(this->soloplayer().no(), cards_to_shift)); } // ask all players, whether they accept the poverty or not bool accept = false; for (this->player_current_ = &this->player_following(this->soloplayer()); this->player_current() != this->soloplayer(); this->player_current_ = &(this->player_following(this->player_current()))) { if (!this->isvirtual()) { ::ui->poverty_ask(this->player_current(), this->poverty_cardno()); if ( !(FAST_PLAY & FAST_NS::PAUSE) && !( ::bug_report_replay && ::bug_report_replay->auto_action())) { if (this->player_current_->type() != Player::HUMAN) ::ui->sleep(::setting(Setting::CARD_PLAY_DELAY)); } if (::game_status != GAMESTATUS::GAME_POVERTY_SHIFT) return ; } // if (!this->isvirtual()) accept = this->player_current().poverty_take_accept(this->poverty_cardno()); if (::game_status != GAMESTATUS::GAME_POVERTY_SHIFT) return ; if (accept) { { // test for swines and hyperswines if (this->rule()(Rule::SWINES) && this->rule()(Rule::SWINES_ANNOUNCEMENT_BEGIN) && this->reservation(this->player_current()).swines) this->swines_announce(this->player_current()); if (this->rule()(Rule::HYPERSWINES) && this->rule()(Rule::HYPERSWINES_ANNOUNCEMENT_BEGIN) && this->reservation(this->player_current()).hyperswines) this->hyperswines_announce(this->player_current()); } // test for swines and hyperswines ::ui->gameplay_action(GameplayAction::PovertyAccepted(this->player_current().no())); break; } else { // if !(accept) for (vector::iterator p = this->players().begin(); p != this->players().end(); p++) (*p)->poverty_take_denied(this->player_current()); if (!this->isvirtual()) { ::ui->poverty_take_denied(this->player_current()); ::ui->gameplay_action(GameplayAction::PovertyDenied(this->player_current().no())); } } // if !(accept) } // for (this->player_current_) if (!accept) { this->player_current_ = &(this->soloplayer()); for (vector::iterator p = this->players().begin(); p != this->players().end(); p++) (*p)->poverty_take_denied_by_all(); if (!this->isvirtual()) { ::ui->poverty_take_denied_by_all(); ::ui->gameplay_action(GameplayAction::PovertyDeniedByAll()); } this->soloplayer().sorted_hand().add(*this->poverty_cards_); this->soloplayer().hand_sort(); this->poverty_cards_ = new HandCards(*this->poverty_cards_); ::game_status = GAMESTATUS::GAME_REDISTRIBUTE; } else { // if !(!accept) // set the teaminfo this->set_teaminfo(this->soloplayer(), TEAM::RE); this->set_teaminfo(this->player_current(), TEAM::RE); this->soloplayer().set_team(TEAM::RE); this->player_current().set_team(TEAM::RE); for (vector::iterator p = this->players().begin(); p != this->players().end(); p++) { if (this->teaminfo(**p) == TEAM::UNKNOWN) { this->set_teaminfo(**p, TEAM::CONTRA); (*p)->set_team(TEAM::CONTRA); } } // for (p) this->teaminfo_set_at_gamestart(); HandCards cards_to_return = this->player_current().poverty_cards_change(cards_to_shift); if (::game_status != GAMESTATUS::GAME_POVERTY_SHIFT) return ; this->poverty_cards_ = &cards_to_return; DEBUG_ASSERTION((this->poverty_cards_->player() == this->player_current()), "Game::poverty_shift():\n" " the current player '" << this->player_current().no() << "' shifts back cards from player '" << this->poverty_cards_->player().no() << "'"); DEBUG_ASSERTION((this->poverty_cardno() == cards_to_return.size()), "Game::poverty_shift():\n" " number of cards returned (" << cards_to_return.size() << ") is not the same as are shifted (" << this->poverty_cardno() << ")."); for (vector::iterator p = this->players().begin(); p != this->players().end(); p++) (*p)->poverty_take_accepted(this->player_current(), cards_to_return.size(), cards_to_return.numberoftrumps()); if (!this->isvirtual()) { ::ui->poverty_take_accepted(this->player_current(), cards_to_return.size(), cards_to_return.numberoftrumps()); ::ui->gameplay_action(GameplayAction::PovertyReturned(this->player_current().no(), cards_to_return)); } // add the cards to the player this->player_current_ = &this->soloplayer(); this->soloplayer().poverty_cards_get_back(cards_to_return); // save the number of trumps this->poverty_cardno_ = Hand(this->soloplayer(), cards_to_return).numberoftrumps(); if (!this->isvirtual()) ::ui->gametype_changed(); // check, that the swines announcement is correct if (this->swines_announced()) { if (this->swines_owner()->team() == TEAM::RE) { this->swines_owner_ = NULL; this->swines_announced_ = false; for (vector::iterator p = this->players().begin(); p != this->players().end(); ++p) { if ((*p)->hand().numberoftrumpaces() == this->rule()(Rule::NUMBER_OF_SAME_CARDS)) { this->swines_announce(**p); break; } } // for (p \in this->players()) } // if (this->swines_owner()->team() == TEAM::RE) } // if (this->swines_announced()) if (::game_status != GAMESTATUS::GAME_POVERTY_SHIFT) return ; this->poverty_cards_ = NULL; ::game_status = GAMESTATUS::GAME_RESERVATION; } // if !(!accept) this->poverty_shifted_ = true; return ; } // void Game::poverty_shift() /** ** ** distribute the cards randomly at the players ** ** @param - ** ** @return - ** ** @version 0.4.4 ** ** @author Borg Enders ** @author Diether Knof ** */ void Game::distributecards() { ::pseudo_rand_set(this->seed()); if (!SEED_OUT) cout << "seed: " << this->seed() << "\n"; vector all_cards = this->rule().cards(); for (unsigned i = 1; i < this->rule()(Rule::NUMBER_OF_SAME_CARDS); ++i) all_cards.insert(all_cards.end(), this->rule().cards().begin(), this->rule().cards().end()); vector > hands(this->playerno()); while (!all_cards.empty()) { for (vector >::iterator h = hands.begin(); h != hands.end(); ++h) { DEBUG_ASSERTION(!all_cards.empty(), "Game::distributecards():\n" " valid cards cannot be distributed between the players"); vector::iterator c = (all_cards.begin() + RAND(all_cards.size())); h->push_back(*c); all_cards.erase(c); } // for (h \in hands) } // while (!cards.empty()) // create the hands for the players for (unsigned p = 0; p < this->playerno(); p++) { this->players_[(p + this->startplayer().no()) % this->playerno()] ->set_hand(hands[p]); } return ; } // void Game::distributecards() /** ** the next player has to play a card ** ** @param - ** ** @return - ** ** @version 0.4.4 ** ** @author Borg Enders ** @author Diether Knof ** ** @bug uses processor time, not real time */ void Game::nextplayer() { DEBUG_ASSERT(this->self_check()); #ifdef POSTPHONED DEBUG_ASSERTION((this->player_current().hand().cardsnumber() == this->trickno() - this->trick_current_no()), "Game::nextplayer():\n" " current player has not the right number of cards on the hand\n" "trick " << this->trick_current_no() << "\n" << this->player_current()); #endif // ***ToDo DK this uses the processor time const clock_t time_before_playing_card = ::clock(); HandCard const played_card = this->player_current().card_get(); if (!(::game_status & GAMESTATUS::GAME)) return ; DEBUG_ASSERTION(played_card, "Game::nextplayer():\n" " result of 'Player::card_get()' is an empty card.\n" " Player number: " << this->player_current().no() << "\n" " Player type: " << this->player_current().type()); // here always update, // so that the user can do something during the calculation of the ai ::ui->update(); // testing, whether the card is valid. // if it is not, take the first valid card of the hand. if (!this->trick_current().isvalid(played_card)) { #ifndef RELEASE cerr << "ai type = " << played_card.player().type() << endl; cerr << "heuristic = " << static_cast(this->player_current() ).lastHeuristic_ << endl; cerr << *this; #endif #ifndef POSTPHONED cerr << this->player_current().hand() << endl; cerr << static_cast(this->player_current()).lastHeuristic_ << endl; cerr << this->trick_current() << endl; DEBUG_ASSERTION(false, "Game::nextplayer()\n" " card \"" << played_card << "\" is not valid" << endl); #else // *** the opposite team has won #endif } if ( !(FAST_PLAY & FAST_NS::PAUSE) && !( ::bug_report_replay && ::bug_report_replay->auto_action())) { // wait a bit, before the card is played // if it is the human who has to play // Bug: DK // When the time wraps around, it is assumed, that the calculation // has taken no time if (this->player_current().type() & Player::AI) { if (!this->isvirtual()) { #ifdef POSTPHONED // ToDo: cannot use this code when using 'clock()' while (time_before_playing_card + (::setting(Setting::CARD_PLAY_DELAY) * static_cast(CLOCKS_PER_SEC) / 1000) > ::clock() ) { #endif ::ui->sleep(max(long(0), long(::setting(Setting::CARD_PLAY_DELAY) - ((::clock() - time_before_playing_card) / (static_cast(CLOCKS_PER_SEC) / 1000) )))); #ifdef POSTPHONED // ToDo: cannot use this code when using 'clock()' } // while (still have to wait) #endif } // if (!this->isvirtual()) } // if (this->player_current().type() & Player::AI) } // if (!(FAST_PLAY & FAST_NS::PAUSE)) this->check_for_genscher(played_card); // remove the card from the hand, add it to the trick // and give the message to the ui this->player_current().hand().playcard(played_card); this->trick_current() += played_card; // set the next player as current player if (this->trick_current().isfull()) this->player_current_ = const_cast(&(this->trick_current().winnerplayer())); else this->player_current_ = &this->player_following(this->player_current()); for (vector::iterator player = this->players().begin(); player != this->players().end(); player++) { (*player)->card_played(played_card); } this->determine_silent_marriage(); if (this->isvirtual()) ::ui->virtual_card_played(played_card); else { ::ui->card_played(played_card); this->teaminfo_update(); Aiconfig::Heuristic heuristic = Aiconfig::NO_HEURISTIC; switch (played_card.player().type()) { case Player::HUMAN: case Player::AI: heuristic = static_cast(played_card.player()).lastHeuristic_; break; case Player::UNSET: case Player::AI_DUMMY: case Player::AI_RANDOM: heuristic = Aiconfig::NO_HEURISTIC; break; case Player::NETWORK: heuristic = Aiconfig::NETWORK; break; } // switch (played_card.player().type()) ::ui->gameplay_action(GameplayAction::CardPlayed(played_card.player().no(), played_card, heuristic)); } // request announcement of the players this->announcements_request(); return ; } // void Game::nextplayer(); /** ** ** evaluates who is startplayer for nextcard and updates player ** ** @param - ** ** @return - ** ** @version 0.4.4 ** ** @author Borg Enders ** @author Diether Knof ** */ void Game::evaluatetrick() { // Winnerplayer has as result the number of the winner Player const& winnerplayer = this->trick_current().winnerplayer(); // test for marriage this->determine_marriage(); if (!(::game_status & GAMESTATUS::GAME)) return ; // test for catcher of the first fox of swines if (this->rule()(Rule::SWINE_ONLY_SECOND) && !this->first_fox_catcher()) { for (vector::const_iterator p = this->players_begin(); p != this->players_end(); ++p) { if (this->trick_current().card_of_player(**p).istrumpace()) { this->first_fox_catcher_ = &winnerplayer; (*p)->hand_sort(); break; } // if (is trump ace) } // for (p \in this->player) } // if (fox in trick) // test whether the player has to make an announcement if ( (this->type() == GAMETYPE::NORMAL) && (this->trick_current_no() == 1) && (this->rule()(Rule::ANNOUNCEMENT_FIRST_TRICK_THIRTY_POINTS)) && (this->trick_current().points() >= 30) && ( !this->rule()(Rule::ANNOUNCEMENT_FIRST_TRICK_THIRTY_POINTS_ONLY_FIRST) || ( (this->announcement_of_team(TEAM::RE) == ANNOUNCEMENT::NOANNOUNCEMENT) && (this->announcement_of_team(TEAM::CONTRA) == ANNOUNCEMENT::NOANNOUNCEMENT) ) ) ) { this->make_announcement(ANNOUNCEMENT::NO120, this->trick_current().winnerplayer()); } // if (player must make an announcement) // request announcement of the players this->announcements_request(); // tell the players and the ui, that the trick is full for (vector::const_iterator player = this->players_begin(); player != this->players_end(); player++) (*player)->trick_full(this->trick_current()); if (this->isvirtual()) { ::ui->virtual_trick_full(); } else { ::ui->gameplay_action(GameplayAction::TrickFull(this->trick_current())); ::ui->trick_full(); ::game_status = GAMESTATUS::GAME_TRICK_TAKEN; ::ui->gameplay_action(GameplayAction::TrickTaken()); // this is needed for the network for (vector::const_iterator player = this->players_begin(); player != this->players_end(); player++) (*player)->trick_taken(); } if (!(::game_status & GAMESTATUS::GAME)) return ; // move the trick in the trickpile this->trick_current().move_in_trickpile(); // request announcement of the players this->announcements_request(); // Startplayer for next round this->player_current_ = &this->player(winnerplayer.no()); return ; } // void Game::evaluatetrick(); /** ** the game is finished ** ** @param - ** ** @return - ** ** @author Borg Enders ** @author Diether Knof ** ** @version 0.4.4 **/ void Game::finish() { if ((this->type() == GAMETYPE::MARRIAGE) && (this->marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET)) { this->marriage_determination_trickno_ = this->trick_current_no(); this->set_type(GAMETYPE::MARRIAGE_SOLO); for (vector::const_iterator player = this->players_begin(); player != this->players_end(); player++) this->teaminfo_[(*player)->no()] = ((**player == this->soloplayer()) ? TEAM::RE : TEAM::CONTRA); this->marriage_selector_ = MARRIAGE_SELECTOR::TEAM_SET; // update the teaminfo of the players for (vector::iterator player = this->players().begin(); player != this->players().end(); player++) (*player)->marriage(this->soloplayer(), this->soloplayer()); } // if (undetermined marriage) this->finished_ = true; return ; } // void Game::finish() /** ** print seed information on stdout ** see SEED_OUT in ../constants.cpp ** ** @param - ** ** @return - ** ** @version 0.7.3 ** ** @author Diether Knof **/ void Game::print_seed_statistics() const { cout << "seed: " << seed() << "\n"; { // solo if (GAMETYPE::is_solo(this->type())) cout << " player " << this->soloplayer().no() << ": " << this->type() << '\n'; } // solo { // marriage for (unsigned p = 0; p < this->playerno(); ++p) if (this->player(p).hand().numberofclubqueens() == this->rule()(Rule::NUMBER_OF_SAME_CARDS)) cout << " player " << p << ": marriage\n"; } // marriage { // genscher for (unsigned p = 0; p < this->playerno(); ++p) if (this->player(p).hand().has_possible_genscher()) cout << " player " << p << ": genscher\n"; } // genscher { // thrown nines / kings / nines + kings for (unsigned p = 0; p < this->playerno(); ++p) { Hand const& hand = this->player(p).hand(); if (hand.numberof(Card::NINE) >= this->rule()(Rule::MIN_NUMBER_OF_THROWING_NINES)) cout << " player " << p << ": " << GAMETYPE::THROWN_NINES << " (" << hand.numberof(Card::NINE) << ")\n"; if (hand.numberof(Card::KING) >= this->rule()(Rule::MIN_NUMBER_OF_THROWING_KINGS)) cout << " player " << p << ": " << GAMETYPE::THROWN_KINGS << " (" << hand.numberof(Card::KING) << ")\n"; if (hand.numberof(Card::NINE) + hand.numberof(Card::KING) >= this->rule()(Rule::MIN_NUMBER_OF_THROWING_NINES_AND_KINGS)) cout << " player " << p << ": " << GAMETYPE::THROWN_NINES_AND_KINGS << " (" << hand.numberof(Card::NINE) << " + " << hand.numberof(Card::KING) << ")\n"; } // for (p < this->playerno()) } // thrown nines / kings / nines + kings { // fox highest trump for (unsigned p = 0; p < this->playerno(); ++p) { Hand const& hand = this->player(p).hand(); unsigned c; for (c = 0; c < hand.cardsnumber(); c++) if (Card(Card::DIAMOND, Card::ACE).less(hand.card(c))) break; if (c == hand.cardsnumber()) cout << " player " << p << ": " << GAMETYPE::FOX_HIGHEST_TRUMP << '\n'; } // for (p < this->playerno()) } // fox highest trump { // poverty for (unsigned p = 0; p < this->playerno(); p++) { Hand const& hand = this->player(p).hand(); if (hand.has_poverty()) { cout << " player " << p << ": " << GAMETYPE::POVERTY << " ("; if ( this->rule()(Rule::POVERTY_FOX_DO_NOT_COUNT) && (hand.numberoftrumpaces() > 0)) cout << (hand.numberoftrumps() - hand.numberoftrumpaces()) << " + " << hand.numberoftrumpaces(); else cout << hand.numberoftrumps(); cout << ")\n"; } } // for (p < this->playerno()) } // poverty { // swines bool swines = false; for (unsigned p = 0; p < this->playerno(); p++) if (this->player(p).hand().has_swines()) { cout << " player " << p << ": swines\n"; swines = true; } if (swines) for (unsigned p = 0; p < this->playerno(); p++) if (this->player(p).hand().has_hyperswines()) cout << " player " << p << ": hyperswines\n"; } // swines return ; } // void Game::print_seed_statistics() const