Moderator: Andres Valverde
Alessandro Scotti wrote:Hi,
I am adding bitbases to Kiwi based on the Heinz "knowledgeable" approach (see http://www.aarontay.per.sg/Winboard/egtb.html for references to Heinz paper) and it looks like each bitbase requires quite a bit of work. I would like to start with the most important 4-piece endings... can you tell me what are the most common and/or useful in your experience? For now I have KPK, KPPK and KBPK, but that's just because I've personally seen Kiwi blunder on those!
Also, I wonder whether all of the non-trivial bitbases are really needed. For example how important is KQKQ?
The latter question is also important for size, as I would like the entire bitbase set to take as little space as possible so skipping some can be very useful.
Alessandro Scotti wrote:Hi Tony,
you are right, I'll have to implement them all or almost all (and in the proper order)!
Maybe there is another way to save space. I could include some bitbases only for the stronger side on move, then extend one ply if I detect an hit when the other side is on move. At the next ply I should always get a result from the bitbase and return immediately, so possibly that's not too expensive. What do you think?
>Fortunately, the unimportant ones are mostly the smaller ones.
26.01.2005 00:05 660.997 kbkp.bbb
26.01.2005 00:05 660.997 kbkp.bbw
26.01.2005 00:05 218.411 kbnk.bbb
26.01.2005 00:05 218.411 kbnk.bbw
26.01.2005 00:05 660.997 kbpk.bbb
26.01.2005 00:05 660.997 kbpk.bbw
26.01.2005 00:05 660.997 knkp.bbb
26.01.2005 00:05 660.997 knkp.bbw
26.01.2005 00:05 660.997 knpk.bbb
26.01.2005 00:05 660.997 knpk.bbw
26.01.2005 00:05 72.240 kpblk.bbw
26.01.2005 00:05 86.688 kpk.bbb
26.01.2005 00:05 86.688 kpk.bbw
26.01.2005 00:05 814.868 kpkp.bbw
26.01.2005 00:05 254.647 kppk.bbb
26.01.2005 00:05 254.647 kppk.bbw
26.01.2005 00:06 18.741.946 kppkp.bbb
26.01.2005 00:06 18.741.946 kppkp.bbw
26.01.2005 00:05 28.644 kqk.bbb
26.01.2005 00:05 28.644 kqk.bbw
26.01.2005 00:05 218.411 kqkb.bbb
26.01.2005 00:05 218.411 kqkb.bbw
26.01.2005 00:05 218.411 kqkn.bbb
26.01.2005 00:05 218.411 kqkn.bbw
26.01.2005 00:05 1.057.594 kqkp.bbb
26.01.2005 00:05 1.057.594 kqkp.bbw
26.01.2005 00:05 349.457 kqkq.bbw
26.01.2005 00:05 349.457 kqkr.bbb
26.01.2005 00:05 349.457 kqkr.bbw
26.01.2005 00:05 28.644 krk.bbb
26.01.2005 00:05 28.644 krk.bbw
26.01.2005 00:05 218.411 krkb.bbb
26.01.2005 00:05 218.411 krkb.bbw
26.01.2005 00:05 218.411 krkn.bbb
26.01.2005 00:05 218.411 krkn.bbw
26.01.2005 00:05 1.057.594 krkp.bbb
26.01.2005 00:05 1.057.594 krkp.bbw
26.01.2005 00:05 349.457 krkr.bbw
Dieter B?r?ner wrote:>Fortunately, the unimportant ones are mostly the smaller ones.
Tony, I agree with most you have said - but is the above really true? Seems not too much difference to me - at least no big factor.
You might wonder, why I have kbnk bitbase, and not implemented it by knowlege. I got frustrated, when I tried to code all exceptions and checked the code against Nalimov TBs to verify it. So I just included it - the 440 kB will not hurt.
Regards,
Dieter
Dann Corbit wrote:I am wondering about Dieter's bitbase system.
Why did you not create every 5 man file? It seems a small cost.
What have your experiments shown for Elo difference?
Is the format of your bitbase files public?
I am guessing you just use the nalimov index scheme and the object of the lookup is 2 bits wide. Is that right?
Do you memory map the bitbase files? Do you load them into ram?
static unsigned wdl_tab[5] = {1, 3, 9, 27, 81};
/* Careful: index evaluated twice! (Could be easily avoided) */
#define RES_WDL_POS(table, index, res) \
do \
{ \
unsigned ti = (index)/5; \
unsigned tim = (index)%5; \
res = (table[ti]/wdl_tab[tim])%3; \
} while(0)
typedef struct irec4_tab
{
int (*probe_func)(unsigned char **, int, int, int, int, int, int, int, int *);
const char *name;
unsigned char *bb_table[2];
unsigned long crc32[2];
unsigned flags;
unsigned len;
} IREC4_TAB;
/* [5][5] would be sufficient */
static IREC4_TAB irec_kxky_tab[5][8] =
{
/* Pawn */
{
{probe_kpkp, "kpkp", {NULL, NULL},{0x1b2bedd1UL, 0}, WDL, WDL_SIZE(KPKP_SIZ)}, /* kpkp */
{NULL, NULL, {NULL, NULL},{0, 0}, 0, 0}, /* kpkn */
{NULL, NULL, {NULL, NULL},{0, 0}, 0, 0}, /* kpkb */
{NULL, NULL, {NULL, NULL},{0, 0}, 0, 0}, /* kpkr */
{NULL, NULL, {NULL, NULL},{0, 0}, 0, 0} /* kpkq */
},
/* Knight */
{
{probe_kmkp, "knkp", {NULL, NULL},{0x0043461bUL, 0xff21e06bUL}, 0, WD_SIZE(KXKP_SIZ)}, /* knkp */
{probe_kmkm, NULL, {NULL, NULL},{0, 0}, NOTB, 0}, /* knkn */
{NULL, NULL, {NULL, NULL},{0, 0}, 0, 0}, /* knkb */
{NULL, NULL, {NULL, NULL},{0, 0}, 0, 0}, /* knkr */
{NULL, NULL, {NULL, NULL},{0, 0}, 0, 0} /* knkq */
},
/* Bishop */
{
{probe_kmkp, "kbkp", {NULL, NULL},{0xfd331caeUL, 0x43c588e9UL}, 0, WD_SIZE(KXKP_SIZ)}, /* kbkp */
{probe_kmkm, NULL, {NULL, NULL},{0, 0}, NOTB, 0}, /* kbkn */
{probe_kmkm, NULL, {NULL, NULL},{0, 0}, NOTB, 0}, /* kbkb */
{NULL, NULL, {NULL, NULL},{0, 0}, 0, 0}, /* kbkr */
{NULL, NULL, {NULL, NULL},{0, 0}, 0, 0} /* kbkq */
},
/* Rook */
{
{probe_khkp, "krkp", {NULL, NULL},{0xf75df9cfUL, 0xd8ac90daUL}, WDL, WDL_SIZE(KXKP_SIZ)}, /* krkp */
{probe_khkm, "krkn", {NULL, NULL},{0xa3546575UL, 0x24edf19aUL}, 0, WD_SIZE(KXKY_SIZ)}, /* krkn */
{probe_khkm, "krkb", {NULL, NULL},{0xe6c2486eUL, 0xe7e1f204UL}, 0, WD_SIZE(KXKY_SIZ)}, /* krkb */
{probe_khkh, "krkr", {NULL, NULL},{0xa9d9fc99UL, 0}, WDL, WDL_SIZE(KXKY_SIZ)}, /* krkr */
{NULL, NULL, {NULL, NULL},{0, 0}, 0, 0} /* krkq */
},
/* Queen */
{
{probe_khkp, "kqkp", {NULL, NULL},{0x0ddf01e2UL, 0x09bc5e65UL}, WDL, WDL_SIZE(KXKP_SIZ)}, /* kqkp */
{probe_khkm, "kqkn", {NULL, NULL},{0x34954cd1UL, 0x2a1882fcUL}, 0, WD_SIZE(KXKY_SIZ)}, /* kqkn */
{probe_khkm, "kqkb", {NULL, NULL},{0x1993ac58UL, 0xae6a84d1UL}, 0, WD_SIZE(KXKY_SIZ)}, /* kqkb */
{probe_khkh, "kqkr", {NULL, NULL},{0x6c9d0eeaUL, 0x1d46e24dUL}, WDL, WDL_SIZE(KXKY_SIZ)}, /* kqkr */
{probe_khkh, "kqkq", {NULL, NULL},{0x06dcf330UL, 0}, WDL, WDL_SIZE(KXKY_SIZ)} /* kqkq */
}
};
For example the line
/* Knight */
{
{probe_kmkp, "knkp", {NULL, NULL},{0x0043461bUL, 0xff21e06bUL}, 0, WD_SIZE(KXKP_SIZ)}, /* knkp */
/* code assumes A1 is 0, H1 is 7 and H8 is 63 */
/* Nalimov's trick for saving a bit of space */
#define EXCLUDE3(sq, sq1, sq2, sq3) (sq-((sq>sq1)+(sq>sq2)+(sq>sq3)))
#define KXKP_INDEX(wk, bk, x, p) \
(kkpos[wk][bk]*61*48 + EXCLUDE3(x,wk,bk,p)*48 + (p)-8)
#define RES_WD_POS(table, index, res) \
do \
{ \
unsigned ti = index/8; \
unsigned tim = index%8; \
res = (table[ti]>>tim)%2; \
} while(0)
#define MIRROR_LEFTRIGHT(p) ((p)^7)
int probe_kmkp(unsigned char **table, int side, int piece1, int piece2,
int wk, int bk, int x, int p, int *score)
{
unsigned index;
int bit;
int res;
if (!table[side])
return BB_WORTHLESS;
if (col(wk) >= 4)
{
/* mirror the board left to right */
wk = MIRROR_LEFTRIGHT(wk);
bk = MIRROR_LEFTRIGHT(bk);
x = MIRROR_LEFTRIGHT(x);
p = MIRROR_LEFTRIGHT(p);
}
index = KXKP_INDEX(wk, bk, x, p);
RES_WD_POS(table[side], index, bit);
if (bit == BB_DRAW)
{
*score = 0;
res = BB_EXACT;
}
else
{
/* P-side wins, look for exceptions */
if (piece1 == bishop)
{
if (bk == A1 && p == A2 && (wk == C1 || wk == C2 || wk == C3))
return BB_WORTHLESS;
if (bk == A8 && p == A7 && (wk == C8 || wk == C7 || wk == C6))
return BB_WORTHLESS;
}
else
{
/* KNKP: Many exceptions */
if ((bk == A1||bk == A2) && (p==A2||p==A3||p==A4) && Distance(wk,bk) <= 3)
return BB_WORTHLESS;
if ((bk == A8||bk == A7) && (p==A7||p==A6||p==A5) && Distance(wk,bk) <= 3)
return BB_WORTHLESS;
}
if (side == black)
{
*score = 500;
res = BB_LOWER_BOUND;
}
else
{
*score = -500;
res = BB_UPPER_BOUND;
}
}
return res;
}
if( FileOfSquare(wp) == 0 && ! is_light_square(wb) ) {
if( distance( bk, A8 ) < (distance( wk, A8 )-1) && distance(bk,A8) < distance( wp,A8) ) {
predicted_value = 0;
}
}
int bbAdjustKPPK( PackedArray * pa, int wtm, int op )
{
printf( "Adjusting KPPK/%d...", wtm );
// Predictions are based on the KPK bitbases, get them
PackedArray * kpk_wtm = getBitBase( bb_KPK, bb_WtM );
PackedArray * kpk_btm = getBitBase( bb_KPK, bb_BtM );
if( kpk_wtm == 0 || kpk_btm == 0 ) {
printf( "*** Warning: KPPK adjuster can't find KPK bitbase\n" );
return -1;
}
// Use also the KPPK table with black to move
PackedArray * kppk_btm = 0;
if( wtm ) {
kppk_btm = getBitBase( bb_KPPK, bb_BtM );
}
PositionEnumerator posEnum;
posEnum.addPiece( WhitePawn );
posEnum.addPiece( WhitePawn );
unsigned good_predictions = 0;
while( posEnum.hasMorePositions() ) {
if( FileOfSquare( posEnum.getWhiteKingPos() ) >= 4 ) {
// Skip this position (simmetry)
posEnum.gotoNextPosition();
continue;
}
if( ! posEnum.isValidPosition( wtm ? true : false ) ) {
good_predictions++;
posEnum.gotoNextPosition();
continue;
}
int index = getBitBaseIndex( bb_KPPK, posEnum );
// Predict and fix the bitbase
int wk = posEnum.getWhiteKingPos();
int bk = posEnum.getBlackKingPos();
int p1 = posEnum.getPiecePos( 2 );
int p2 = posEnum.getPiecePos( 3 );
if( p1 > p2 ) {
int x = p1;
p1 = p2;
p2 = x;
}
unsigned predicted_value =
kpk_wtm->get( getBitBaseIndex(bb_KPK,wk,bk,p1) ) |
kpk_wtm->get( getBitBaseIndex(bb_KPK,wk,bk,p2) ) |
kpk_btm->get( getBitBaseIndex(bb_KPK,wk,bk,p1) ) |
kpk_btm->get( getBitBaseIndex(bb_KPK,wk,bk,p2) );
if( RankOfSquare(p1) == 1 || RankOfSquare(p2) == 1 ) {
predicted_value = 1;
}
if( FileOfSquare(p1) < 7 && p2 == (p1+1) ) predicted_value = 1;
if( FileOfSquare(p1) < 6 && p2 == (p1+2) ) predicted_value = 1;
if( FileOfSquare(p1) < 5 && p2 == (p1+3) ) predicted_value = 1;
if( kppk_btm != 0 ) {
predicted_value |= kppk_btm->get(index);
}
if( predicted_value == pa->get(index) ) {
good_predictions++;
}
pa->set( index, pa->get(index) ^ predicted_value );
posEnum.gotoNextPosition();
}
printf( " done, accuracy = %05.2f\n", (good_predictions * 100.0) / getBitBaseIndexRange(bb_KPPK) );
return 0;
}
C:\yacet>zip x.zip kbpk.*
adding: kbpk.bbb (208 bytes security) (deflated 75%)
adding: kbpk.bbw (208 bytes security) (deflated 82%)
C:\yacet>unzip -v x
Archive: x.zip
Length Method Size Ratio Date Time CRC-32 Name
-------- ------ ------- ----- ---- ---- ------ ----
660997 Defl:N 163541 75% 25.01.05 23:05 52d702e8 kbpk.bbb
660997 Defl:N 122037 82% 25.01.05 23:05 6c3276de kbpk.bbw
-------- ------- --- -------
1321994 285578 78% 2 files
White to move:
failed = 0
won = 8283622 = 65.83% (95.95% of valid)
draw = 349608 = 02.78% (04.05% of valid)
lost = 0 = 00.00% (00.00% of valid)
invalid= 3949682 = 31.39%
Black to move (score is relative to black):
failed = 0
won = 0 = 00.00% (00.00% of valid)
draw = 1719846 = 13.67% (16.78% of valid)
lost = 8529618 = 67.79% (83.22% of valid)
invalid= 2333448 = 18.54%
Daniel Shawul wrote:Hi all
I want to add only KPK bitbase. If i understand correctly i have to
1) get results of positions from Nalimove tb's
2) put it in an array
3) calculate index and fetch result
how to calculate index?? Or do i have to use hashing?
daniel
uint8 KPK_bitbase[24576];
void init_kpk(void) {
FILE *f = fopen("kpk.bin", "rb");
int i;
if(f == NULL) {
printf("kpk.bin not found!\n");
quit();
}
for(i = 0; i < 24576; i++) KPK_bitbase[i] = fgetc(f);
fclose(f);
}
int probe_kpk(int wksq, int wpsq, int bksq, int side) {
int index;
wksq = COMPRESS(wksq); bksq = COMPRESS(bksq);
wpsq = (wpsq & 3) + ((wpsq / 16) - 1) * 4;
index = side + 2*bksq + 128*wksq + 8192*wpsq;
return (KPK_bitbase[index/8] & (1<<(index&7)));
}
Dieter B?r?ner wrote:Delfi uses bitbases compressed in RAM - yes? Do you have much expertize in compression? What would be a fast method, that is easily understandable, but still compresses reasonably? Your smart suggestions sound a bit too heave for "on the way" processing. Or do you intend to use this during probing? Just RLE will for general files probably not compress very well.
Fabien Letouzey wrote:I have some expertise in data compression, although I have not experimented with win/loss/draw TB compression yet. I think Shaeffer's approach in the last Chinook paper (I think the one on 10-man DB) is in the right direction. I call this kind of algorithm "RLH" = RLE + Huffman.
Of course for quick experiments one can start with zlib on small blocks. I'd be curious what the overhead is.
Dann Corbit wrote:64 bit computers will make addressing of 7 man bitbase files simple.
Assume that you have a very large address space and also the memory to address. It will be true before long.
Return to Programming and Technical Discussions
Users browsing this forum: No registered users and 4 guests