1 /****************************************************************************
2 * Copyright (C) 2012 by Jens Nissen jens-chessx@gmx.net *
3 ****************************************************************************/
4
5 #include <QCoreApplication>
6 #include <QHash>
7
8 #include "ecoinfo.h"
9 #include "database.h"
10 #include "tags.h"
11
12 #if defined(_MSC_VER) && defined(_DEBUG)
13 #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
14 #define new DEBUG_NEW
15 #endif // _MSC_VER
16
sortPlayersLt(const PlayerInfoListItem & a1,const PlayerInfoListItem & a2)17 static bool sortPlayersLt(const PlayerInfoListItem& a1, const PlayerInfoListItem& a2)
18 {
19 if(a1.second == a2.second)
20 {
21 return (a1.first < a2.first);
22 }
23 return a1.second > a2.second;
24 }
25
EcoInfo()26 EcoInfo::EcoInfo()
27 {
28 m_database = nullptr;
29 reset();
30 }
31
~EcoInfo()32 EcoInfo::~EcoInfo()
33 {
34 }
35
EcoInfo(Database * db,const QString & eco)36 EcoInfo::EcoInfo(Database* db, const QString & eco)
37 {
38 setDatabase(db);
39 setCode(eco);
40 reset();
41 update();
42 }
43
name() const44 QString EcoInfo::name() const
45 {
46 return m_code;
47 }
48
setDatabase(Database * db)49 void EcoInfo::setDatabase(Database* db)
50 {
51 m_database = db;
52 }
53
setCode(const QString & eco)54 void EcoInfo::setCode(const QString& eco)
55 {
56 m_code = eco;
57 update();
58 }
59
toResult(const QString & res) const60 int EcoInfo::toResult(const QString& res) const
61 {
62 if(res.startsWith("1/2"))
63 {
64 return Draw;
65 }
66 else if(res.startsWith('1'))
67 {
68 return WhiteWin;
69 }
70 else if(res.startsWith('0'))
71 {
72 return BlackWin;
73 }
74 else
75 {
76 return ResultUnknown;
77 }
78 }
79
toPoints(const QString & res) const80 float EcoInfo::toPoints(const QString& res) const
81 {
82 if(res.startsWith("1/2"))
83 {
84 return 0.5;
85 }
86 else if(res.startsWith('1'))
87 {
88 return 1.0;
89 }
90 else if(res.startsWith('0'))
91 {
92 return 0.0;
93 }
94 else
95 {
96 return -1.0;
97 }
98 }
99
update()100 void EcoInfo::update()
101 {
102 QHash<QString, float> playersWhite;
103 QHash<QString, float> playersBlack;
104
105 const IndexX* index = m_database->index();
106
107 // Determine matching tag values
108 ValueIndex eco = index->getValueIndex(m_code);
109
110 // Clean previous statistics
111 reset();
112
113 for(int i = 0; i < (int) m_database->count(); ++i)
114 {
115 if(index->valueIndexFromTag(TagNameECO, i) != eco)
116 {
117 continue;
118 }
119 QString result = index->tagValue(TagNameResult, i);
120 int res = toResult(result);
121 QString whitePlayer = index->tagValue(TagNameWhite, i);
122 QString blackPlayer = index->tagValue(TagNameBlack, i);
123 // The following works as QHash initializes a default-constructed value to 0
124 float fres = toPoints(result);
125 if(fres >= 0)
126 {
127 playersWhite[whitePlayer] += fres;
128 playersBlack[blackPlayer] += (1.0 - fres);
129 m_gamesWhite[whitePlayer]++;
130 m_gamesBlack[blackPlayer]++;
131 }
132 else
133 {
134 // This looks silly, but the []operator has a side effect!
135 if(playersWhite[whitePlayer] == 0)
136 {
137 playersWhite[whitePlayer] = 0;
138 }
139 if(playersBlack[blackPlayer] == 0)
140 {
141 playersBlack[blackPlayer] = 0;
142 }
143 }
144
145 m_result[res]++;
146 m_count++;
147 }
148
149 for (auto it = playersWhite.cbegin(); it != playersWhite.cend(); ++it)
150 {
151 m_playersWhite.append(PlayerInfoListItem(it.key(), it.value()));
152 }
153 std::sort(m_playersWhite.begin(), m_playersWhite.end(), sortPlayersLt);
154
155 for (auto it = playersBlack.cbegin(); it != playersBlack.cend(); ++it)
156 {
157 m_playersBlack.append(PlayerInfoListItem(it.key(), it.value()));
158 }
159 std::sort(m_playersBlack.begin(), m_playersBlack.end(), sortPlayersLt);
160 }
161
162
formattedScore(const int result[4],int count) const163 QString EcoInfo::formattedScore(const int result[4], int count) const
164 {
165 if(!count)
166 {
167 return QCoreApplication::translate("EcoInfo", "<i>no games</i>");
168 }
169 QString score = "<b>";
170 QChar scoresign[4] = {'*', '+', '=', '-'};
171 QStringList results;
172 results << "\\*" << "1-0" << "1/2-1/2" << "0-1";
173 int order[] = { WhiteWin, Draw, BlackWin, ResultUnknown };
174 QString format = QString("<a href='result:%1'> %2%3</a>");
175
176 for(int j=0;j<4;j++)
177 {
178 int i = order[j];
179 score += format.arg(results[i]).arg(scoresign[i]).arg(result[i]);
180 }
181 int n = count - result[ResultUnknown];
182 if(n)
183 {
184 score += QString(" (%1%)").arg((100.0 * result[WhiteWin] + 50.0 * result[Draw]) / (n), 1, 'f', 1);
185 }
186 score += "</b>";
187 return score;
188 }
189
190
formattedScore() const191 QString EcoInfo::formattedScore() const
192 {
193 return tr("Total: %1").arg(formattedScore(m_result, m_count));
194 }
195
reset()196 void EcoInfo::reset()
197 {
198 for(int c = White; c <= Black; ++c)
199 {
200 for(int r = 0; r < 4; ++r)
201 {
202 m_result[r] = 0;
203 }
204 }
205 m_playersWhite.clear();
206 m_playersBlack.clear();
207 m_gamesWhite.clear();
208 m_gamesBlack.clear();
209 m_count = 0;
210 m_rating[0] = 99999;
211 m_rating[1] = 0;
212 }
213
formattedGameCount() const214 QString EcoInfo::formattedGameCount() const
215 {
216 return QCoreApplication::translate("EcoInfo", "Games in database %1: %2")
217 .arg(m_database->name()).arg(m_count);
218 }
219
formattedRating() const220 QString EcoInfo::formattedRating() const
221 {
222 if(!m_rating[1])
223 {
224 return QString();
225 }
226 else if(m_rating[0] == m_rating[1])
227 {
228 return QCoreApplication::translate("EcoInfo", "Rating: <b>%1</b>").arg(m_rating[0]);
229 }
230 else
231 return QCoreApplication::translate("EcoInfo", "Rating: <b>%1-%2</b>")
232 .arg(m_rating[0]).arg(m_rating[1]);
233 }
234
listOfPlayers() const235 QString EcoInfo::listOfPlayers() const
236 {
237 QString playersList;
238
239 playersList.append(QString("<a name='ListWhite'></a><table><tr><th><a href=\"#ListBlack\">⇩</a>%1</th><th>%2</th></tr>").arg(tr("White Player"), tr("Score")));
240
241 for(PlayerInfoList::const_iterator it = m_playersWhite.constBegin(); it != m_playersWhite.constEnd(); ++it)
242 {
243 playersList += QString("<tr><td><a href=\"player-white:%1\">%2</a></td><td>%3/%4</td></tr>")
244 .arg((*it).first, (*it).first)
245 .arg((*it).second)
246 .arg(m_gamesWhite[(*it).first]);
247 }
248
249 playersList = playersList.append("</table>");
250
251 playersList.append(QString("<a name='ListBlack'></a><table><tr><th><a href=\"#ListWhite\">⇧</a>%1</th><th>%2</th></tr>").arg(tr("Black Player"), tr("Score")));
252
253 for(PlayerInfoList::const_iterator it = m_playersBlack.begin(); it != m_playersBlack.end(); ++it)
254 {
255 playersList += QString("<tr><td><a href=\"player-black:%1\">%2</a></td><td>%3/%4</td></tr>")
256 .arg((*it).first, (*it).first)
257 .arg((*it).second)
258 .arg(m_gamesBlack[(*it).first]);
259 }
260
261 playersList = playersList.append("</table>");
262
263 return playersList;
264 }
265