1 /****************************************************************************
2 *   Copyright (C) 2017 by Jens Nissen jens-chessx@gmx.net                   *
3 ****************************************************************************/
4 
5 #include "arenabook.h"
6 #include "abk.h"
7 #include "gamex.h"
8 #include "tags.h"
9 
10 #include <QMutexLocker>
11 
12 using namespace chessx;
13 
14 #if defined(_MSC_VER) && defined(_DEBUG)
15 #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
16 #define new DEBUG_NEW
17 #endif // _MSC_VER
18 
19 // ---------------------------------------------------------
20 // construction
21 // ---------------------------------------------------------
22 
ArenaBook()23 ArenaBook::ArenaBook() :
24     Database(),
25     m_file(nullptr),
26     m_posCount(0),
27     m_count(0)
28 {
29 }
30 
~ArenaBook()31 ArenaBook::~ArenaBook()
32 {
33     for(int i = 0; i < m_games.count(); ++i)
34     {
35         delete m_games[i];
36     }
37     m_games.clear();
38     m_index.clear();
39     close();
40 }
41 
42 // ---------------------------------------------------------
43 // Database Interface implementation
44 // ---------------------------------------------------------
45 
open(const QString & filename,bool)46 bool ArenaBook::open(const QString &filename, bool)
47 {
48     if(m_file)
49     {
50         return false;
51     }
52     m_break = false;
53     m_filename = filename;
54     if(openFile(filename, true))
55     {
56         m_utf8 = false;
57         QFileInfo fi(m_filename);
58         m_posCount = fi.size() / sizeof(ABK_ENTRY);
59         return true;
60     }
61     return false;
62 }
63 
add_move(GameX * game,const ABK_MOVE * move)64 void ArenaBook::add_move(GameX* game, const ABK_MOVE* move)
65 {
66     if ((move->from >> 3) + 1 == 32 && (move->to >> 3) + 1 == 32) return;
67 
68     Move m = game->board().prepareMove((Square)move->from, (Square)move->to);
69 
70     // Check the promotion piece and convert
71     if (m.isPromotion() && move->promotion)
72     {
73         static const PieceType promotionPiece[] = { None, Rook, Knight, Bishop, Queen };
74         m.setPromoted(promotionPiece[move->promotion]);
75     }
76 
77     game->dbAddMove(m);
78 }
79 
tag_game(GameX * game,int ply,GameId index)80 void ArenaBook::tag_game(GameX* game, int ply, GameId index)
81 {
82     // Determine tags
83     QString nameWhite = QString("Game %1").arg(m_count+1);
84     game->setTag(TagNameWhite, nameWhite);
85     game->setTag(TagNamePlyCount, QString::number(ply));
86     QString valLength = QString::number((game->plyCount() + 1) / 2);
87     game->setTag(TagNameLength, valLength);
88     QString eco = game->ecoClassify().left(3);
89     if(!eco.isEmpty())
90     {
91         game->setTag(TagNameECO, eco);
92     }
93     // Copy them to the index as well
94     setTagsToIndex(*game, index);
95 }
96 
parseFile()97 bool ArenaBook::parseFile()
98 {
99     if (!m_file) return false;
100 
101     QMutexLocker m(&m_mutex);
102 
103     m_count = 0;
104 
105     QByteArray ba = m_file->readAll();
106     const ABK_ENTRY* arena_book = (ABK_ENTRY*) ba.constData();
107 
108     int ply = 0;
109     int node[1000] = { 0 };
110     ABK_MOVE move_stack[1000];
111 
112     ply = 0;
113     node[0] = 900;  // offset to first node in abk-file
114 
115     GameX* game = new GameX;
116 
117     while(1)
118     {
119         add_move(game, &arena_book[node[ply]].move);
120 
121         if (arena_book[node[ply]].first_child > 0)
122         {  // current game
123 
124             move_stack[ply] = arena_book[node[ply]].move;
125             node[ply + 1] = arena_book[node[ply]].first_child;
126             ++ply;
127         }
128         else
129         {
130             m_count = m_index.add();
131 
132             tag_game(game, ply, m_count);
133             game->unmountBoard();
134             m_games.append(game);
135 
136             node[ply] = arena_book[node[ply]].next_sibling;
137             while (node[ply] < 0)
138             {
139                 --ply;
140                 if (ply < 0)
141                 {
142                     return true; // We are done!
143                 }
144                 node[ply] = arena_book[node[ply]].next_sibling;
145             }
146             ++m_count;
147             game = new GameX;
148             for (int i = 0; i < ply; i++)
149             {
150                 add_move(game, &move_stack[i]);
151             }
152         }
153         if (node[ply] >= (int) m_posCount)
154         {
155             delete game;
156             return false; // Node out of range
157         }
158     }
159 
160     close(); // no need to keep it open, everything is in memory
161     return true;
162 }
163 
filename() const164 QString ArenaBook::filename() const
165 {
166     return m_filename;
167 }
168 
count() const169 quint64 ArenaBook::count() const
170 {
171     return m_count;
172 }
173 
positionCount() const174 quint64 ArenaBook::positionCount() const
175 {
176     return m_posCount;
177 }
178 
loadGame(GameId gameId,GameX & game)179 bool ArenaBook::loadGame(GameId gameId, GameX& game)
180 {
181     if (gameId < count())
182     {
183         loadGameMoves(gameId, game);
184         return true;
185     }
186     return false;
187 }
188 
loadGameMoves(GameId gameId,GameX & game)189 void ArenaBook::loadGameMoves(GameId gameId, GameX & game)
190 {
191     game = *m_games[gameId];
192     loadGameHeaders(gameId, game);
193 }
194 
findPosition(GameId index,const BoardX & position)195 int ArenaBook::findPosition(GameId index, const BoardX &position)
196 {
197     GameX g;
198     loadGameMoves(index, g);
199     return g.cursor().findPosition(position);
200 }
201 
openFile(const QString & filename,bool readOnly)202 bool ArenaBook::openFile(const QString &filename, bool readOnly)
203 {
204     //open file
205     QFile* file = new QFile(filename);
206     if(readOnly && !file->exists())
207     {
208         delete file;
209         return false;
210     }
211     if (file->open(readOnly ? QIODevice::ReadOnly : QIODevice::WriteOnly))
212     {
213         m_file = file;
214         return true;
215     }
216 
217     delete file;
218     return false;
219 }
220 
close()221 void ArenaBook::close()
222 {
223     //close the file, and delete objects
224     if(m_file)
225     {
226         m_file->close();
227     }
228     delete m_file;
229     m_file = 0;
230 }
231