/********************************************************************** * * 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/announcement.h" #include "../party/party.h" #include "../party/rule.h" #include "../card/trick.h" #include "../ui/ui.h" #ifdef USE_NETWORK #include "../network/server.h" #endif /** ** ** -> result ** ** @param team the team ** ** @return the announcement of the team 'team' ** ** @version 0.6.1 ** ** @author Diether Knof ** **/ Game::AnnouncementWithTrickno Game::announcement_of_team(Team const& team) const { DEBUG_ASSERTION(((team == TEAM::RE) || (team == TEAM::CONTRA) || ((this->type() == GAMETYPE::MARRIAGE) && (this->marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET))), "Game::announcement_of_team(team)\n" " invalid team = " << team); // In an undetermined marriage the contra team cannot have announced. // This case is treated here, because in this case there does not // exists a player of the team 'contra' so the assertion later on would fail. if ( (this->type() == GAMETYPE::MARRIAGE) && (this->marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET) && (team == TEAM::CONTRA) ) return AnnouncementWithTrickno(); // the player of the team, that has made the highest announcement Player const* player = NULL; // take the highest announcement for(vector::const_iterator p = this->players().begin(); p != this->players().end(); p++) { if ((*p)->team() == team) if (!player || (this->announcement_of_player(**p).announcement > this->announcement_of_player(*player).announcement)) player = *p; } // for (p) if (player == NULL) { DEBUG_ASSERTION(this->isvirtual(), "Game::announcement_of_team(team):\n" " no player of team '" << team << "' found!"); return AnnouncementWithTrickno(); } return this->announcement_of_player(*player); } // Game::AnnouncementWithTrickno Game::announcement_of_team(Team const& team) const /** ** ** -> result ** ** @param player the player ** ** @return the highest announcement of player 'player' ** ** @author Borg Enders ** @author Diether Knof ** ** @version 0.6.1 ** **/ Game::AnnouncementWithTrickno const& Game::announcement_of_player(Player const& player) const { DEBUG_ASSERTION((player.no() < this->playerno()), "Game::announcement_of_player(player)\n" << " illegal playerno of player = " << player.no()); return this->announcement_[player.no()].back(); } // Game::AnnouncementWithTrickno const& Game::announcement_of_player(Player const& player) const /** ** ** -> result ** ** @param player the player ** ** @return all announcements of player 'player' ** ** @author Borg Enders ** @author Diether Knof ** ** @version 0.6.1 ** **/ vector const& Game::announcements_of_player(Player const& player) const { DEBUG_ASSERTION((player.no() < this->playerno()), "Game::announcement_of_player(player)\n" << " illegal playerno of player = " << player.no()); return this->announcement_[player.no()]; } // vector const& Game::announcements_of_player(Player const& player) const /** ** request announcements from the players ** ** @param - ** ** @return - ** ** @author Diether Knof ** ** @version 0.7.4 **/ void Game::announcements_request() { bool again = true; // whether to ask the players again while (again) { again = false; // ask for announcements for (vector::const_iterator p = this->players().begin(); p != this->players().end(); p++) { Player& player = **p; if (this->valid_announcement(player)) { Announcement const announcement = player.announcement_request(); if (this->announcement_valid(announcement, player)) { this->make_announcement(announcement, player); if (this->announcement_of_player(player) == announcement) again = true; } // if (this->announcement_valid(announcement, player)) } // if (this->valid_announcement(*p)) } // for (p < this->playerno()) } // while (again) return ; } // void Game::announcements_request() /** ** -> result ** ** @param player player to check ** ** @result valid announcement for 'player' ** NOANNOUNCEMENT, if 'player' cannot announce anymore ** ** @author Diether Knof ** ** @version 0.7.4 **/ Announcement Game::valid_announcement(Player const& player) const { if (!TEAM::is_real(player.team())) return ANNOUNCEMENT::NOANNOUNCEMENT; if (ANNOUNCEMENT::is_real(this->announcement_of_team(TEAM::opposite(player.team()))) && this->announcement_valid(ANNOUNCEMENT::to_reply(this->announcement_of_team(TEAM::opposite(player.team()))), player)) return ANNOUNCEMENT::to_reply(this->announcement_of_team(TEAM::opposite(player.team()))); if (this->announcement_valid(player.next_announcement(), player)) return player.next_announcement(); return ANNOUNCEMENT::NOANNOUNCEMENT; } // Announcement Game::valid_announcement(Player player) const /** ** ** 'player' announces 'announcement' ** ** @param announcement announcement made ** @param player player who announces ** ** @result - ** ** @author Borg Enders ** @author Diether Knof ** ** @version 0.4.4 ** **/ void Game::make_announcement(Announcement announcement, Player const& player) { // test whether the announcement is valid if (announcement_valid(announcement, player)) { if (ANNOUNCEMENT::is_reply(announcement)) { if (announcement == ANNOUNCEMENT::REPLY) announcement = ANNOUNCEMENT::to_reply(announcement_of_team(opposite(player.team()) ).announcement); this->announcement_replied_on_.announcement = ANNOUNCEMENT::from_reply(announcement); this->announcement_replied_on_.trickno = this->tricks().size(); } #ifdef USE_NETWORK // check, that the server has send the announcement if ( (::server->has_parent_connection()) && (announcement > this->announcement_over_network_[player.no()]) ) { // send the announcement (request) over the network ::server->gameplay_action(GameplayAction::Announcement(player.no(), announcement)); return ; } // if (::server->has_parent_connection()) #endif // set the announcement this->announcement_[player.no()].push_back(AnnouncementWithTrickno(announcement, this->tricks().size())); // update the team info this->set_teaminfo(player, player.team()); // tell all players, that the announcement is made for(vector::const_iterator p = this->players().begin(); p != this->players().end(); p++) (*p)->announcement_made(announcement, player); // tell the ui, that the announcement is made if (!this->isvirtual()) { ::ui->announcement_made(announcement, player); ::ui->gameplay_action(GameplayAction::Announcement(player.no(), announcement)); } } // if (announcement_valid(announcement, player) return ; } // void Game::make_announcement(Announcement announcement, Player player) #ifdef USE_NETWORK /** ** make an announcement from the network ** This method is used, so that the server checks, whether an announcement ** is valid. So when an announcement is made locally, it is pushed to the ** server. The server checks and sends the announcement over the network. ** Then the client gets the announcement from the server and can announce. ** ** @param announcement announcement made ** @param player player who announces ** ** @result - ** ** @author Diether Knof ** ** @version 0.7.4 ** ** @todo fix it **/ void Game::make_announcement_from_network(Announcement announcement, Player const& player) { DEBUG_ASSERTION((this->announcement_over_network_[player.no()] <= announcement), "Game::make_announcement_from_network(announcement, player)\n" " announcement from the network = " << this->announcement_over_network_[player.no()] << " >= " << announcement << " = announcement to make"); // Probleme: // * Server hört nicht (läßt sich z.B. noch den Spieltyp anzeigen) // * Ansagen werden vom Server nicht übernommen/akzeptiert, // Beispiel, wenn der Server noch das Anzeigefenster offen hat und der // Client schon seine nächste Ansage tätigt. // // * Umgehung: Ansage nur zulassen, wenn Stich voll ist oder der Spieler selber am Zug ist. #ifdef DKNOF COUT << (::server->has_parent_connection() ? "child" : "parent") << ": make announcement from network: " << player.name() << ": " << announcement << endl; COUT << "valid: " << this->announcement_valid(announcement, player) << endl; #endif this->announcement_over_network_[player.no()] = announcement; this->make_announcement(announcement, player); return ; } // void Game::make_announcement_from_network(Announcement announcement, Player player) #endif // #ifdef USE_NETWORK /** ** ** -> result ** ** @param announcement the announcement ** @param player the player, who asks to make the announcement ** ** @return whether the announcement is valid ** ** @author Diether Knof ** ** @version 0.6.2 ** **/ bool Game::announcement_valid(Announcement const& announcement, Player const& player) const { #if 0 #ifdef DKNOF #ifdef NETWORK if (!::server->has_parent_connection()) if (announcement == ANNOUNCEMENT::NO60) return false; #endif #endif #endif // test, whether status is in a game if ( !::in_running_game()) return false; if (this->tricks_.empty()) return false; // no announcement after the 'Rule::ANNOUNCEMENT_LAST' trick if (this->tricks_remaining_no() < (this->rule()(Rule::ANNOUNCEMENT_LAST) + (this->trick_current().isfull() ? 1 : 0))) return false; // as long as the marriage is not determined, no announcement is valid if (this->marriage_selector() != MARRIAGE_SELECTOR::TEAM_SET) return false; Announcement const& team_announcement = this->announcement_of_team(player.team()).announcement; if (ANNOUNCEMENT::is_real(team_announcement) && is_reply(team_announcement)) return false; // the announcement must be a better one if (team_announcement >= announcement) return false; if (ANNOUNCEMENT::is_reply(announcement)) { if (!this->rule()(Rule::ANNOUNCEMENT_REPLY)) return false; if (team_announcement != ANNOUNCEMENT::NOANNOUNCEMENT) return false; AnnouncementWithTrickno const& opposite_announcement = announcement_of_team(opposite(player.team())); if ( (announcement != ANNOUNCEMENT::REPLY) && (ANNOUNCEMENT::from_reply(announcement) != opposite_announcement.announcement) ) return false; // same trick - I can make my announcement // Remark: 'this->trick_current_no()' can be different from // 'this->trick_.size()' if (this->tricks().size() == opposite_announcement.trickno) return true; return false; } // if (announcement == ANNOUNCEMENT::REPLY) unsigned const marriage_deferral = ( ( (this->type() == GAMETYPE::MARRIAGE) || (this->type() == GAMETYPE::MARRIAGE_SOLO)) ? (this->marriage_determination_trickno() - 1) : 0); bool valid = (((this->rule()(Rule::ANNOUNCEMENT_TILL_FULL_TRICK) ? this->real_tricks_remaining_no() : player.hand().cardsnumber()) + marriage_deferral) >= this->rule().remaining_cards(announcement)); // verify that all announcements before are valid if ( (announcement > ANNOUNCEMENT::NO120) && (this->announcement_of_team(player.team()).announcement != ANNOUNCEMENT::previous(announcement) ) ) valid &= this->announcement_valid(ANNOUNCEMENT::previous(announcement), player); return valid; } // bool Game::announcement_valid(Announcement const& announcement, Player const& player) /** ** ** -> result ** ** @param announcement the announcement ** @param player the player, who asks to make the announcement ** ** @return whether this is the last possibility to announce 'announcement' ** ** @author Diether Knof ** ** @version 0.7.3 ** **/ bool Game::last_chance_to_announce(Announcement const& announcement, Player const& player) const { if (!this->announcement_valid(announcement, player)) return false; unsigned const marriage_deferral = ( (this->type() == GAMETYPE::MARRIAGE) ? (this->marriage_determination_trickno() - 1) : 0); return (((this->rule()(Rule::ANNOUNCEMENT_TILL_FULL_TRICK) ? this->real_tricks_remaining_no() : player.hand().cardsnumber()) + marriage_deferral) == this->rule().remaining_cards(announcement)); } // bool Game::last_chance_to_announce(Announcement announcement, Player player) const