-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathuniques.h
311 lines (258 loc) · 9.22 KB
/
uniques.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
#include "chess.h"
// TODO: get rid of this and perform everything in QBBs!
void HexaToQuadBB(QuadBitBoardPosition *qbb, GameState *gameState, HexaBitBoardPosition *hbb)
{
gameState->chance = hbb->chance;
gameState->whiteCastle = hbb->whiteCastle;
gameState->blackCastle = hbb->blackCastle;
gameState->enPassent = hbb->enPassent;
gameState->halfMoveCounter = hbb->halfMoveCounter;
qbb->white = hbb->whitePieces;
uint64 knights = hbb->knights;
uint64 queens = hbb->bishopQueens & hbb->rookQueens;
uint64 bishops = hbb->bishopQueens & ~queens;
uint64 rooks = hbb->rookQueens & ~queens;
uint64 pawns = hbb->pawns & RANKS2TO7;
uint64 kings = hbb->kings;
qbb->nbk = knights | bishops | kings;
qbb->pbq = pawns | bishops | queens;
qbb->rqk = rooks | queens | kings;
}
void quadToHexaBB(HexaBitBoardPosition *hbb, QuadBitBoardPosition *qbb, GameState *gameState)
{
uint64 bishops = qbb->nbk & qbb->pbq;
uint64 queens = qbb->pbq & qbb->rqk;
uint64 kings = qbb->nbk & qbb->rqk;
uint64 odd = qbb->nbk ^ qbb->pbq ^ qbb->rqk; // pawn or knight or rook
uint64 pawns = odd & qbb->pbq;
uint64 knights = odd & qbb->nbk;
uint64 rooks = odd & qbb->rqk;
hbb->bishopQueens = bishops | queens;
hbb->rookQueens = rooks | queens;
hbb->kings = kings;
hbb->knights = knights;
hbb->pawns = pawns;
hbb->whitePieces = qbb->white;
hbb->chance = gameState->chance;
hbb->whiteCastle = gameState->whiteCastle;
hbb->blackCastle = gameState->blackCastle;
hbb->enPassent = gameState->enPassent;
hbb->halfMoveCounter = gameState->halfMoveCounter;
}
#pragma pack(push, 1)
struct UniquePosRecord
{
uint64 hash; // 8 bytes hash should be enough as we aren't dealing with too many positions yet
QuadBitBoardPosition pos; // 32 bytes
GameState state; // 2 bytes
uint32 count; // 4 bytes
uint32 next; // 4 bytes index to next record (for chaining), ~0 for invalid/null
};
struct FileRecord
{
QuadBitBoardPosition pos; // 32 bytes
uint32 count : 23;
uint32 chance : 1;
uint32 whiteCastle : 2;
uint32 blackCastle : 2;
uint32 enPassent : 4;
};
#pragma pack(pop)
CT_ASSERT(sizeof(FileRecord) == 36);
CT_ASSERT(sizeof(UniquePosRecord) == 50);
// 26 bits -> 64 million entries: 3200 MB hash table
#define UNIQUE_TABLE_BITS 26
#define UNIQUE_TABLE_SIZE (1 << UNIQUE_TABLE_BITS)
#define UNIQUE_TABLE_INDEX_BITS (UNIQUE_TABLE_SIZE - 1)
#define UNIQUE_TABLE_HASH_BITS (ALLSET ^ UNIQUE_TABLE_INDEX_BITS)
// hash table to store unique board positions
UniquePosRecord *hashTable = NULL;
// additional memory to store positions in case there is hash collision
// 200 million entries for extra alloc (10000 MB)
#define EXTRA_ALLOC_SIZE 200 * 1024 * 1024
UniquePosRecord *additionalAlloc;
uint32 indexInAlloc = 0;
uint32 allocNewRecord(UniquePosRecord *prev)
{
if (additionalAlloc == NULL)
{
printf("\nAllocating additional memory\n");
additionalAlloc = (UniquePosRecord *) malloc(EXTRA_ALLOC_SIZE * sizeof(UniquePosRecord));
memset(additionalAlloc, 0, EXTRA_ALLOC_SIZE * sizeof(UniquePosRecord));
}
UniquePosRecord *newEntry = &additionalAlloc[indexInAlloc];
if (indexInAlloc == EXTRA_ALLOC_SIZE)
{
printf("\nRan out of memory!!\n");
getch();
exit(0);
}
// add to chain and return the entry
prev->next = indexInAlloc;
return indexInAlloc++;
}
bool findPositionAndUpdateCounter(HexaBitBoardPosition *pos, uint64 hash, uint32 partialCount)
{
if (hashTable == NULL)
{
hashTable = (UniquePosRecord*)malloc(UNIQUE_TABLE_SIZE * sizeof(UniquePosRecord));
memset(hashTable, 0, UNIQUE_TABLE_SIZE * sizeof(UniquePosRecord));
}
UniquePosRecord *record = &hashTable[hash & UNIQUE_TABLE_INDEX_BITS];
while (true)
{
if (record->hash == hash)
{
// match
record->count += partialCount;
return true;
}
else if (record->hash == 0)
{
// empty
QuadBitBoardPosition qbb;
GameState state;
HexaToQuadBB(&qbb, &state, pos);
record->pos = qbb;
record->state = state;
record->hash = hash;
record->count = partialCount;
record->next = ~0;
return false;
}
uint32 nextRecord = record->next;
if (nextRecord == ~0)
{
nextRecord = allocNewRecord(record);
}
record = &additionalAlloc[nextRecord];
}
}
// find uniques till specified depth
uint64 perft_unique(HexaBitBoardPosition *pos, uint32 depth, uint32 partialCount = 1)
{
CMove moves[MAX_MOVES];
uint32 nMoves = 0;
if (depth == 0)
{
// check the postion in list of existing positions
// add to list (with occurence count = 1) if it's a new position
// otherwise just increment the occurence counter of the position
uint64 hash = computeZobristKey(pos);
bool found = findPositionAndUpdateCounter(pos, hash, partialCount);
if (found)
return 0;
else
return 1;
}
nMoves = generateMoves(pos, moves);
uint64 uniqueCount = 0;
for (int i = 0; i < nMoves; i++)
{
HexaBitBoardPosition childPos = *pos;
uint64 fakeHash = 0;
makeMove(&childPos, fakeHash, moves[i], pos->chance);
uniqueCount += perft_unique(&childPos, depth - 1, partialCount);
}
return uniqueCount;
}
void saveUniquesToFile(int depth)
{
char fileName[256];
sprintf(fileName, "c:\\ankan\\unique\\uniques_%d.dat", depth);
FILE *fp = fopen(fileName, "wb+");
// TODO: sort before writing?
int recordsWritten = 0;
// 1. first save hash table entries
for (int i = 0; i < UNIQUE_TABLE_SIZE; i++)
{
UniquePosRecord *record = &hashTable[i];
if (record->count)
{
FileRecord rec;
rec.pos = record->pos;
rec.count = record->count;
rec.chance = record->state.chance;
rec.enPassent = record->state.enPassent;
rec.whiteCastle = record->state.whiteCastle;
rec.blackCastle = record->state.blackCastle;
fwrite(&rec, sizeof(FileRecord), 1, fp);
recordsWritten++;
}
}
// 2. save the entries in additional allocation
for (int i = 0; i < indexInAlloc; i++)
{
UniquePosRecord *record = &additionalAlloc[i];
FileRecord rec;
rec.pos = record->pos;
rec.count = record->count;
rec.chance = record->state.chance;
rec.enPassent = record->state.enPassent;
rec.whiteCastle = record->state.whiteCastle;
rec.blackCastle = record->state.blackCastle;
fwrite(&rec, sizeof(FileRecord), 1, fp);
recordsWritten++;
}
fclose(fp);
printf("\n%d records saved\n", recordsWritten);
// delete the hash table and additional allocation
free(hashTable);
hashTable = NULL;
free(additionalAlloc);
additionalAlloc = NULL;
indexInAlloc = 0;
}
// find unique chess positions (and their occurence counts) for the specified depth
// save them in a binary file (that can be later used to compute deeper perfts
// the file is sorted on the hash of position to make merging (and duplicate removal) easy
// when processing multiple files later
void findUniques(int depth)
{
BoardPosition testBoard;
Utils::readFENString("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", &testBoard);
Utils::dispBoard(&testBoard);
HexaBitBoardPosition testBB;
Utils::board088ToHexBB(&testBB, &testBoard);
start = clock();
uint64 count = perft_unique(&testBB, depth);
end = clock();
double t = ((double)end - start) / CLOCKS_PER_SEC;
printf("\nUnique(%d) = %llu, time: %g seconds\n", depth, count, t);
// save to disk
saveUniquesToFile(depth);
// compute further unique values using the results from previous level
int curDepth = depth + 1;
while (1)
{
// read from file into memory
// and then find child boards and uniques for them
char fileName[256];
sprintf(fileName, "c:\\ankan\\unique\\uniques_%d.dat", curDepth - 1);
FILE *fp = fopen(fileName, "rb+");
while(1)
{
FileRecord record;
int read = fread(&record, sizeof(FileRecord), 1, fp);
if (!read)
{
// done!
break;
}
// TODO: handle when we run out of memory
// run sort + merge algorithm to do sorted merging and duplicate removal
HexaBitBoardPosition pos;
GameState state = { 0 };
state.blackCastle = record.blackCastle;
state.chance = record.chance;
state.enPassent = record.enPassent;
state.whiteCastle = record.whiteCastle;
quadToHexaBB(&pos, &(record.pos), &state);
perft_unique(&pos, 1, record.count);
}
fclose(fp);
saveUniquesToFile(curDepth);
curDepth++;
}
getchar();
}