Uri Blass wrote:I agree that calculating perft(14) is not a good way to verify the correctness of a move generator.
You are correct.
I did not claim that
perft(14) would properly exercise all of the generate/execute/retract code in any program. But it does quite well for those positions which can be reached. Further, as
Symbolic is primarily a chess program and not a
perft() program. Many, many routines which have nothing to do with
perft() are exercised. That's in part why
Symbolic is a slow
perft() calculator relative to at least some specialized
perft() programs.
One way I use to verify move generation is to feed various well-known FEN files (e.g., BWTC, WAC, WCSAC, etc.) into the program being tested and run
perft() for various depths and checking the results with a known good program.
A more thorough way is to use the random game generator code and compare the generation count for each position between the program being tested and a known good generator. It's tough for a bug to hide in hundreds of billions random positions. This technique is also good for testing specialized generators: gainer moves only, checking moves only, etc.
Symbolic's source is 21,135 lines long. The source specific to it's
perft() transposition code is less than 0.5% of that, only 100 lines:
Code: Select all
NodeCount Position::PathCountTran(const ui depth, PCTBasePtr baseptr)
{
typedef enum
{
EsInit,
EsInitPly,
EsExecute,
EsTermPly,
EsTerm,
EsExit
} Es;
NodeCount total;
Es state = EsInit;
ui ply;
PcPIRList pcpirlist;
PcPIRNodePtr pnptr;
while (state != EsExit)
switch (state)
{
case EsInit:
ply = 0;
pcpirlist.Append(new PcPIRNode());
pnptr = pcpirlist.GetHead();
state = EsInitPly;
break;
case EsInitPly:
pnptr->Reset();
if (ply == depth)
{
pnptr->sum = 1;
state = EsTermPly;
}
else
{
if (ply == (depth - 1))
{
pnptr->sum = CountMoves();
state = EsTermPly;
}
else
{
if (baseptr->Probe(*this, (depth - ply), pnptr->sum))
state = EsTermPly;
else
{
Generate(pnptr->gmvec);
state = EsExecute;
};
};
};
break;
case EsExecute:
if (pnptr->AllMovesUsed())
{
baseptr->Stash(*this, (depth - ply), pnptr->sum);
state = EsTermPly;
}
else
{
Execute(pnptr->NextMove());
ply++;
if (pcpirlist.GetCount() == ply)
pcpirlist.Append(new PcPIRNode());
pnptr = pnptr->GetNext();
state = EsInitPly;
};
break;
case EsTermPly:
if (ply == 0)
{
total = pnptr->sum;
state = EsTerm;
}
else
{
pnptr->GetPrev()->sum += pnptr->sum;
ply--;
pnptr = pnptr->GetPrev();
Retract();
state = EsExecute;
};
break;
case EsTerm:
pcpirlist.Reset();
state = EsExit;
break;
default:
break;
};
return total;
}