Moderator: Pachnes
QuakePhil wrote:Currently my tablebase is indexed using the side-to-move information which is probably not required? And also the last piece moved square - information necessary for move generator because if this last piece can be captured, then it must be captured.
QuakePhil wrote:Then for three pieces its WWB + WBB + WBb + Wbb + WwB + wwB = 195 MB:(
syzygy wrote:What you are trying to do, is encode positions after each capture, i.e. after "sub-plies". As a result, you need to encode the piece-that-is-moving. This is very very suboptimal.
QuakePhil wrote:While it's true that in my engine I've broken down single-ply captures that involve multiple captures into sub-plies, in other words a1xa3xa5 is actually represented as 2 plys worth of moves: move a1xa3 (with same side to move) and then move a3xa5 (with next side to move). However, the tactical square information is still required - whether you are doing multiple ply captures or single ply captures. It is part of the rule (of this particular variant, not of turkish checkers in general) that if you are able to take the piece which moved last, then you are required to take (and any other moves are illegal) This rule applies after any kind of capture or quiet move.
The tip about WBB vs WWB is very good, I will use that! It sheds light on the "only one side computation is required". As you say, this is applicable in general, so WwB can become BbW, etc. This should effectively halve the size of tablebases. Thanks!
function build(wpawns,bpawns,wkings,bkings)
{
n/a_count = n/a_count_last_pass = 0;
do
{
n/a_count = build2(...);
if (n/a_count == 0)
return 0; // everything has been calculated
if (n/a_count > 0 && n/a_count == n/a_count_last_pass)
return 1; // nothing new being calculated
n/a_count_last_pass = n/a_count;
}
while (n/a_count > 0);
return 0;
}
function build2(wpawns,bpawns,wkings,bkings)
{
for (each position in this pawns + kings setup)
{
if (dtm[position]) != n/a) continue;
decode(position);
generate_moves(position);
sofar = n/a;
all_moves_known = 1;
if (no moves)
{
evaluate(); // this should be a basic "decision()" with no positional calculations, just basic q: does each side have material?
if (player which just moved has won)
sofar = lose in 0; // reversed because this is only ever probed from one move up
else if (player which just moved has lost)
sofar = win in 0;
else
sofar = draw;
}
else for(each move)
{
do_move();
probe = encode_and_test();
if (probe != n/a)
{
if (same side to move)
sofar = MAX(-next_dtm[probe], sofar); // -next_dtm will transform "win in 1" to "win in 2", "win in 2" to "win in 3", etc
else
sofar = MAX(next_dtm[probe], sofar); // next_dtm will transform "win in 1" to "lose in 2", "lose in 2" to "win in 3", etc
}
if (probe == n/a)
{
if (K-K)
{
evaluate();
if (player which just moved has won)
sofar = MAX(win in 1, sofar); // MAX(win in 1, win in 0) will return win in 0
else
sofar = MAX(draw, sofar);
}
else
all_moves_known = 0;
}
undo_move();
}
if (all_moves_known == 0) // here comes the really bonehead part
{
if (sofar > 0) // if "win in x"
{ // preserve known wins for side to move (losses don't work)
; // this way subsequent passes will calculate longer winning variations
} // until there is no more to calculate
else
sofar = n/a;
}
dtm[position] = sofar; // store dtm
}
return number_of_n/a_stored_this_pass;
}
dtm[200MB] = {n/a,n/a, ... , n/a}; // lol, this will be 2MB with the space savings discussed in this thread
build(0,0,1,1); // K-K
build(0,0,2,0); // KK- // for positions rechable from KK-K
build(0,0,0,2); // -KK // e.g. W - B W - - - - ; white to move
do {
loops = 0
loops += build(0,0,2,1); // KK-K
loops += build(0,0,1,2); // K-KK
loops += build(0,1,1,1); // K-Km
// etc...
if (loops > 0 && loops == oldloops)
break;// infinite loop break // can occur because not all dtm are reached
// because of stupid stuff like pawns on 8th rank, etc... // ideally this should never happen
oldloops = loops;
} while (loops > 0);
QuakePhil wrote:I see what you mean now with regards to reducing size -- any positions which are "forcing" need not be stored/indexed. If I'm understanding that correctly, but then it smells to me like complicated bug prone code for unknown amount of space saving... Definitely something to try but at a later stage.
h.g.muller wrote:If you unmove from a position where a capture is possible, only uncaptures would be legal.
h.g.muller wrote:I usually build a table together with all possible successor tables, (stored in the broken positions), as my generator is purely RAM based, and doesn't do any disk acces at all. So it has to start from scratch every time.
But even when you build table by table, the problem I sketched would occur when 'seeding' the (n+1)-men table from the n-men table. You have to undo e.p. capture from every position in the successor with a 6th-rank Pawn to get to the table with one more Pawn, and you would get to a position with e.p. rights then.
syzygy wrote:Do you generate everything as one big table, or do you generate the smaller tables before the larger tables? If the latter, there is not much difference.
It is not necessay to retrograde from an already generated table or (pawn) slice to the table or slice being generated. You can simply loop once through the table or slice being generated and perform the forward moves into the earlier table or slice. You then retrograde from the positions that were solved in this loop.
For regular chess I do in fact retrograde from (non-pawn-)captures in pawnless tables and within pawn slices, but I don't retrograde from pawn moves and pawn captures (instead I perform forward moves).
Retrograding from captures is a good thing, since it is easy enough to implement and it is more efficient as well (given that there will usually be many "capture" positions that result in the same "captured" position). Retrograding from pawn moves is not important from an efficiency point of view: pawn moves and captures are 1-1 or 2-1. An n-piece position with a pawn on c5 can only come from an n-piece position with a pawn on c4. An n-piece position with a pawn on c4 can only come from an n-piece position with a pawn on c2 or c3. For pawn captures it is similar.
In atomic chess (in which a capture also captures the surrounding pieces) retrograding from captures is a terrible nuisance, so I resolve captures by forward moves. Not as efficient, but there is not really a practical alternative.
In Turkish checkers, it seems that forward moves are the practical way to go for resolving captures.
h.g.muller wrote:Interesting you did atomic. Did you publish any statistics for 3-5-men endings for that?
Users browsing this forum: Google [Bot] and 0 guests