libscid 0.1.0
Chess applications made easy.
Loading...
Searching...
No Matches
indexentry.h
1/*
2* Copyright (c) 1999-2002 Shane Hudson
3* Copyright (c) 2006-2009 Pascal Georges
4* Copyright (C) 2014-2017 Fulvio Benini
5
6* This file is part of Scid (Shane's Chess Information Database).
7*
8* Scid is free software: you can redistribute it and/or modify
9* it under the terms of the GNU General Public License as published by
10* the Free Software Foundation.
11*
12* Scid 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*
17* You should have received a copy of the GNU General Public License
18* along with Scid. If not, see <http://www.gnu.org/licenses/>.
19*/
20
21#ifndef SCID_INDEXENTRY_H
22#define SCID_INDEXENTRY_H
23
24#include "scid/core/game_result.h"
25#include "scid/database/common.h"
26#include "scid/core/date.h"
27#include "scid/database/game_id.h"
28#include "scid/database/matsig.h"
29#include <cstring> //memcmp
30
31namespace scid::database {
32
33// HPSIG_SIZE = size of HomePawnData array in an IndexEntry.
34// It is nine bytes: the first scid::core::byte contains the number of valid entries
35// in the array, and the next 8 bytes contain up to 16 half-scid::core::byte entries.
36const scid::core::uint HPSIG_SIZE = 9;
37
38const scid::core::ratingT MAX_ELO = 4000; // Since we store Elo Ratings in 12 bits
39
40const scid::core::byte CUSTOM_FLAG_MASK[] = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5 };
41
42
43//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44// Class IndexEntry: one of these per game in the index file.
45//
46// It contains more than just the location of the game data in the main
47// data file. For fast searching, it also store some other important
48// values: players, event, site, date, result, eco, gamelength.
49//
50// It takes 56 bytes.
52 uint64_t offset_ : 46; // Start of gamefile record for this game.
53 uint64_t gameDataSize_ : 18; // Length of gamefile record for this game.
54
55 uint32_t nComments_ : 4;
56 uint32_t whiteID_ : 28;
57
58 uint32_t nVariations_ : 4;
59 uint32_t blackID_ : 28;
60
61 uint32_t nNags_ : 4;
62 uint32_t eventID_ : 28;
63
64 uint32_t siteID_;
65
66 uint32_t variant_ : 1;
67 uint32_t roundID_ : 31;
68
69 uint32_t whiteElo_ : 12;
70 uint32_t date_ : 20;
71
72 uint32_t blackElo_ : 12;
73 uint32_t eventDate_ : 20;
74
75 uint32_t numHalfMoves_ : 10;
76 uint32_t flags_ : 22;
77
78 uint32_t result_ : 2;
79 uint32_t whiteEloType_ : 3;
80 uint32_t blackEloType_ : 3;
81 uint32_t finalMatSig_ : 24; // material of the final position in the game
82
83 uint16_t ECOcode_;
84
85 uint8_t storedLineCode_;
86
87 scid::core::byte HomePawnData [HPSIG_SIZE]; // homePawnSig data.
88
89public:
90 uint64_t GetOffset() const { return offset_; }
91 uint32_t GetLength() const { return gameDataSize_; }
92 idNumberT GetWhite() const { return whiteID_; }
93 scid::core::ratingT GetWhiteElo() const { return whiteElo_; }
94 scid::core::ratingTypeT GetWhiteRatingType() const { return whiteEloType_; }
95 idNumberT GetBlack() const { return blackID_; }
96 scid::core::ratingT GetBlackElo() const { return blackElo_; }
97 scid::core::ratingTypeT GetBlackRatingType() const { return blackEloType_; }
98 idNumberT GetEvent() const { return eventID_; }
99 idNumberT GetSite() const { return siteID_; }
100 idNumberT GetRound() const { return roundID_; }
101 scid::core::dateT GetDate() const { return date_; }
102 scid::core::dateT GetEventDate() const { return eventDate_; }
103 scid::core::resultT GetResult() const { return result_; }
104 scid::core::uint GetVariationCount() const { return DecodeCount(nVariations_); }
105 scid::core::uint GetCommentCount() const { return DecodeCount(nComments_); }
106 scid::core::uint GetNagCount() const { return DecodeCount(nNags_); }
107 uint16_t GetNumHalfMoves() const { return numHalfMoves_; }
108 matSigT GetFinalMatSig() const { return finalMatSig_; }
109 scid::core::byte GetStoredLineCode() const { return storedLineCode_; }
110 EcoCode GetEcoCode() const { return ECOcode_; }
111 bool GetFlag(uint32_t mask) const { return (flags_ & mask) == mask; }
112 uint32_t GetRawFlags() const { return flags_; }
113 uint16_t GetRaw4bitsCounts() const {
114 uint16_t res = nVariations_ & 0x0F;
115 res |= static_cast<uint16_t>(nComments_ & 0x0F) << 4;
116 res |= static_cast<uint16_t>(nNags_ & 0x0F) << 8;
117 return res;
118 }
119
120 void setChessStd() { variant_ = 0; }
121 void setChess960() { variant_ = 1; }
122 bool isChessStd() const { return variant_ == 0; }
123
124 const scid::core::byte* GetHomePawnData() const { return HomePawnData; }
125 void SetHomePawnData(scid::core::byte hpCount, const scid::core::byte hpVal[8]) {
126 HomePawnData[0] = hpCount; // First scid::core::byte stores the count
127 std::copy_n(hpVal, 8, HomePawnData + 1);
128 }
129
130 // set functions, assert that the value is not truncated.
131 void SetOffset(uint64_t offset) {
132 offset_ = offset;
133 ASSERT(GetOffset() == offset);
134 }
135 void SetLength(size_t length) {
136 gameDataSize_ = length;
137 ASSERT(GetLength() == length);
138 }
139 void SetWhite(idNumberT id) {
140 whiteID_ = id;
141 ASSERT(GetWhite() == id);
142 }
143 void SetWhiteElo(scid::core::ratingT elo) {
144 whiteElo_ = elo;
145 ASSERT(GetWhiteElo() == elo);
146 }
147 void SetWhiteRatingType(scid::core::ratingTypeT b) {
148 whiteEloType_ = b;
149 ASSERT(GetWhiteRatingType() == b);
150 }
151 void SetBlack(idNumberT id) {
152 blackID_ = id;
153 ASSERT(GetBlack() == id);
154 }
155 void SetBlackElo(scid::core::ratingT elo) {
156 blackElo_ = elo;
157 ASSERT(GetBlackElo() == elo);
158 }
159 void SetBlackRatingType(scid::core::ratingTypeT b) {
160 blackEloType_ = b;
161 ASSERT(GetBlackRatingType() == b);
162 }
163 void SetEvent(idNumberT id) {
164 eventID_ = id;
165 ASSERT(GetEvent() == id);
166 }
167 void SetSite(idNumberT id) {
168 siteID_ = id;
169 ASSERT(GetSite() == id);
170 }
171 void SetRound(idNumberT id) {
172 roundID_ = id;
173 ASSERT(GetRound() == id);
174 }
175 void SetDate(scid::core::dateT date) {
176 date_ = date;
177 ASSERT(GetDate() == date);
178 }
179 void SetEventDate(scid::core::dateT edate) {
180 eventDate_ = edate;
181 ASSERT(GetEventDate() == edate);
182 }
183 void SetResult(scid::core::resultT res) {
184 result_ = res;
185 ASSERT(GetResult() == res);
186 }
187 void SetVariationCount(unsigned x) { nVariations_ = EncodeCount(x); }
188 void SetCommentCount(unsigned x) { nComments_ = EncodeCount(x); }
189 void SetNagCount(unsigned x) { nNags_ = EncodeCount(x); }
190 void SetRawVariationCount(unsigned x) {
191 nVariations_ = x;
192 ASSERT(x == nVariations_);
193 }
194 void SetRawCommentCount(unsigned x) {
195 nComments_ = x;
196 ASSERT(x == nComments_);
197 }
198 void SetRawNagCount(unsigned x) {
199 nNags_ = x;
200 ASSERT(x == nNags_);
201 }
202 void SetNumHalfMoves(scid::core::ushort b) {
203 numHalfMoves_ = b;
204 ASSERT(GetNumHalfMoves() == b);
205 }
206 void SetFinalMatSig(matSigT ms) {
207 finalMatSig_ = ms;
208 ASSERT(GetFinalMatSig() == ms);
209 }
210 void SetStoredLineCode(scid::core::byte b) {
211 storedLineCode_ = b;
212 ASSERT(GetStoredLineCode() == b);
213 }
214 void SetEcoCode(EcoCode eco) {
215 ECOcode_ = eco;
216 ASSERT(GetEcoCode() == eco);
217 }
218 void SetFlag(uint32_t flagMask, bool set) {
219 if (set)
220 flags_ |= flagMask;
221 else
222 flags_ &= ~flagMask;
223 }
224
225 // Handy functions that do not directly access member vars.
226 scid::core::uint GetYear () const { return scid::core::date_GetYear (GetDate()); }
227 scid::core::uint GetMonth() const { return scid::core::date_GetMonth (GetDate()); }
228 scid::core::uint GetDay () const { return scid::core::date_GetDay (GetDate()); }
229
230 void SetPlayer(scid::core::colorT col, idNumberT id) {
231 return (col == scid::core::BLACK) ? SetBlack(id) : SetWhite(id);
232 }
233
234 scid::core::byte GetRating() const;
235
236 bool GetStartFlag () const { return GetFlag(1 << IDX_FLAG_START); }
237 bool GetPromotionsFlag () const { return GetFlag(1 << IDX_FLAG_PROMO); }
238 bool GetUnderPromoFlag() const { return GetFlag(1 << IDX_FLAG_UPROMO); }
239 bool GetCommentsFlag () const { return (GetCommentCount() > 0); }
240 bool GetVariationsFlag () const { return (GetVariationCount() > 0); }
241 bool GetNagsFlag () const { return (GetNagCount() > 0); }
242 bool GetDeleteFlag () const { return GetFlag(1 << IDX_FLAG_DELETE); }
243
244 static scid::core::uint CharToFlag (char ch);
245 static uint32_t CharToFlagMask (char flag);
246 static uint32_t StrToFlagMask (const char* flags);
247 scid::core::uint GetFlagStr(char* dest, const char* flags) const;
248
249 void SetStartFlag (bool b) { SetFlag(1 << IDX_FLAG_START, b); }
250 void SetPromotionsFlag (bool b) { SetFlag(1 << IDX_FLAG_PROMO, b); }
251 void SetUnderPromoFlag (bool b) { SetFlag(1 << IDX_FLAG_UPROMO, b); }
252 void SetDeleteFlag (bool b) { SetFlag(1 << IDX_FLAG_DELETE, b); }
253 void clearFlags() { return SetFlag(IDX_MASK_ALLFLAGS, false); }
254 bool equalExceptFlags(IndexEntry ie) const {
255 ie.flags_ = flags_;
256 static_assert(std::has_unique_object_representations_v<IndexEntry>);
257 return memcmp(this, &ie, sizeof(IndexEntry)) == 0;
258 }
259
260 enum {
261 // IndexEntry Flag types:
262 IDX_FLAG_START = 0, // Game has own start position.
263 IDX_FLAG_PROMO = 1, // Game contains promotion(s).
264 IDX_FLAG_UPROMO = 2, // Game contains promotion(s).
265 IDX_FLAG_DELETE = 3, // Game marked for deletion.
266 IDX_FLAG_WHITE_OP = 4, // White openings flag.
267 IDX_FLAG_BLACK_OP = 5, // Black openings flag.
268 IDX_FLAG_MIDDLEGAME = 6, // Middlegames flag.
269 IDX_FLAG_ENDGAME = 7, // Endgames flag.
270 IDX_FLAG_NOVELTY = 8, // Novelty flag.
271 IDX_FLAG_PAWN = 9, // Pawn structure flag.
272 IDX_FLAG_TACTICS = 10, // Tactics flag.
273 IDX_FLAG_KSIDE = 11, // Kingside play flag.
274 IDX_FLAG_QSIDE = 12, // Queenside play flag.
275 IDX_FLAG_BRILLIANCY = 13, // Brilliancy or good play.
276 IDX_FLAG_BLUNDER = 14, // Blunder or bad play.
277 IDX_FLAG_USER = 15, // User-defined flag.
278 IDX_FLAG_CUSTOM1 = 16, // Custom flag.
279 IDX_FLAG_CUSTOM2 = 17, // Custom flag.
280 IDX_FLAG_CUSTOM3 = 18, // Custom flag.
281 IDX_FLAG_CUSTOM4 = 19, // Custom flag.
282 IDX_FLAG_CUSTOM5 = 20, // Custom flag.
283 IDX_FLAG_CUSTOM6 = 21, // Custom flag.
284 IDX_NUM_FLAGS = 22,
285 };
286 static const uint32_t IDX_MASK_ALLFLAGS = 0xFFFFFFFF;
287
288private:
289 static scid::core::uint EncodeCount (scid::core::uint x) {
290 if (x <= 10) { return x; }
291 if (x <= 12) { return 10; }
292 if (x <= 17) { return 11; } // 11 indicates 15 (13-17)
293 if (x <= 24) { return 12; } // 12 indicates 20 (18-24)
294 if (x <= 34) { return 13; } // 13 indicates 30 (25-34)
295 if (x <= 44) { return 14; } // 14 indicates 40 (35-44)
296 return 15; // 15 indicates 50 or more
297 }
298 static scid::core::uint DecodeCount (scid::core::uint x) {
299 static scid::core::uint countCodes[16] = {0,1,2,3,4,5,6,7,8,9,10,15,20,30,40,50};
300 return countCodes[x & 15];
301 }
302};
303
304
305inline scid::core::byte IndexEntry::GetRating() const {
306 scid::core::ratingT welo = GetWhiteElo();
307 scid::core::ratingT belo = GetBlackElo();
308 auto rating = (welo != 0 && belo != 0) ? (welo + belo) / 140 : 0;
309 static_assert(std::is_signed_v<decltype(rating)>);
310
311 // Bonus for comments or Nags
312 if (GetCommentCount() > 2 || GetNagCount() > 2) {
313 if (rating < 21) { // Missing elo
314 rating = 38;
315 } else {
316 rating += 6;
317 }
318 }
319
320 // Early draw penalty
321 if (GetResult() == scid::core::RESULT_Draw) {
322 scid::core::uint moves = GetNumHalfMoves();
323 if (moves < 80) {
324 rating -= 3;
325 if (moves < 60) {
326 rating -= 2;
327 if (moves < 40) rating -= 2;
328 }
329 }
330 }
331
332 if (rating < 0) return 0;
333 else return static_cast<scid::core::byte> (rating);
334}
335
336//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
337// IndexEntry::CharToFlag():
338// Returns the flag number corresponding to the given character.
339inline scid::core::uint
340IndexEntry::CharToFlag (char ch)
341{
342 scid::core::uint flag = 0;
343 switch (toupper(ch)) {
344 case 'D': flag = IDX_FLAG_DELETE; break;
345 case 'W': flag = IDX_FLAG_WHITE_OP; break;
346 case 'B': flag = IDX_FLAG_BLACK_OP; break;
347 case 'M': flag = IDX_FLAG_MIDDLEGAME; break;
348 case 'E': flag = IDX_FLAG_ENDGAME; break;
349 case 'N': flag = IDX_FLAG_NOVELTY; break;
350 case 'P': flag = IDX_FLAG_PAWN; break;
351 case 'T': flag = IDX_FLAG_TACTICS; break;
352 case 'K': flag = IDX_FLAG_KSIDE; break;
353 case 'Q': flag = IDX_FLAG_QSIDE; break;
354 case '!': flag = IDX_FLAG_BRILLIANCY; break;
355 case '?': flag = IDX_FLAG_BLUNDER; break;
356 case 'U': flag = IDX_FLAG_USER; break;
357 case '1': flag = IDX_FLAG_CUSTOM1; break;
358 case '2': flag = IDX_FLAG_CUSTOM2; break;
359 case '3': flag = IDX_FLAG_CUSTOM3; break;
360 case '4': flag = IDX_FLAG_CUSTOM4; break;
361 case '5': flag = IDX_FLAG_CUSTOM5; break;
362 case '6': flag = IDX_FLAG_CUSTOM6; break;
363 }
364 return flag;
365}
366
367//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
368// IndexEntry::CharToFlagMask():
369// Transform a char in a mask that can be used with GetFlag() and SetFlag()
370inline uint32_t IndexEntry::CharToFlagMask(char flag)
371{
372 switch (toupper(flag)) {
373 case 'S': return 1 << IDX_FLAG_START;
374 case 'X': return 1 << IDX_FLAG_PROMO;
375 case 'Y': return 1 << IDX_FLAG_UPROMO;
376 case 'D': return 1 << IDX_FLAG_DELETE;
377 case 'W': return 1 << IDX_FLAG_WHITE_OP;
378 case 'B': return 1 << IDX_FLAG_BLACK_OP;
379 case 'M': return 1 << IDX_FLAG_MIDDLEGAME;
380 case 'E': return 1 << IDX_FLAG_ENDGAME;
381 case 'N': return 1 << IDX_FLAG_NOVELTY;
382 case 'P': return 1 << IDX_FLAG_PAWN;
383 case 'T': return 1 << IDX_FLAG_TACTICS;
384 case 'K': return 1 << IDX_FLAG_KSIDE;
385 case 'Q': return 1 << IDX_FLAG_QSIDE;
386 case '!': return 1 << IDX_FLAG_BRILLIANCY;
387 case '?': return 1 << IDX_FLAG_BLUNDER;
388 case 'U': return 1 << IDX_FLAG_USER;
389 case '1': return 1 << IDX_FLAG_CUSTOM1;
390 case '2': return 1 << IDX_FLAG_CUSTOM2;
391 case '3': return 1 << IDX_FLAG_CUSTOM3;
392 case '4': return 1 << IDX_FLAG_CUSTOM4;
393 case '5': return 1 << IDX_FLAG_CUSTOM5;
394 case '6': return 1 << IDX_FLAG_CUSTOM6;
395 }
396
397 ASSERT(0);
398 return 0;
399}
400
401//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
402// IndexEntry::StrToFlagMask():
403// Transform a string in a mask that can be used with GetFlag() and SetFlag()
404inline uint32_t IndexEntry::StrToFlagMask(const char* flags)
405{
406 if (flags == 0) return 0;
407
408 uint32_t res = 0;
409 while (*flags != 0) {
410 res |= IndexEntry::CharToFlagMask(*(flags++));
411 }
412 return res;
413}
414
415//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
416// IndexEntry::GetFlagStr():
417// Fills in the provided flag string with information on the
418// user-settable flags set for this game.
419// Returns the number of specified flags that are turned on.
420inline scid::core::uint
421IndexEntry::GetFlagStr(char* dest, const char* flags) const
422{
423 if (flags == NULL) { flags = "DWBMENPTKQ!?U123456"; }
424 scid::core::uint count = 0;
425 while (*flags != 0) {
426 uint32_t mask = CharToFlagMask(*flags);
427 ASSERT(mask != 0);
428 if (GetFlag(mask)) {
429 *dest++ = *flags;
430 count++;
431 }
432 flags++;
433 }
434 *dest = 0;
435 return count;
436}
437
438
439} // namespace scid::database
440#endif
Definition indexentry.h:51
class StrRange - parse a string interpreting its content as 1 or 2 integers separated by whitespace.
Definition common.h:30