~ubuntu-branches/debian/sid/chessx/sid

« back to all changes in this revision

Viewing changes to src/database/openingtree.cpp

  • Committer: Package Import Robot
  • Author(s): Niklas Fiekas
  • Date: 2013-10-31 17:02:37 UTC
  • Revision ID: package-import@ubuntu.com-20131031170237-wghf5j9jlv28gmls
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   (C) 2007-2009 Michal Rudolf <mrudolf@kdewebdev.org>                  *
 
3
 *                                                                         *
 
4
 *   This program is free software; you can redistribute it and/or modify  *
 
5
 *   it under the terms of the GNU General Public License as published by  *
 
6
 *   the Free Software Foundation; either version 2 of the License, or     *
 
7
 *   (at your option) any later version.                                   *
 
8
 ***************************************************************************/
 
9
 
 
10
#include "openingtree.h"
 
11
#include "common.h"
 
12
#include "database.h"
 
13
 
 
14
#include <QtCore>
 
15
 
 
16
const unsigned MinAveYear = 1;
 
17
const unsigned MinAveRating = 5;
 
18
 
 
19
MoveData::MoveData()
 
20
{
 
21
    count = 0;
 
22
    for(int  r = ResultUnknown; r <= BlackWin; ++r)
 
23
    {
 
24
        result[r] = 0;
 
25
    }
 
26
    year = rating = 0;
 
27
    dated = rated = 0;
 
28
 
 
29
}
 
30
 
 
31
void MoveData::addGame(Game& g, Color c, MoveType movetype)
 
32
{
 
33
    if(!count)
 
34
        move = (movetype == StandardMove) ? g.moveToSan(Game::MoveOnly, Game::PreviousMove)
 
35
               : qApp->translate("MoveData", "[end]");
 
36
    ++count;
 
37
    result[g.result()]++;
 
38
    unsigned elo = (c == White) ? g.tag("WhiteElo").toInt() : g.tag("BlackElo").toInt();
 
39
    if(elo >= 1000)
 
40
    {
 
41
        rating += elo;
 
42
        ++rated;
 
43
    }
 
44
    unsigned y = g.tag("Date").section(".", 0, 0).toInt();
 
45
    if(y > 1000)
 
46
    {
 
47
        year += y;
 
48
        ++dated;
 
49
    }
 
50
}
 
51
 
 
52
double MoveData::percentage() const
 
53
{
 
54
    unsigned c = result[ResultUnknown] + 2 * result[WhiteWin] + result[Draw];
 
55
    return c * 500 / count / 10.0;
 
56
}
 
57
 
 
58
int MoveData::averageRating() const
 
59
{
 
60
    return rated ? rating / rated : 0;
 
61
}
 
62
 
 
63
int MoveData::averageYear() const
 
64
{
 
65
    return dated ? year / dated : 0;
 
66
}
 
67
 
 
68
bool operator<(const MoveData& m1, const MoveData& m2)
 
69
{
 
70
    return m1.count < m2.count || (m1.count == m2.count && m1.move < m2.move);
 
71
}
 
72
 
 
73
bool compareMove(const MoveData& m1, const MoveData& m2)
 
74
{
 
75
    return m1.move < m2.move;
 
76
}
 
77
 
 
78
bool compareScore(const MoveData& m1, const MoveData& m2)
 
79
{
 
80
    return m1.percentage() < m2.percentage() ||
 
81
           (m1.percentage() == m2.percentage() && m1.move < m2.move);
 
82
}
 
83
 
 
84
bool compareRating(const MoveData& m1, const MoveData& m2)
 
85
{
 
86
    return m1.averageRating() < m2.averageRating() ||
 
87
           (m1.averageRating() == m2.averageRating() && m1.move < m2.move);
 
88
}
 
89
 
 
90
bool compareYear(const MoveData& m1, const MoveData& m2)
 
91
{
 
92
    return m1.averageYear() < m2.averageYear() ||
 
93
           (m1.averageYear() == m2.averageYear() && m1.move < m2.move);
 
94
}
 
95
 
 
96
OpeningTreeUpdater oupd;
 
97
 
 
98
void OpeningTreeUpdater::run()
 
