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 
30 #ifdef USE_UI_GTKMM
31 
32 #include "players_db.h"
33 #include "ui.h"
34 #include "cards.h"
35 
36 #include "../../party/party.h"
37 #include "../../player/player.h"
38 #include "../../player/playersDb.h"
39 #include "../../player/ai/aiDb.h"
40 
41 #include "../../utils/string.h"
42 
43 
44 #include <gtkmm/treeview.h>
45 #include <gtkmm/radiobutton.h>
46 #include <gtkmm/scrolledwindow.h>
47 #include <gdk/gdkkeysyms.h>
48 namespace UI_GTKMM_NS {
49 
50 constexpr PlayersDB::Statistic PlayersDB::statistic_all[];
51 
52 /** -> result
53  **
54  ** @param    statistic   statistic
55  **
56  ** @return   string of 'statistic'
57  **/
58 string
to_string(Statistic const statistic)59   PlayersDB::to_string(Statistic const statistic)
60   {
61     switch(statistic) {
62     case Statistic::total:
63       (void)_("Statistic::total");
64       return "total";
65     case Statistic::won:
66       (void)_("Statistic::won");
67       return "won";
68     case Statistic::lost:
69       (void)_("Statistic::lost");
70       return "lost";
71     case Statistic::percent_won:
72       (void)_("Statistic::percent won");
73       return "percent won";
74     } // switch(statistic)
75 
76     return "";
77   } // static string PlayersDB::to_string(Statistic statistic)
78 
79 /** translation
80  **
81  ** @param    statistic   statistic
82  **
83  ** @return   translation
84  **/
85 string
gettext(Statistic const statistic)86   PlayersDB::gettext(Statistic const statistic)
87   {
88     return ::gettext("Statistic::" + to_string(statistic));
89   }
90 
91 /** Constructor for the model
92  **
93  ** @param    playerno   number of players in the party
94  **/
PlayersDBModel(unsigned const playerno)95 PlayersDB::PlayersDBModel::PlayersDBModel(unsigned const playerno) :
96   Gtk::TreeModel::ColumnRecord(),
97   type(),
98   statistic(playerno)
99 {
100   this->add(this->type);
101   for (unsigned p = 0; p < playerno; p++)
102     this->add(this->statistic[p]);
103 } // PlayersDB::PlayersDBModel::PlayersDBModel(unsigned playerno)
104 
105 /** constructor
106  **
107  ** @param    parent   the parent object
108  **/
PlayersDB(Base * const parent)109 PlayersDB::PlayersDB(Base* const parent) :
110   Base(parent),
111   StickyDialog("FreeDoko – " + _("Window::players database"), false)
112 {
113   this->ui->add_window(*this);
114 
115   this->set_default_size(0, 3 * this->ui->cards->height());
116   this->signal_realize().connect(sigc::mem_fun(*this, &PlayersDB::init));
117 
118 #ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
119   this->signal_key_press_event().connect(sigc::mem_fun(*this, &PlayersDB::on_key_press_event));
120 #endif
121 } // PlayersDB::PlayersDB(Base* parent)
122 
123 /** destructor
124  **/
125 PlayersDB::~PlayersDB() = default;
126 
127 /** create all subelements
128  **/
129 void
init()130 PlayersDB::init()
131 {
132   this->set_icon(this->ui->icon);
133 
134   { // hbox
135     auto hbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 1 EM));
136     { // statistic type
137       auto vbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 1 EX / 2));
138 
139       Gtk::RadioButton::Group statistic_group;
140       for (auto statistic : statistic_all) {
141         auto const radio_button
142           = Gtk::manage(new Gtk::RadioButton(statistic_group,
143                                              gettext(statistic)));
144         vbox->add(*radio_button);
145         radio_button->signal_clicked().connect(sigc::bind<Statistic const>(sigc::mem_fun(*this, &PlayersDB::set_statistic), statistic));
146         if (statistic == Statistic::percent_won)
147           radio_button->set_active(true);
148 
149       } // for (statistic)
150       vbox->set_valign(Gtk::ALIGN_START);
151       hbox->add(*vbox);
152     } // statistic type
153 
154     { // the treeview
155 
156       // the points table
157       this->players_db_model = std::make_unique<PlayersDBModel>(this->ui->party().players().size());
158 
159       this->players_db_list
160         = Gtk::TreeStore::create(*this->players_db_model);
161       this->players_db_treeview
162         = Gtk::manage(new Gtk::TreeView(this->players_db_list));
163 
164       this->players_db_treeview->append_column(_("players database data type"),
165                                                this->players_db_model->type);
166       this->players_db_treeview->get_column_cell_renderer(0)->set_property("xalign", 1);
167       for (unsigned p = 0; p < this->ui->party().players().size(); p++) {
168         this->players_db_treeview->append_column(this->ui->party().players()[p].name(),
169                                                  this->players_db_model->statistic[p]);
170         this->players_db_treeview->get_column_cell_renderer(p + 1)->set_property("xalign", 0.5);
171       } // for (p < this->ui->party().players().size())
172 
173       { // the scrolled window
174         auto scrolled_window = Gtk::manage(new Gtk::ScrolledWindow);
175         scrolled_window->set_policy(Gtk::POLICY_NEVER,
176                                     Gtk::POLICY_AUTOMATIC);
177         scrolled_window->add(*(this->players_db_treeview));
178 
179         hbox->pack_start(*scrolled_window);
180       } // the scrolled window
181     } // the treeview
182     this->get_content_area()->pack_start(*hbox);
183   } // hbox
184 
185   { // action area
186     { // clear button
187       auto clear_button = Gtk::manage(new Gtk::Button(_("Button::clear database")));
188       clear_button->set_image_from_icon_name("edit-clear-all");
189       clear_button->set_always_show_image();
190       this->add_action_widget(*clear_button, Gtk::RESPONSE_NONE);
191       clear_button->signal_clicked().connect(sigc::mem_fun(*this, &PlayersDB::clear_db));
192     } // clear button
193 
194     add_close_button(*this);
195   } // action area
196 
197   this->show_all_children();
198 
199   this->signal_show().connect(sigc::mem_fun(*this,
200                                             &PlayersDB::recreate_db));
201   this->recreate_db();
202 
203   Party::signal_open().connect_back([this](Party&) { this->recreate_db(); }, this->disconnector_);
204   party->signal_close().connect_back(*this, &PlayersDB::hide, this->disconnector_);
205 } // void PlayersDB::init()
206 
207 /** the name of 'player' has changed
208  **
209  ** @param    player   the player with the changed name
210  **/
211 void
name_changed(Player const & player)212 PlayersDB::name_changed(Player const& player)
213 {
214   if (!this->get_realized())
215     return ;
216 
217   this->players_db_treeview->get_column(this->ui->party().players().no(player) + 1
218                                        )->set_title(" " + player.name() + " ");
219 } // void PlayersDB::name_changed(Player player)
220 
221 /** recreates the table
222  **/
223 void
recreate_db()224 PlayersDB::recreate_db()
225 {
226   if (!this->get_realized())
227     return ;
228 
229   if (!this->players_db_list)
230     return ;
231 
232   this->players_db_list->clear();
233 
234   { // ranking
235     this->ranking = *this->players_db_list->append();
236     this->ranking[this->players_db_model->type]
237       = _("ranking");
238   } // ranking
239 
240   { // average game points
241     this->average_game_points = *this->players_db_list->append();
242     this->average_game_points[this->players_db_model->type]
243       = _("average game points");
244   } // average game points
245 
246   { // average game trick points
247     this->average_game_trick_points = *this->players_db_list->append();
248     this->average_game_trick_points[this->players_db_model->type]
249       = _("average game trick points");
250   } // average game trick points
251 
252   {
253     for (unsigned p = 0; p < 4; ++p) {
254       this->ranking[this->players_db_model->statistic[p]]
255         = this->statistic_data(TLWcount());
256     }
257   }
258 
259   { // game type
260     this->games = *this->players_db_list->append();
261     this->games[this->players_db_model->type]
262       = _("games");
263     this->games_marriage
264       = *(this->players_db_list->append(this->games.children()));
265     this->games_marriage[this->players_db_model->type]
266       = _("GameType::Group::marriage");
267     this->games_poverty
268       = *(this->players_db_list->append(this->games.children()));
269     this->games_poverty[this->players_db_model->type]
270       = _("GameType::Group::poverty");
271     this->games_solo
272       = *(this->players_db_list->append(this->games.children()));
273     this->games_solo[this->players_db_model->type]
274       = _("GameType::Group::solo");
275     this->games_solo_color
276       = *(this->players_db_list->append(this->games_solo.children()));
277     this->games_solo_color[this->players_db_model->type]
278       = _("GameType::Group::Solo::color");
279     this->games_solo_picture
280       = *(this->players_db_list->append(this->games_solo.children()));
281     this->games_solo_picture[this->players_db_model->type]
282       = _("GameType::Group::Solo::picture");
283     this->games_solo_picture_single
284       = *(this->players_db_list->append(this->games_solo_picture.children()));
285     this->games_solo_picture_single[this->players_db_model->type]
286       = _("GameType::Group::Solo::Picture::single");
287     this->games_solo_picture_double
288       = *(this->players_db_list->append(this->games_solo_picture.children()));
289     this->games_solo_picture_double[this->players_db_model->type]
290       = _("GameType::Group::Solo::Picture::double");
291 
292     this->game.clear();
293     for (auto gt : game_type_list) {
294       switch (gt) {
295       case GameType::normal:
296         this->game.push_back(*this->players_db_list->prepend(this->games.children()));
297         break;
298       case GameType::thrown_nines:
299       case GameType::thrown_kings:
300       case GameType::thrown_nines_and_kings:
301       case GameType::thrown_richness:
302       case GameType::fox_highest_trump:
303       case GameType::redistribute:
304       case GameType::poverty:
305         this->game.push_back(*this->players_db_list->append(this->games_poverty.children()));
306         break;
307       case GameType::genscher:
308       case GameType::marriage:
309       case GameType::marriage_solo:
310       case GameType::marriage_silent:
311         this->game.push_back(*this->players_db_list->append(this->games_marriage.children()));
312         break;
313       case GameType::solo_jack:
314       case GameType::solo_queen:
315         this->game.push_back(*this->players_db_list->append(this->games_solo_picture_single.children()));
316         break;
317       case GameType::solo_king:
318       case GameType::solo_queen_jack:
319       case GameType::solo_king_jack:
320       case GameType::solo_king_queen:
321         this->game.push_back(*this->players_db_list->append(this->games_solo_picture_double.children()));
322         break;
323       case GameType::solo_koehler:
324         this->game.push_back(*this->players_db_list->append(this->games_solo_picture.children()));
325         break;
326       case GameType::solo_club:
327       case GameType::solo_heart:
328       case GameType::solo_spade:
329       case GameType::solo_diamond:
330         this->game.push_back(*this->players_db_list->append(this->games_solo_color.children()));
331         break;
332       case GameType::solo_meatless:
333         this->game.push_back(*this->players_db_list->append(this->games_solo.children()));
334         break;
335 #if 0
336       default:
337         DEBUG_ASSERTION(false,
338                         "UI_GTKMM::PlayersDB::recreate_db()\n"
339                         "  game type '" << static_cast<GameType>(gt) << "' is not considered");
340         break;
341 #endif
342       } // switch(gt)
343       this->game.back()[this->players_db_model->type] = _(gt);
344     } // for (gt \in GameType)
345   } // game type
346 
347   { // special points
348     this->specialpoints = *this->players_db_list->append();
349     this->specialpoints[this->players_db_model->type]
350       = _("Specialpoint::specialpoints");
351     this->specialpoints_winning
352       = *this->players_db_list->append(this->specialpoints.children());
353     this->specialpoints_winning[this->players_db_model->type]
354       = _("Specialpoint::Group::winning");
355     this->specialpoints_announcement
356       = *this->players_db_list->append(this->specialpoints.children());
357     this->specialpoints_announcement[this->players_db_model->type]
358       = _("Specialpoint::Group::announcement");
359 
360     this->specialpoint.clear();
361     for (auto sp : Specialpoint::types) {
362       switch (sp) {
363       case Specialpoint::Type::nospecialpoint:
364       case Specialpoint::Type::caught_fox:
365       case Specialpoint::Type::caught_fox_last_trick:
366       case Specialpoint::Type::fox_last_trick:
367       case Specialpoint::Type::charlie:
368       case Specialpoint::Type::caught_charlie:
369       case Specialpoint::Type::dulle_caught_dulle:
370       case Specialpoint::Type::heart_trick:
371       case Specialpoint::Type::doppelkopf:
372         this->specialpoint[sp] = *this->players_db_list->prepend(this->specialpoints.children());
373         break;
374       case Specialpoint::Type::won:
375       case Specialpoint::Type::no90:
376       case Specialpoint::Type::no60:
377       case Specialpoint::Type::no30:
378       case Specialpoint::Type::no0:
379       case Specialpoint::Type::contra_won:
380       case Specialpoint::Type::solo:
381       case Specialpoint::Type::bock:
382         this->specialpoint[sp] = *this->players_db_list->append(this->specialpoints_winning.children());
383         break;
384       case Specialpoint::Type::no120_said:
385       case Specialpoint::Type::no90_said:
386       case Specialpoint::Type::no60_said:
387       case Specialpoint::Type::no30_said:
388       case Specialpoint::Type::no0_said:
389       case Specialpoint::Type::no90_said_120_got:
390       case Specialpoint::Type::no60_said_90_got:
391       case Specialpoint::Type::no30_said_60_got:
392       case Specialpoint::Type::no0_said_30_got:
393       case Specialpoint::Type::no120_reply:
394       case Specialpoint::Type::no90_reply:
395       case Specialpoint::Type::no60_reply:
396       case Specialpoint::Type::no30_reply:
397       case Specialpoint::Type::no0_reply:
398         this->specialpoint[sp] = *this->players_db_list->append(this->specialpoints_announcement.children());
399         break;
400       } // switch(sp)
401       this->specialpoint[sp][this->players_db_model->type] = _(sp);
402     } // for (sp \in Specialpoint)
403   } // special points
404 
405   { // heuristics
406     this->heuristics = *this->players_db_list->append();
407     this->heuristics[this->players_db_model->type]
408       = _("AiConfig::Group::heuristics");
409     this->heuristics_general
410       = *this->players_db_list->append(this->heuristics.children());
411     this->heuristics_general[this->players_db_model->type]
412       = _("AiConfig::Heuristic::Group::normal game");
413     this->heuristics_poverty
414       = *this->players_db_list->append(this->heuristics.children());
415     this->heuristics_poverty[this->players_db_model->type]
416       = _("AiConfig::Heuristic::Group::poverty");
417     this->heuristics_solo
418       = *this->players_db_list->append(this->heuristics.children());
419     this->heuristics_solo[this->players_db_model->type]
420       = _("AiConfig::Heuristic::Group::solo");
421     this->heuristics_solo_color
422       = *this->players_db_list->append(this->heuristics_solo.children());
423     this->heuristics_solo_color[this->players_db_model->type]
424       = _("AiConfig::Heuristic::Group::Solo::color");
425     this->heuristics_solo_picture
426       = *this->players_db_list->append(this->heuristics_solo.children());
427     this->heuristics_solo_picture[this->players_db_model->type]
428       = _("AiConfig::Heuristic::Group::Solo::picture");
429     this->heuristics_solo_meatless
430       = *this->players_db_list->append(this->heuristics_solo.children());
431     this->heuristics_solo_meatless[this->players_db_model->type]
432       = _("AiConfig::Heuristic::Group::Solo::meatless");
433 
434     this->heuristic.clear();
435     for (auto const heuristic : aiconfig_heuristic_list) {
436       switch (heuristic) {
437       case Aiconfig::Heuristic::error:
438         break;
439       case Aiconfig::Heuristic::no_heuristic:
440       case Aiconfig::Heuristic::manual:
441       case Aiconfig::Heuristic::bug_report:
442         this->heuristic.push_back(this->heuristics);
443         break;
444       case Aiconfig::Heuristic::only_one_valid_card:
445         this->heuristic.push_back(*this->players_db_list->prepend(this->heuristics.children()));
446         break;
447       case Aiconfig::Heuristic::choose_best_card_in_last_tricks:
448       case Aiconfig::Heuristic::choose_best_card:
449         this->heuristic.push_back(*this->players_db_list->prepend(this->heuristics.children()));
450         std::swap(this->heuristic[static_cast<int>(Aiconfig::Heuristic::only_one_valid_card)],
451                   this->heuristic.back());
452         break;
453       case Aiconfig::Heuristic::start_with_color_solo_ace:
454       case Aiconfig::Heuristic::start_with_color_single_ace:
455       case Aiconfig::Heuristic::start_with_color_double_ace:
456       case Aiconfig::Heuristic::jab_with_color_ace:
457       case Aiconfig::Heuristic::choose_ten:
458       case Aiconfig::Heuristic::play_to_marry:
459       case Aiconfig::Heuristic::play_to_get_married:
460       case Aiconfig::Heuristic::choose_for_color_trick:
461       case Aiconfig::Heuristic::jab_color_over_fox:
462       case Aiconfig::Heuristic::start_with_color:
463       case Aiconfig::Heuristic::start_with_low_color:
464       case Aiconfig::Heuristic::start_with_low_trump:
465       case Aiconfig::Heuristic::retry_color:
466       case Aiconfig::Heuristic::play_color_for_partner:
467       case Aiconfig::Heuristic::try_color_for_partner:
468       case Aiconfig::Heuristic::play_color_for_partner_ace:
469       case Aiconfig::Heuristic::play_bad_color:
470       case Aiconfig::Heuristic::jab_color_trick_with_dulle:
471       case Aiconfig::Heuristic::save_dulle:
472       case Aiconfig::Heuristic::pfund_in_first_color_run:
473       case Aiconfig::Heuristic::serve_color_trick:
474       case Aiconfig::Heuristic::serve_trump_trick:
475       case Aiconfig::Heuristic::choose_pfund:
476       case Aiconfig::Heuristic::choose_pfund_before_partner:
477       case Aiconfig::Heuristic::jab_for_ace:
478       case Aiconfig::Heuristic::create_fehl:
479       case Aiconfig::Heuristic::best_winning:
480       case Aiconfig::Heuristic::low_high:
481       case Aiconfig::Heuristic::play_trump:
482       case Aiconfig::Heuristic::play_for_team:
483       case Aiconfig::Heuristic::jab_fox:
484       case Aiconfig::Heuristic::jab_for_doppelkopf:
485       case Aiconfig::Heuristic::try_for_doppelkopf:
486       case Aiconfig::Heuristic::play_for_partner_worries:
487       case Aiconfig::Heuristic::draw_trump:
488       case Aiconfig::Heuristic::play_to_jab_later:
489       case Aiconfig::Heuristic::partner_backhand_draw_trump:
490       case Aiconfig::Heuristic::play_highest_color_card_in_game:
491       case Aiconfig::Heuristic::jab_to_win:
492       case Aiconfig::Heuristic::grab_trick:
493       case Aiconfig::Heuristic::last_player_pass_small_trick:
494       case Aiconfig::Heuristic::cannot_jab:
495         this->heuristic.push_back(*this->players_db_list->append(this->heuristics_general.children()));
496         break;
497       case Aiconfig::Heuristic::color_jab_for_ace:
498       case Aiconfig::Heuristic::color_low_high:
499       case Aiconfig::Heuristic::play_color_in_solo:
500         this->heuristic.push_back(*this->players_db_list->append(this->heuristics_solo_color.children()));
501         break;
502       case Aiconfig::Heuristic::choose_pfund_poverty:
503       case Aiconfig::Heuristic::poverty_special_play_pfund:
504       case Aiconfig::Heuristic::poverty_special_give_no_points:
505       case Aiconfig::Heuristic::poverty_special_offer_pfund:
506       case Aiconfig::Heuristic::poverty_re_trump_color_trick_high:
507       case Aiconfig::Heuristic::poverty_re_play_trump:
508       case Aiconfig::Heuristic::poverty_contra_play_color:
509       case Aiconfig::Heuristic::poverty_contra_trump_color_trick_high:
510       case Aiconfig::Heuristic::poverty_leave_to_partner:
511       case Aiconfig::Heuristic::poverty_overjab_re:
512       case Aiconfig::Heuristic::poverty_best_winning_card:
513         this->heuristic.push_back(*this->players_db_list->append(this->heuristics_poverty.children()));
514         break;
515       case Aiconfig::Heuristic::picture_pull_down_color:
516       case Aiconfig::Heuristic::picture_start_with_highest_color:
517       case Aiconfig::Heuristic::picture_get_last_trumps:
518       case Aiconfig::Heuristic::picture_draw_trumps:
519       case Aiconfig::Heuristic::picture_play_last_trumps:
520       case Aiconfig::Heuristic::picture_jab_color_trick_for_sure:
521         this->heuristic.push_back(*this->players_db_list->append(this->heuristics_solo_picture.children()));
522       case Aiconfig::Heuristic::meatless_start_with_best_color:
523       case Aiconfig::Heuristic::meatless_retry_last_color:
524         this->heuristic.push_back(*this->players_db_list->append(this->heuristics_solo_meatless.children()));
525         break;
526       } // switch(h)
527       this->heuristic.back()[this->players_db_model->type]
528         = _(heuristic);
529     } // for (h \in Aiconfig::Heuristic)
530   } // heuristics
531 
532   this->update_db();
533 
534   this->players_db_treeview->show_all();
535 } // void PlayersDB::recreate_db()
536 
537 /** updates the statistics
538  **/
539 void
update_db()540 PlayersDB::update_db()
541 {
542   if (!this->get_realized())
543     return ;
544 
545   if (!this->players_db_list)
546     return ;
547 
548   Party const& party = this->ui->party();
549 
550   for (unsigned p = 0; p < party.players().size(); ++p) {
551     auto const& player = this->ui->party().players()[p];
552     auto const& db = player.db();
553 
554     this->ranking[this->players_db_model->statistic[p]]
555       = String::to_string(static_cast<int>(db.rank.value() * 10) / 10.0);
556 
557     this->average_game_points[this->players_db_model->statistic[p]]
558       = String::to_string(static_cast<int>(db.averageGamePoints() * 10) / 10.0);
559 
560     this->average_game_trick_points[this->players_db_model->statistic[p]]
561       = String::to_string(static_cast<int>(db.averageGameTrickPoints() * 10) / 10.0);
562 
563     { // game type
564       this->games[this->players_db_model->statistic[p]]
565         = this->statistic_data(db.games_all());
566       this->games_marriage[this->players_db_model->statistic[p]]
567         = this->statistic_data(db.games_group_marriage());
568       this->games_poverty[this->players_db_model->statistic[p]]
569         = this->statistic_data(db.games_group_poverty());
570       this->games_solo[this->players_db_model->statistic[p]]
571         = this->statistic_data(db.games_group_solo());
572       this->games_solo_color[this->players_db_model->statistic[p]]
573         = this->statistic_data(db.games_group_solo_color());
574       this->games_solo_picture[this->players_db_model->statistic[p]]
575         = this->statistic_data(db.games_group_solo_picture());
576       this->games_solo_picture_single[this->players_db_model->statistic[p]]
577         = this->statistic_data(db.games_group_solo_picture_single());
578       this->games_solo_picture_double[this->players_db_model->statistic[p]]
579         = this->statistic_data(db.games_group_solo_picture_double());
580       for (auto gt : game_type_list) {
581         this->game[static_cast<int>(gt)][this->players_db_model->statistic[p]]
582           = this->statistic_data(db.games(gt));
583       } // for (gt \in gameTYPE)
584     } // game type
585 
586     { // special points
587       this->specialpoints[this->players_db_model->statistic[p]]
588         = this->statistic_data(db.specialpoints_all());
589       this->specialpoints_winning[this->players_db_model->statistic[p]]
590         = this->statistic_data(db.specialpoints_group_winning());
591       this->specialpoints_announcement[this->players_db_model->statistic[p]]
592         = this->statistic_data(db.specialpoints_group_announcement());
593       for (auto sp : Specialpoint::types) {
594         this->specialpoint[sp][this->players_db_model->statistic[p]]
595           = this->statistic_data(db.specialpoints(sp));
596       } // for (sp : Specialpoint::types)
597     } // special points
598     { // heuristics
599       if (dynamic_cast<AiDb const*>(&db) == nullptr)
600         continue;
601       auto const& aidb = dynamic_cast<AiDb const&>(db);
602       auto const total = aidb.heuristic(Aiconfig::Heuristic::no_heuristic);
603 
604       this->heuristics[this->players_db_model->statistic[p]]
605         = this->heuristic_statistic_data(total, total);
606       this->heuristics_general[this->players_db_model->statistic[p]]
607         = this->heuristic_statistic_data(total, aidb.heuristics_group_general());
608       this->heuristics_poverty[this->players_db_model->statistic[p]]
609         = this->heuristic_statistic_data(total, aidb.heuristics_group_poverty());
610       this->heuristics_solo[this->players_db_model->statistic[p]]
611         = this->heuristic_statistic_data(total, aidb.heuristics_group_solo());
612       this->heuristics_solo_color[this->players_db_model->statistic[p]]
613         = this->heuristic_statistic_data(total, aidb.heuristics_group_solo_color());
614       this->heuristics_solo_picture[this->players_db_model->statistic[p]]
615         = this->heuristic_statistic_data(total, aidb.heuristics_group_solo_picture());
616       this->heuristics_solo_meatless[this->players_db_model->statistic[p]]
617         = this->heuristic_statistic_data(total, aidb.heuristics_group_solo_meatless());
618       for (auto const heuristic : aiconfig_heuristic_list) {
619         if (this->heuristic[static_cast<int>(heuristic)] == this->heuristics)
620           continue;
621         this->heuristic[static_cast<int>(heuristic)][this->players_db_model->statistic[p]]
622           = this->heuristic_statistic_data(total, aidb.heuristic(heuristic));
623       } // for (h \in Heuristic)
624     } // heuristics
625   } // for (p < party.players().size())
626 } // void PlayersDB::update_db()
627 
628 /** sets the statistic
629  **
630  ** @param    statistic   new statistic
631  **/
632 void
set_statistic(Statistic const statistic)633 PlayersDB::set_statistic(Statistic const statistic)
634 {
635   this->statistic = statistic;
636   this->update_db();
637 } // void PlayersDB::set_statistic(Statistic const statistic)
638 
639 /** clears the database
640  **
641  ** @todo    remove 'const cast'
642  **/
643 void
clear_db()644 PlayersDB::clear_db()
645 {
646   for (auto& p : this->ui->party().players())
647     p.db().clear();
648 
649   this->update_db();
650 } // void PlayersDB::clear_db()
651 
652 /** -> result
653  **
654  ** @param    tlwcount   counter
655  **
656  ** @return   string of the data analysed by 'this->statistic'
657  **/
658 Glib::ustring
statistic_data(TLWcount const & tlwcount) const659 PlayersDB::statistic_data(TLWcount const& tlwcount) const
660 {
661   switch(this->statistic) {
662   case Statistic::total:
663     return String::to_string(tlwcount.total());
664   case Statistic::won:
665     if (tlwcount.total() > 0)
666       return String::to_string(tlwcount.won());
667     else
668       return "-";
669   case Statistic::lost:
670     if (tlwcount.total() > 0)
671       return String::to_string(tlwcount.lost());
672     else
673       return "-";
674   case Statistic::percent_won:
675     if (tlwcount.total() > 0)
676       return (String::to_string(tlwcount.won() * 100
677                                 / tlwcount.total())
678               + "%");
679     else
680       return "-";
681   } // switch(this->statistic)
682 
683   return "-";
684 } // void PlayersDB::statistic_data(TLWcout tlwcount) const
685 
686 /** -> result
687  **
688  ** @param    total   total number of heuristics
689  ** @param    num     number of the active heuristics
690  **
691  ** @return   string of the data analysed by 'this->statistic'
692  **/
693 Glib::ustring
heuristic_statistic_data(unsigned const total,unsigned const num) const694 PlayersDB::heuristic_statistic_data(unsigned const total,
695                                     unsigned const num) const
696 {
697   if (num == 0)
698     return "-";
699 
700   switch(this->statistic) {
701   case Statistic::total:
702   case Statistic::won:
703   case Statistic::lost:
704     return String::to_string(num);
705   case Statistic::percent_won:
706     return (String::to_string((num * 100 / total))
707             + "."
708             + String::to_string((num * 1000 / total) % 10)
709             + "%");
710   } // switch(this->statistic)
711 
712   return "-";
713 } // void PlayersDB::heuristic_statistic_data(unsigned total, unsigned num) const
714 
715 /** a key has been pressed
716  ** C-o: output of the statistics on 'stdout'
717  **
718  ** @param    key   the key
719  **
720  ** @return   whether the key was managed
721  **/
722 bool
on_key_press_event(GdkEventKey * const key)723 PlayersDB::on_key_press_event(GdkEventKey* const key)
724 {
725   bool managed = false;
726 
727   if ((key->state & ~GDK_SHIFT_MASK) == GDK_CONTROL_MASK) {
728     switch (key->keyval) {
729     case GDK_KEY_o: // ouput of the preferences
730       for (auto& p : this->ui->party().players()) {
731         cout << p.name() << '\n'
732           << "{\n"
733           <<   p.db()
734           << "}\n";
735       }
736       managed = true;
737       break;
738     } // switch (key->keyval)
739   } // if (key->state == GDK_CONTROL_MASK)
740 
741   return (managed
742           || this->StickyDialog::on_key_press_event(key)
743           || this->ui->key_press(key));
744 } // bool PlayersDB::on_key_press_event(GdkEventKey* key)
745 
746 } // namespace UI_GTKMM_NS
747 
748 #endif // #ifdef USE_UI_GTKMM
749