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