99
{
 
100
    Game g;
 
101
    QMap<Move, MoveData> moves;
 
102
    int games = 0;
 
103
    for(int i = 0; i < m_filter->size(); ++i)
 
104
    {
 
105
        m_filter->database()->lock();
 
106
        m_filter->database()->loadGameMoves(i, g);
 
107
        m_filter->database()->unlock();
 
108
        int id = g.findPosition(m_board);
 
109
        if((id != NO_MOVE) && (m_bEnd ? g.atGameEnd(id) : true))
 
110
        {
 
111
            if(m_updateFilter)
 
112
            {
 
113
                m_filter->set(i, id + 1); // not zero means success, but id could be 0.
 
114
            }
 
115
            m_filter->database()->lock();
 
116
            m_filter->database()->loadGameHeaders(i, g);
 
117
            m_filter->database()->unlock();
 
118
            g.moveToId(id);
 
119
            if(g.atGameEnd())
 
120
            {
 
121
                moves[Move()].addGame(g, m_board.toMove(), MoveData::GameEnd);
 
122
            }
 
123
            else
 
124
            {
 
125
                g.forward();
 
126
                moves[g.move()].addGame(g, m_board.toMove());
 
127
            }
 
128
            ++games;
 
129
        }
 
130
        else
 
131
        {
 
132
            if(m_updateFilter)
 
133
            {
 
134
                m_filter->set(i, 0);
 
135
            }
 
136
        }
 
137
        if(i * 100 / m_filter->size() > (i - 1) * 100 / m_filter->size())
 
138
        {
 
139
            emit progress(i * 100 / m_filter->size());
 
140
        }
 
141
        if(m_break)
 
142
        {
 
143
            break;
 
144
        }
 
145
    }
 
146
    *m_games = games;
 
147
    m_moves->clear();
 
148
    if(!m_break)
 
149
    {
 
150
        for(QMap<Move, MoveData>::iterator it = moves.begin(); it != moves.end(); ++it)
 
151
        {
 
152
            m_moves->append(it.value());
 
153
        }
 
154
        qSort(m_moves->begin(), m_moves->end());
 
155
        emit UpdateFinished(&m_board);
 
156
    }
 
157
    else
 
158
    {
 
159
        emit UpdateTerminated(&m_board);
 
160
    }
 
161
}
 
162
 
 
163
void OpeningTreeUpdater::cancel()
 
164
{
 
165
    m_break = true;
 
166
}
 
167
 
 
168
bool OpeningTreeUpdater::updateFilter(Filter& f, const Board& b, QList<MoveData>& m, int& g, bool updateFilter, bool bEnd)
 
169
{
 
170
    m_break = false;
 
171
    m_filter = &f;
 
172
    m_board = b;
 
173
    m_moves = &m;
 
174
    m_games = &g;
 
175
    m_bEnd  = bEnd;
 
176
    m_updateFilter = updateFilter;
 
177
    // todo: if running wait for stop
 
178
    start();
 
179
    return true;
 
180
}
 
181
 
 
182
bool OpeningTree::updateFilter(Filter& f, const Board& b, bool updateFilter, bool bEnd)
 
183
{
 
184
    if(!oupd.isRunning())
 
185
    {
 
186
        if(&f == m_filter && b == m_board && m_bEnd == bEnd)
 
187
        {
 
188
            return true;
 
189
        }
 
190
        m_bEnd = bEnd;
 
191
        m_board = b;
 
192
        m_filter = &f;
 
193
        m_updateFilter = updateFilter;
 
194
        emit openingTreeUpdateStarted();
 
195
        m_bRequestPending = false;
 
196
        connect(&oupd, SIGNAL(UpdateFinished(Board*)), this, SLOT(updateFinished(Board*)), Qt::UniqueConnection);
 
197
        connect(&oupd, SIGNAL(UpdateTerminated(Board*)), this, SLOT(updateTerminated(Board*)), Qt::UniqueConnection);
 
198
        connect(&oupd, SIGNAL(progress(int)), SIGNAL(progress(int)), Qt::UniqueConnection);
 
199
        return oupd.updateFilter(f, b, m_moves, m_games, m_updateFilter, m_bEnd);
 
200
    }
 
201
    else
 
202
    {
 
203
        if(&f == m_filter && b == m_board && m_bEnd == bEnd)
 
204
        {
 
205
            return true;
 
206
        }
 
207
        m_bEnd = bEnd;
 
208
        m_board = b;
 
209
        m_filter = &f;
 
210
        m_updateFilter = updateFilter;
 
211
        m_bRequestPending = true;
 
212
        oupd.cancel();
 
213
        return false;
 
214
    }
 
215
}
 
216
 
 
217
void OpeningTree::cancel(bool bVisible)
 
218
{
 
219
    if(!bVisible && oupd.isRunning())
 
220
    {
 
221
        m_bRequestPending = false;
 
222
        oupd.cancel();
 
223
        oupd.wait(200);
 
224
    }
 
225
}
 
226
 
 
227
void OpeningTree::updateFinished(Board* b)
 
228
{
 
229
    sort();
 
230
    emit openingTreeUpdated();
 
231
    if(m_bRequestPending)
 
232
    {
 
233
        updateTerminated(b);
 
234
    }
 
235
}
 
236
 
 
237
void OpeningTree::updateTerminated(Board*)
 
238
{
 
239
    if(m_bRequestPending)
 
240
    {
 
241
        emit openingTreeUpdateStarted();
 
242
        m_bRequestPending = false;
 
243
        connect(&oupd, SIGNAL(UpdateFinished(Board*)), this, SLOT(updateFinished(Board*)), Qt::UniqueConnection);
 
244
        connect(&oupd, SIGNAL(UpdateTerminated(Board*)), this, SLOT(updateTerminated(Board*)), Qt::UniqueConnection);
 
245
        connect(&oupd, SIGNAL(progress(int)), SIGNAL(progress(int)), Qt::UniqueConnection);
 
246
        oupd.updateFilter(*m_filter, m_board, m_moves, m_games, m_updateFilter, m_bEnd);
 
247
    }
 
248
}
 
