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/announcement.h"
32 #include "../party/rule.h"
33 #include "../ui/ui.h"
34
35 /** constructor
36 **
37 ** @param game corresponding game
38 **/
Announcements(Game & game)39 Announcements::Announcements(Game& game) :
40 game_(&game)
41 { }
42
43 /** constructor
44 **
45 ** @param announcements announcements to copy
46 ** @param game corresponding game
47 **/
Announcements(Announcements const & announcements,Game & game)48 Announcements::Announcements(Announcements const& announcements,
49 Game& game) :
50 game_(&game),
51 announcements_(announcements.announcements_)
52 { }
53
54 /** -> result
55 **
56 ** @param team the team
57 **
58 ** @return the announcement of the team 'team'
59 **/
60 AnnouncementWithTrickno
last(Team const team) const61 Announcements::last(Team const team) const
62 {
63 auto const& game = *this->game_;
64 DEBUG_ASSERTION( (team == Team::re)
65 || (team == Team::contra)
66 || game.marriage().is_undetermined(),
67 "Announcements::last(team)\n"
68 " invalid team = " << team);
69
70 // In an undetermined marriage the contra team cannot have announced.
71 // This case is treated here, because in this case there does not
72 // exists a player of the team 'contra' so the assertion later on would fail.
73 if ( game.marriage().is_undetermined()
74 && (team != Team::re) )
75 return {};
76
77 // the player of the team, that has made the highest announcement
78 Player const* player = nullptr;
79 auto const& rule = game.rule();
80
81 // take the highest announcement
82 for (auto const& p : game.players()) {
83 if ( rule(Rule::Type::knocking)
84 && !is_real(game.teaminfo().get(p)))
85 continue;
86
87 if (p.team() == team)
88 if (!player
89 || (this->last(p).announcement
90 > this->last(*player).announcement))
91 player = &p;
92 } // for (p : this->players())
93
94 if ( rule(Rule::Type::knocking)
95 && !player)
96 return {};
97
98 if (!player) {
99 DEBUG_ASSERTION(game.isvirtual(),
100 "Announcements::last(team):\n"
101 " no player of team '" << team << "' found!");
102 return {};
103 }
104
105 return this->last(*player);
106 } // AnnouncementWithTrickno Announcements::last(Team team) const
107
108 /** -> result
109 **
110 ** @param player the player
111 **
112 ** @return the highest announcement of player 'player'
113 **/
114 AnnouncementWithTrickno
last(Player const & player) const115 Announcements::last(Player const& player) const
116 {
117 auto const& announcements = this->announcements_[player.no()];
118 if (announcements.empty())
119 return {};
120 return announcements.back();
121 }
122
123 /** -> result
124 **
125 ** @param player the player
126 **
127 ** @return all announcements of player 'player'
128 **/
129 vector<AnnouncementWithTrickno> const&
all(Player const & player) const130 Announcements::all(Player const& player) const
131 {
132 return this->announcements_[player.no()];
133 }
134
135 /** initializion for a new game
136 **/
137 void
init()138 Announcements::init()
139 {
140 auto const& game = *this->game_;
141 auto const& rule = game.rule();
142 auto const number_of_players = rule(Rule::Type::number_of_players_in_game);
143 this->announcements_
144 = vector<vector<AnnouncementWithTrickno>>(number_of_players);
145 }
146
147 /** remove all announcements but re and contra
148 **/
149 void
remove_all_but_re_contra()150 Announcements::remove_all_but_re_contra()
151 {
152 auto const& game = *this->game_;
153 for (unsigned p = 0; p < game.players().size(); p++) {
154 auto const& player = game.players().player(p);
155 auto& announcements = this->announcements_[p];
156 if (announcements.size() <= 1)
157 continue;
158 if (is_reply(announcements.front().announcement)) {
159 announcements.erase(announcements.begin() + 1,
160 announcements.end());
161 continue;
162 }
163 // the player has made an announcement
164 announcements.erase(announcements.begin() + 2,
165 announcements.end());
166 announcements[1].announcement = Announcement::no120;
167 if (!game.isvirtual()) {
168 ::ui->announcement_made(Announcement::no120, player);
169 this->signal_announcement_made_(Announcement::no120, player);
170 }
171 } // for (p < this->playerno())
172 }
173
174 /** request announcements from the players
175 **/
176 void
request_from_players()177 Announcements::request_from_players()
178 {
179 auto& game = *this->game_;
180 bool ask_again = true;
181
182 while (ask_again) {
183 ask_again = false;
184
185 // ask for announcements
186 for (auto& p : game.players()) {
187 if (this->valid_announcement(p) != Announcement::noannouncement) {
188 auto const announcement = p.announcement_request();
189 if (this->is_valid(announcement, p)) {
190 this->make_announcement(announcement, p);
191 if (this->last(p) == announcement)
192 ask_again = true;
193 } // if (this->announcement_valid(announcement, p))
194 } // if (this->valid_announcement(p))
195 } // for (p < this->playerno())
196 } // while (again)
197 } // void Announcements::request_from_players()
198
199 /** evaluate the trick for announcements
200 **/
201 void
evaluate_trick(Trick const & trick)202 Announcements::evaluate_trick(Trick const& trick)
203 {
204 auto& game = *this->game_;
205 auto const& rule = game.rule();
206 auto const& winnerplayer = trick.winnerplayer();
207
208 // test whether the player has to make an announcement
209 if ( (game.type() == GameType::normal)
210 && (game.tricks().current_no() == 1)
211 && (rule(Rule::Type::announcement_first_trick_thirty_points))
212 && (trick.points() >= 30)
213 && (this->last(winnerplayer.team())
214 != Announcement::no0)
215 && ( !rule(Rule::Type::announcement_first_trick_thirty_points_only_first)
216 || ( (this->last(Team::re) == Announcement::noannouncement)
217 && (this->last(Team::contra) == Announcement::noannouncement) )
218 )
219 && ( !rule(Rule::Type::announcement_first_trick_thirty_points_only_re_contra)
220 || (this->last(winnerplayer.team())
221 == Announcement::noannouncement) )
222 && ( !is_marriage(game.type())
223 || rule(Rule::Type::announcement_first_trick_thirty_points_in_marriage))
224 ) {
225 this->make_announcement(next(this->last(winnerplayer.team())), winnerplayer);
226 } // if (player must make an announcement)
227 }
228
229 /** -> result
230 **
231 ** @param player player to check
232 **
233 ** @result valid announcement for 'player'
234 ** noannouncement, if 'player' cannot announce anymore
235 **/
236 Announcement
valid_announcement(Player const & player) const237 Announcements::valid_announcement(Player const& player) const
238 {
239 if (!is_real(player.team()))
240 return Announcement::noannouncement;
241
242 auto const opposite_announcement = this->last(opposite(player.team()));
243 if ( is_real(opposite_announcement)
244 && this->is_valid(to_reply(opposite_announcement), player))
245 return to_reply(opposite_announcement);
246
247 if (this->is_valid(player.next_announcement(), player))
248 return player.next_announcement();
249
250 return Announcement::noannouncement;
251 }
252
253
254 /** 'player' announces 'announcement'
255 **
256 ** @param announcement announcement made
257 ** @param player player who announces
258 **
259 ** @result -
260 **/
261 void
make_announcement(Announcement announcement,Player const & player)262 Announcements::make_announcement(Announcement announcement,
263 Player const& player)
264 {
265 if (!is_valid(announcement, player))
266 return ;
267 #ifdef TODO
268 // when necessary, a 're' is changed to a 'reply'
269 #endif
270
271 auto& game = *this->game_;
272 if (announcement == Announcement::reply) {
273 auto const opposite_announcement = this->last(opposite(player.team()));
274 announcement = to_reply(opposite_announcement);
275 }
276
277 // set the announcement
278 this->announcements_[player.no()].push_back(AnnouncementWithTrickno(announcement, game.tricks().current_no()));
279 // update the team info
280 if (!game.rule()(Rule::Type::knocking))
281 game.teaminfo().set(player, player.team());
282
283 // tell all players, that the announcement is made
284 for (auto& p : game.players())
285 p.announcement_made(announcement, player);
286
287 game.gameplay_->gameplay_action(GameplayActions::Announcement(player.no(),
288 announcement));
289 if (!game.isvirtual()) {
290 ::ui->announcement_made(announcement, player);
291 this->signal_announcement_made_(announcement, player);
292 ::ui->gameplay_action(GameplayActions::Announcement(player.no(),
293 announcement));
294 game.signal_gameplay_action_(GameplayActions::Announcement(player.no(),
295 announcement));
296 }
297 } // void Announcements::make_announcement(Announcement announcement, Player player)
298
299 /** -> result
300 **
301 ** @param announcement the announcement
302 ** @param player the player, who asks to make the announcement
303 **
304 ** @return the announcement, if it is valid (but 'reply' for 're', if it is only allowed as reply)
305 **/
306 bool
is_valid(Announcement announcement,Player const & player) const307 Announcements::is_valid(Announcement announcement,
308 Player const& player) const
309 {
310 auto const& game = *this->game_;
311 auto const& tricks = game.tricks();
312 auto const& rule = game.rule();
313 // test, whether status is in a game
314 if ( !::in_running_game())
315 return false;
316 if (tricks.empty())
317 return false;
318
319 // no announcement after the 'Rule::Type::announcement_last' trick
320 if (tricks.remaining_no()
321 < (rule(Rule::Type::announcement_last)
322 + (tricks.current().isfull() ? 1 : 0))) {
323 return false;
324 }
325
326 // as long as the marriage is not determined, no announcement is valid
327 if ( (game.marriage().selector() != MarriageSelector::team_set)
328 && (game.marriage().selector() != MarriageSelector::silent))
329 return false;
330
331 Announcement const& last_announcement
332 = ( ( rule(Rule::Type::knocking)
333 && is_with_unknown_teams(game.type()))
334 ? this->last(player).announcement
335 : this->last(player.team()).announcement);
336
337 if (is_reply(last_announcement)) {
338 return false;
339 }
340
341 // change the announcement
342 if (announcement == Announcement::reply) {
343 if (!this->last(opposite(player.team())).announcement)
344 return false;
345
346 if ( (rule(Rule::Type::announcement_strict_limit))
347 && is_real(this->last(opposite(player.team())).announcement) ) {
348 announcement = to_reply(this->last(opposite(player.team())
349 ).announcement);
350 } else {
351 announcement = Announcement::no120;
352 //return false;
353 }
354 } // if (announcement == Announcement::reply)
355
356 // the announcement must be a better one
357 if (last_announcement >= announcement) {
358 return false;
359 }
360
361 unsigned const marriage_deferral
362 = ( ( (game.type() == GameType::marriage)
363 || (game.type() == GameType::marriage_solo))
364 ? (game.marriage().determination_trickno()
365 - 1)
366 : 0);
367 // whether a normal announcement is valid
368 bool valid = (((rule(Rule::Type::announcement_till_full_trick)
369 ? tricks.real_remaining_no()
370 : player.hand().cardsnumber())
371 + marriage_deferral)
372 >= rule.remaining_cards(announcement));
373
374 // change a 're' to a 'reply', if no futher announcement can be made
375 if (!valid && !is_reply(announcement)) {
376 if ( (announcement == Announcement::no120)
377 && !rule(Rule::Type::announcement_strict_limit))
378 announcement = Announcement::reply;
379 else
380 return false;
381 }
382
383 if (is_reply(announcement)) {
384 if (!rule(Rule::Type::announcement_reply)) {
385 return false;
386 }
387
388 if (last_announcement != Announcement::noannouncement) {
389 return false;
390 }
391
392 Announcement const opposite_announcement
393 = this->last(opposite(player.team()));
394
395 if ( (announcement != Announcement::reply)
396 && (from_reply(announcement) != opposite_announcement) ) {
397 return false;
398 }
399
400 // till one trick after the last possible moment
401 // - I can make my announcement
402 // Remark: 'this->tricks().current_no()' can be different from
403 // 'this->trick_.size()'
404 if (((rule(Rule::Type::announcement_till_full_trick)
405 ? tricks.real_remaining_no()
406 : player.hand().cardsnumber())
407 + marriage_deferral)
408 + 1
409 >= rule.remaining_cards(opposite_announcement)) {
410 return true;
411 }
412
413 return false;
414 } // if (announcement == Announcement::reply)
415
416 // verify that all announcements before are valid
417 if ( !rule(Rule::Type::announcement_limit_only_for_current)
418 && (announcement > Announcement::no120)
419 && (last_announcement
420 != previous(announcement) ) ) {
421 return this->is_valid(previous(announcement), player);
422 }
423
424 return valid;
425 } // bool Announcements::is_valid(Announcement announcement, Player player)
426
427 /** -> result
428 **
429 ** @param announcement the announcement
430 ** @param player the player, who asks to make the announcement
431 **
432 ** @return whether this is the last possibility to announce 'announcement'
433 **/
434 bool
last_chance_to_announce(Announcement const announcement,Player const & player) const435 Announcements::last_chance_to_announce(Announcement const announcement,
436 Player const& player) const
437 {
438 auto const& game = *this->game_;
439 auto const& tricks = game.tricks();
440 auto const& rule = game.rule();
441 if (!this->is_valid(announcement, player))
442 return false;
443
444 auto const marriage_deferral = ( (game.type() == GameType::marriage)
445 ? (game.marriage().determination_trickno()
446 - 1)
447 : 0);
448
449 // re as a reply
450 if ( (announcement == Announcement::no120
451 && !rule(Rule::Type::announcement_strict_limit)) ) {
452 Announcement const& opposite_announcement
453 = this->last(::opposite(player.team()));
454 if (opposite_announcement != Announcement::noannouncement)
455 return (((rule(Rule::Type::announcement_till_full_trick)
456 ? tricks.real_remaining_no()
457 : player.hand().cardsnumber())
458 + marriage_deferral) + 1
459 == rule.remaining_cards(opposite_announcement));
460 } // if (re as reply)
461
462
463 if (rule(Rule::Type::announcement_till_full_trick)) {
464 if (!tricks.current().isfull())
465 return false;
466 return (tricks.real_remaining_no()
467 + marriage_deferral
468 == rule.remaining_cards(announcement));
469 } else {
470 if (game.players().current_player() != player)
471 return false;
472 if (tricks.current().has_played(player))
473 return false;
474 return ( player.hand().cardsnumber()
475 + marriage_deferral
476 == rule.remaining_cards(announcement));
477 }
478 } // bool Announcements::last_chance_to_announce(Announcement announcement, Player player) const
479
480 /** @return signal, when an announcement is made
481 **/
482 Signal<void(Announcement, Player const&)>&
signal_announcement_made()483 Announcements::signal_announcement_made()
484 { return this->signal_announcement_made_; }
485
486