1 /**********************************************************************
2  *
3  *   FreeDoko a Doppelkopf-Game
4  *
5  *   Copyright (C) 2001 – 2018 by Diether Knof and Borg Enders
6  *
7  *   This program is free software; you can redistribute it and/or
8  *   modify it under the terms of the GNU General Public License as
9  *   published by the Free Software Foundation; either version 2 of
10  *   the License, or (at your option) any later version.
11  *
12  *   This program is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *   GNU General Public License for more details.
16  *   You can find this license in the file 'gpl.txt'.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with this program; if not, write to the Free Software
20  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  *   MA  02111-1307  USA
22  *
23  *  Contact:
24  *    Diether Knof dknof@posteo.de
25  *
26  **********************************************************************/
27 
28 #include "constants.h"
29 #include "game.h"
30 #include "gameplay.h"
31 #include "gameplay_actions/poverty.h"
32 #include "../os/bug_report_replay.h"
33 #include "../misc/preferences.h"
34 #include "../party/rule.h"
35 #include "../ui/ui.h"
36 
37 /** constructor
38  **
39  ** @param    game   corresponding game
40  **/
GamePoverty(Game & game)41 GamePoverty::GamePoverty(Game& game) noexcept :
42   game_(&game)
43 { }
44 
45 /** constructor
46  **
47  ** @param    poverty   poverty to copy
48  ** @param    game   corresponding game
49  **/
GamePoverty(GamePoverty const & poverty,Game & game)50 GamePoverty::GamePoverty(GamePoverty const& poverty, Game& game) :
51   game_(&game),
52   shifted_cards_(poverty.shifted_cards_),
53   returned_cards_(poverty.returned_cards_),
54   shifted_(poverty.shifted_)
55 { }
56 
57 /** @return   whether cards were shifted
58  **/
59 bool
shifted() const60 GamePoverty::shifted() const noexcept
61 {
62   return this->shifted_;
63 }
64 
65 /** @return   the number of cards, that are shifted by the poverty player
66  **/
67 unsigned
shifted_cardno() const68 GamePoverty::shifted_cardno() const noexcept
69 {
70   return this->shifted_cards().size();
71 }
72 
73 /** @return   the number of cards, that are returned to the poverty player
74  **/
75 unsigned
returned_cardno() const76 GamePoverty::returned_cardno() const noexcept
77 {
78   return this->returned_cards().size();
79 }
80 
81 /** @return   the number of trumps, that are returned to the poverty player
82  **/
83 unsigned
returned_trumpno() const84 GamePoverty::returned_trumpno() const noexcept
85 {
86   return this->returned_cards().count_trumps();
87 }
88 
89 /** @return   the shifted poverty cards
90  **/
91 HandCards const&
shifted_cards() const92 GamePoverty::shifted_cards() const
93 {
94   DEBUG_ASSERTION((this->game_->type() == GameType::poverty),
95                   "GamePoverty::shifted_cards():\n"
96                   "  invalid gametype");
97 
98   return this->shifted_cards_;
99 }
100 
101 /** @return   the returned poverty cards
102  **/
103 HandCards const&
returned_cards() const104 GamePoverty::returned_cards() const
105 {
106   DEBUG_ASSERTION((this->game_->type() == GameType::poverty),
107                   "GamePoverty::returned_cards():\n"
108                   "  invalid gametype '" << this->game_->type() << "'");
109 
110   return this->returned_cards_;
111 }
112 
113 /** reset all data
114  **/
115 void
reset()116 GamePoverty::reset() noexcept
117 {
118   this->shifted_cards_.clear();
119   this->returned_cards_.clear();
120   this->shifted_ = false;
121 }
122 
123 /** make the shifting of the poverty cards
124  **/
125 void
shift()126 GamePoverty::shift()
127 {
128   auto& game = *this->game_;
129   auto& players = game.players();
130   auto const& rule = game.rule();
131   auto& soloplayer = players.soloplayer();
132   auto& gameplay = *game.gameplay_;
133 
134   if (   (game.type() != GameType::poverty)
135       || !rule(Rule::Type::poverty_shift)
136       || (   (soloplayer.hand().count_trumps() <= 1)
137           && rule(Rule::Type::throw_with_one_trump)
138          )
139      ) {
140     players.set_current_player(soloplayer);
141     ::game_status = GameStatus::game_redistribute;
142     return ;
143   } // if (not poverty shift)
144 
145   ::game_status = GameStatus::game_poverty_shift;
146 
147   game.teaminfo().set(soloplayer, Team::re);
148   soloplayer.set_team(Team::re);
149 
150   game.swines().test_swines_from_reservations();
151 
152   if (!game.isvirtual()) {
153     ::ui->poverty_shifting(soloplayer);
154     this->signal_shifting_(soloplayer);
155   }
156 
157   this->shifted_cards_ = soloplayer.poverty_shift();
158 
159   DEBUG_ASSERTION((this->shifted_cards().player()
160                    == soloplayer),
161                   "Game::poverty_shift():\n"
162                   "  the soloplayer '" << soloplayer.no()
163                   << "' shifts cards from player '"
164                   << this->shifted_cards().player().no() << "'");
165 
166   DEBUG_ASSERTION((soloplayer.hand().count_trumps()
167                    == 0),
168                   "Game::poverty_shift():\n"
169                   "  Poverty: the soloplayer still has trump on the hand.\n"
170                   "remaining trumps: "
171                   << soloplayer.hand().count_trumps() << '\n'
172                   << "Hand:\n"
173                   << soloplayer.hand()
174                   << "shifted cards:\n"
175                   << this->shifted_cards());
176 
177   DEBUG_ASSERTION( (this->shifted_cardno()
178                     + soloplayer.hand().cardsnumber()
179                     == rule(Rule::Type::number_of_tricks_in_game)),
180                   "Game::poverty_shift():\n"
181                   "  wrong number of cards shifted: "
182                   << this->shifted_cardno());
183 
184   if (rule(Rule::Type::poverty_shift_only_trump))
185     DEBUG_ASSERTION(this->shifted_cards().count_trumps()
186                       == this->shifted_cardno(),
187                     "Game::poverty_shift():\n"
188                     "  wrong number of cards shifted: "
189                     << "trumps = " << this->shifted_cards().count_trumps()
190                     << " != " << this->shifted_cardno() << " = cards shifted");
191   else if (!rule(Rule::Type::poverty_fox_do_not_count)
192            || game.swines().swines_announced() )
193     // normal case: shift at max 3 cards
194     DEBUG_ASSERTION(this->shifted_cardno()
195                       == rule(Rule::Type::max_number_of_poverty_trumps),
196                     "Game::poverty_shift():\n"
197                     "  wrong number of cards shifted: "
198                     << "poverty cards = " << rule(Rule::Type::max_number_of_poverty_trumps)
199                     << " != " << this->shifted_cardno() << " = cards shifted");
200   else if (!rule(Rule::Type::poverty_fox_shift_extra))
201     // at max 3 cards or number of trumps
202     DEBUG_ASSERTION( (   (this->shifted_cards().count_trumps()
203                           == this->shifted_cardno())
204                       || (this->shifted_cardno()
205                           == rule(Rule::Type::max_number_of_poverty_trumps)) ),
206                     "Game::poverty_shift():\n"
207                     "  wrong number of cards shifted: "
208                     << "trumps or "
209                     << "poverty cards = " << rule(Rule::Type::max_number_of_poverty_trumps)
210                     << " != " << this->shifted_cardno() << " = cards shifted");
211   else
212     // fox are shifted extra
213     DEBUG_ASSERTION( (this->shifted_cardno()
214                       - this->shifted_cards().count_trump_aces()
215                       == rule(Rule::Type::max_number_of_poverty_trumps)),
216                     "Game::poverty_shift():\n"
217                     "  wrong number of cards shifted: "
218                     << "poverty cards = " << rule(Rule::Type::max_number_of_poverty_trumps)
219                     << " != " << this->shifted_cardno() << " - "
220                     << this->shifted_cards().count_trump_aces()
221                     << " = cards shifted - fox");
222 
223   for (auto& p : players)
224     p.poverty_shift(soloplayer, this->shifted_cardno());
225 
226   gameplay.gameplay_action(GameplayActions::PovertyShift(soloplayer.no(),
227                                                                 this->shifted_cards()));
228   if (!game.isvirtual()) {
229     ::ui->poverty_shift(soloplayer, this->shifted_cardno());
230     this->signal_shift_(soloplayer, this->shifted_cardno());
231     ::ui->gameplay_action(GameplayActions::PovertyShift(soloplayer.no(),
232                                                         this->shifted_cards()));
233     game.signal_gameplay_action_(GameplayActions::PovertyShift(soloplayer.no(),
234                                                                 this->shifted_cards()));
235   }
236 
237   // ask all players, whether they accept the poverty or not
238   bool accept = false;
239   players.set_current_player(soloplayer);
240   for (players.next_current_player();
241        players.current_player() != soloplayer;
242        players.next_current_player()) {
243     auto& current_player = players.current_player();
244     if (!game.isvirtual()) {
245       ::ui->poverty_ask(current_player, this->shifted_cardno());
246       this->signal_ask_(current_player, this->shifted_cardno());
247       if (   !(::fast_play & FastPlay::pause)
248           && !(   ::bug_report_replay
249                && ::bug_report_replay->auto_action())) {
250         if (current_player.type() != Player::Type::human)
251           ::ui->sleep(::preferences(Preferences::Type::card_play_delay));
252       }
253       if (::game_status != GameStatus::game_poverty_shift)
254         return ;
255     } // if (!this->isvirtual())
256 
257 
258     accept = current_player.poverty_take_accept(this->shifted_cardno());
259     if (::game_status != GameStatus::game_poverty_shift)
260       return ;
261 
262     if (accept) {
263       { // test for swines and hyperswines before taking the cards
264         if (rule(Rule::Type::swines)
265             && rule(Rule::Type::swines_announcement_begin)
266             && game.reservation(current_player).swines)
267           game.swines().swines_announce(current_player);
268         if (rule(Rule::Type::hyperswines)
269             && rule(Rule::Type::hyperswines_announcement_begin)
270             && game.reservation(current_player).hyperswines)
271           game.swines().hyperswines_announce(current_player);
272       } // test for swines and hyperswines before taking the cards
273       gameplay.gameplay_action(GameplayActions::PovertyAccepted(current_player.no()));
274       ::ui->gameplay_action(GameplayActions::PovertyAccepted(current_player.no()));
275       game.signal_gameplay_action_(GameplayActions::PovertyAccepted(current_player.no()));
276       break;
277     } else { // if !(accept)
278       for (auto& p : players) {
279         p.poverty_take_denied(current_player);
280       }
281       gameplay.gameplay_action(GameplayActions::PovertyDenied(current_player.no()));
282       if (!game.isvirtual()) {
283         ::ui->poverty_take_denied(current_player);
284         this->signal_take_denied_(current_player);
285         ::ui->gameplay_action(GameplayActions::PovertyDenied(current_player.no()));
286         game.signal_gameplay_action_(GameplayActions::PovertyDenied(current_player.no()));
287       }
288     } // if !(accept)
289   } // for (current_player)
290 
291   if (!accept) {
292     players.set_current_player(soloplayer);
293 
294     for (auto& p : players)
295       p.poverty_take_denied_by_all();
296     gameplay.gameplay_action(GameplayActions::PovertyDeniedByAll());
297     if (!game.isvirtual()) {
298       ::ui->poverty_take_denied_by_all();
299       this->signal_take_denied_by_all_();
300       ::ui->gameplay_action(GameplayActions::PovertyDeniedByAll());
301       game.signal_gameplay_action_(GameplayActions::PovertyDeniedByAll());
302     }
303 
304     soloplayer.hand().add(this->shifted_cards());
305 
306     ::game_status = GameStatus::game_redistribute;
307   } else { // if !(!accept)
308     auto& current_player = players.current_player();
309     // set the teaminfo
310     auto& teaminfo = game.teaminfo();
311     teaminfo.set(current_player, Team::re);
312     current_player.set_team(Team::re);
313 
314     for (auto& p : players) {
315       if (teaminfo.get(p) == Team::unknown) {
316         teaminfo.set(p, Team::contra);
317         p.set_team(Team::contra);
318       }
319     } // for (p)
320     teaminfo.set_at_gamestart();
321 
322     this->returned_cards_
323       = current_player.poverty_cards_change(this->shifted_cards());
324     if (::game_status != GameStatus::game_poverty_shift)
325       return ;
326 
327     // check for shifted swines
328     if (rule(Rule::Type::swines)
329         && game.swines().swines_announced()
330         && (game.swines().swines_owner() == soloplayer)) {
331       game.swines().swines_announce(current_player);
332     }
333     if (rule(Rule::Type::hyperswines)
334         && game.swines().hyperswines_announced()
335         && (game.swines().hyperswines_owner() == soloplayer)) {
336       if (!rule(Rule::Type::swines_and_hyperswines_joint)) {
337         game.swines().hyperswines_owner_ = nullptr;
338         game.swines().hyperswines_announced_ = false;
339       }
340       game.swines().hyperswines_announce(current_player);
341     }
342 
343     DEBUG_ASSERTION((this->returned_cards().player()
344                      == current_player),
345                     "Game::poverty_shift():\n"
346                     "  the current player '" << current_player.no()
347                     << "' shifts back cards from player '"
348                     << this->returned_cards().player().no() << "'");
349 
350     DEBUG_ASSERTION((this->returned_cards().size()
351                      == this->shifted_cardno()),
352                     "Game::poverty_shift():\n"
353                     "  number of cards returned ("
354                     << this->returned_cards().size()
355                     << ") is not the same as are shifted ("
356                     << this->shifted_cardno() << ").");
357 
358     for (auto& p : players) {
359       p.poverty_take_accepted(current_player,
360                               this->returned_cardno(),
361                               this->returned_trumpno());
362     }
363     gameplay.gameplay_action(GameplayActions::PovertyReturned(current_player.no(),
364                                                               this->returned_cards()));
365     if (!game.isvirtual()) {
366       ::ui->poverty_take_accepted(current_player,
367                                   this->returned_cardno(),
368                                   this->returned_trumpno());
369       this->signal_take_accepted_(current_player,
370                                   this->returned_cardno(),
371                                   this->returned_trumpno());
372       ::ui->gameplay_action(GameplayActions::PovertyReturned(current_player.no(),
373                                                              this->returned_cards()));
374       game.signal_gameplay_action_(GameplayActions::PovertyReturned(current_player.no(),
375                                                                     this->returned_cards()));
376     }
377 
378 
379     // add the cards to the player
380     players.set_current_player(soloplayer);
381     soloplayer.poverty_cards_get_back(this->returned_cards());
382     if (!game.isvirtual()) {
383       ::ui->gametype_changed();
384       game.signal_gametype_changed_();
385     }
386 
387     if (::game_status != GameStatus::game_poverty_shift)
388       return ;
389 
390   } // if !(!accept)
391 
392   this->shifted_ = true;
393 
394   if (accept) {
395     if (game.swines().swines_announced()) {
396       // check, that the swines announcement is correct
397       auto const& swines_owner = game.swines().swines_owner();
398       if (swines_owner.team() == Team::re) {
399         auto p = container_algorithm::find_if(players,
400                                               [this](auto player) {
401                                               return true;
402                                               });
403 
404         if (p != players.end()) {
405           game.swines().swines_announce(*p);
406         } else {
407           game.swines().swines_owner_ = nullptr;
408           game.swines().swines_announced_ = false;
409         }
410       } // if (this->swines_owner().team() == Team::re)
411     } // if (this->swines_owner())
412   } // if (accept)
413 
414   if (::game_status == GameStatus::game_poverty_shift)
415     ::game_status = GameStatus::game_reservation;
416 } // void GamePoverty::shift()
417 
418 
419 /** signals
420  **/
421 
422 Signal<void(Player const&)>&
signal_shifting()423 GamePoverty::signal_shifting() noexcept
424 { return this->signal_shifting_; }
425 
426 Signal<void(Player const&, unsigned)>&
signal_shift()427 GamePoverty::signal_shift() noexcept
428 { return this->signal_shift_; }
429 
430 Signal<void(Player const&, unsigned)>&
signal_ask()431 GamePoverty::signal_ask() noexcept
432 { return this->signal_ask_; }
433 
434 Signal<void(Player const&)>&
signal_take_denied()435 GamePoverty::signal_take_denied() noexcept
436 { return this->signal_take_denied_; }
437 
438 Signal<void()>&
signal_take_denied_by_all()439 GamePoverty::signal_take_denied_by_all() noexcept
440 { return this->signal_take_denied_by_all_; }
441 
442 Signal<void(Player const&, unsigned, unsigned)>&
signal_take_accepted()443 GamePoverty::signal_take_accepted() noexcept
444 { return this->signal_take_accepted_; }
445 
446 Signal<void(Player const&)>&
signal_get_cards_to_shift()447 GamePoverty::signal_get_cards_to_shift() noexcept
448 { return this->signal_get_cards_to_shift_; }
449 
450 Signal<void(Player const&, unsigned)>&
signal_get_take_accept()451 GamePoverty::signal_get_take_accept() noexcept
452 { return this->signal_get_take_accept_; }
453 
454 Signal<void(Player const&, HandCards)>&
signal_get_exchanged_cards()455 GamePoverty::signal_get_exchanged_cards() noexcept
456 { return this->signal_get_exchanged_cards_; }
457 
458 Signal<void(Player const&, HandCards)>&
signal_cards_get_back()459 GamePoverty::signal_cards_get_back() noexcept
460 { return this->signal_cards_get_back_; }
461