249
 
 
250
QString OpeningTree::debug()
 
251
{
 
252
    QString s;
 
253
    for(int i = 0; i < m_moves.count(); ++i)
 
254
        s.append(QString("%1. %2\t%3 games\t%4%\n")
 
255
                 .arg(i + 1).arg(m_moves[i].move).arg(m_moves[i].count).arg(m_moves[i].percentage()));
 
256
    return s;
 
257
}
 
258
 
 
259
int OpeningTree::rowCount(const QModelIndex& parent) const
 
260
{
 
261
    return parent.isValid() ? 0 : m_moves.count();
 
262
}
 
263
 
 
264
int OpeningTree::columnCount(const QModelIndex&) const
 
265
{
 
266
    return m_names.count();
 
267
}
 
268
 
 
269
OpeningTree::OpeningTree() : m_sortcolumn(1), m_order(Qt::DescendingOrder), m_filter(0)
 
270
{
 
271
    m_names << tr("Move") << tr("Count") << tr("Score") << tr("Rating") << tr("Year");
 
272
}
 
273
 
 
274
OpeningTree::OpeningTree(Filter & f, const Board & b, bool updFilter) :
 
275
    m_sortcolumn(1), m_order(Qt::DescendingOrder), m_filter(0)
 
276
{
 
277
    m_names << tr("Move") << tr("Count") << tr("Score") << tr("Rating") << tr("Year");
 
278
    updateFilter(f, b, updFilter, false);
 
279
}
 
280
 
 
281
QVariant OpeningTree::headerData(int section, Qt::Orientation orientation, int role) const
 
282
{
 
283
    if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
 
284
    {
 
285
        return m_names[section];
 
286
    }
 
287
    else
 
288
    {
 
289
        return QVariant();
 
290
    }
 
291
}
 
292
 
 
293
QVariant OpeningTree::data(const QModelIndex& index, int role) const
 
294
{
 
295
    if(role != Qt::DisplayRole || !index.isValid() || index.row() >= m_moves.count())
 
296
    {
 
297
        return QVariant();
 
298
    }
 
299
    switch(index.column())
 
300
    {
 
301
    case 0:
 
302
        return QString("%1: %2").arg(index.row() + 1).arg(m_moves[index.row()].move);
 
303
    case 1:
 
304
    {
 
305
        if(m_games == 0)
 
306
        {
 
307
            return "";
 
308
        }
 
309
        int percentage = m_moves[index.row()].count * 1000 / m_games / 10.0;
 
310
        QString approx;
 
311
        if(percentage == 0)
 
312
        {
 
313
            percentage = 1;
 
314
            approx = "<";
 
315
        }
 
316
        return QString("%1: %2%3%")
 
317
               .arg(m_moves[index.row()].count)
 
318
               .arg(approx)
 
319
               .arg(percentage);
 
320
    }
 
321
    case 2:
 
322
        return QString("%1%").arg(m_moves[index.row()].percentage());
 
323
    case 3:
 
324
        return m_moves[index.row()].rated >= MinAveRating ?
 
325
               m_moves[index.row()].averageRating() : QVariant();
 
326
    case 4:
 
327
        return m_moves[index.row()].dated >= MinAveYear ?
 
328
               m_moves[index.row()].averageYear() : QVariant();
 
329
 
 
330
    default:
 
331
        return QVariant();
 
332
    }
 
333
}
 
334
 
 
335
void OpeningTree::sort(int column, Qt::SortOrder order)
 
336
{
 
337
    m_sortcolumn = column;
 
338
    m_order = order;
 
339
    switch(column)
 
340
    {
 
341
    case 0:
 
342
        qSort(m_moves.begin(), m_moves.end(), compareMove);
 
343
        break;
 
344
    case 1:
 
345
        qSort(m_moves.begin(), m_moves.end());
 
346
        break;
 
347
    case 2:
 
348
        qSort(m_moves.begin(), m_moves.end(), compareScore);
 
349
        break;
 
350
    case 3:
 
351
        qSort(m_moves.begin(), m_moves.end(), compareRating);
 
352
        break;
 
353
    case 4:
 
354
        qSort(m_moves.begin(), m_moves.end(), compareYear);
 
355
        break;
 
356
    };
 
357
    if(order == Qt::DescendingOrder)
 
358
        for(int i = 0; i < m_moves.count() / 2; ++i)
 
359
        {
 
360
            qSwap(m_moves[i], m_moves[m_moves.count() - i - 1]);
 
361
        }
 
362
    beginResetModel();
 
363
    endResetModel();
 
364
}
 
365
 
 
366
void OpeningTree::sort()
 
367
{
 
368
    sort(m_sortcolumn, m_order);
 
369
}
 
370
 
 
371
QString OpeningTree::move(const QModelIndex& index) const
 
372
{
 
373
    return index.isValid() ? m_moves[index.row()].move : QString();
 
374
}
 
375
 
 
376
Board OpeningTree::board() const
 
377
{
 
378
    return m_board;
 
379
}
 
380