Discussion of chess software programming and technical issues.
Moderator: Ras
sje
Posts: 4675 Joined: Mon Mar 13, 2006 7:43 pm
Post
by sje » Tue May 12, 2015 4:45 pm
Revised source for the random game generator
I've modified Symbolic's random game generator so that each thread has its own non-thread-safe (fast) PRNG and all threads poll the same early-quit interrupt flag.
Code: Select all
typedef struct
{
volatile bool *stopptr;
ui64 limit;
ui64 ptvec[PosTermLen];
ui64 totalply;
ui plymax;
} RgRec;
static void * RgTask(void *ptr)
{
RgRec * const rgrptr = (RgRec *) ptr;
Position position;
PRNG prng(false);
ui64 index = 0;
ForEachPosTerm(posterm)
rgrptr->ptvec[posterm] = 0;
rgrptr->totalply = 0;
rgrptr->plymax = 0;
position.LoadInitialArray();
while (!(*rgrptr->stopptr) && (index < rgrptr->limit))
{
const PosTerm posterm = position.RandomGame(prng);
const ui plycount = position.GetPlyCount();
rgrptr->ptvec[posterm]++;
rgrptr->totalply += plycount;
rgrptr->plymax = std::max(plycount, rgrptr->plymax);
index++;
};
return 0;
}
void IntCoPro::DoRandomGames(void)
{
const ui64 limit = (ui64) strtoll(tokens.Arg(1).c_str(), 0, 10);
const ui distthreadcount = DIPtr->GetDistThreadCount();
const ui64 splitlimit = limit / distthreadcount;
Thread *threadptrvec[DistThreadLen];
RgRec rgvec[DistThreadLen];
ui64 ptvec[PosTermLen];
ui64 totalply = 0;
ui plymax = 0;
// Reset the reportable results
ForEachPosTerm(posterm)
ptvec[posterm] = 0;
// Create the threads
ZOL(index, distthreadcount)
{
RgRec * const rgrptr = &rgvec[index];
rgrptr->stopptr = &DIPtr->PendingSignals[SignalInt];
if (index < (distthreadcount - 1))
rgrptr->limit = splitlimit;
else
rgrptr->limit = limit - (splitlimit * (distthreadcount - 1));
// Start the thread
threadptrvec[index] = new Thread(RgTask, rgrptr);
};
// Destroy the threads
ZOL(index, distthreadcount)
{
const RgRec * const rgrptr = &rgvec[index];
// End the thread
delete threadptrvec[index];
ForEachPosTerm(posterm)
ptvec[posterm] += rgrptr->ptvec[posterm];
totalply += rgrptr->totalply;
plymax = std::max(rgrptr->plymax, plymax);
};
// In case of interrupt signal
DIPtr->PendingSignals[SignalInt] = false;
// Calculate actual game count
ui64 gamecount = 0;
ForEachPosTerm(posterm)
gamecount += ptvec[posterm];
// Report
ForEachPosTerm(posterm)
if (posterm != PosTermUnterminated)
{
const ui64 count = ptvec[posterm];
const double ratio = count / (double) gamecount;
std::ostringstream oss;
oss <<
std::setw(12) << PosTermNameVec[posterm] << " " <<
std::setw(8) << count << " " <<
std::setprecision(6) << ratio;
WriteLn(oss.str());
};
WriteLn("Average ply length: " + EncodeDouble(totalply / (double) gamecount));
WriteLn("Maximum ply length: " + EncodeUi(plymax));
}
sje
Posts: 4675 Joined: Mon Mar 13, 2006 7:43 pm
Post
by sje » Tue May 12, 2015 4:53 pm
For making a single random game, some code from the
Position class:
Code: Select all
Move Position::SelectRandomMove(PRNG& prng) const
{
Move move;
GMVec gmvec;
Generate(gmvec);
if (gmvec.GetCount() == 0)
move.SetVoid();
else
move = gmvec[prng.Pick(gmvec.GetCount())].GetMove();
return move;
}
PosTerm Position::RandomGame(PRNG& prng)
{
RetractAll();
while (!IsGameOver())
Execute(SelectRandomMove(prng));
return CalcPosTerm();
}
And:
Code: Select all
PosTerm Position::CalcPosTerm(void) const
{
PosTerm posterm;
if (HasNoMoves())
{
if (inch)
posterm = PosTermCheckmate;
else
posterm = PosTermStalemate;
}
else
{
if (IsDrawnFiftyMoves())
posterm = PosTermFiftyMoves;
else
{
if (IsDrawnInsufficient())
posterm = PosTermInsufficient;
else
{
if (IsDrawnRepetition())
posterm = PosTermRepetition;
else
posterm = PosTermUnterminated;
};
};
};
return posterm;
}
Note in the above routine the priority assigned to the five different game terminations, something not completely specified by FIDE.
sje
Posts: 4675 Joined: Mon Mar 13, 2006 7:43 pm
Post
by sje » Tue May 12, 2015 5:04 pm
Forgot to mention a pair of very handy preprocessor macros I've used for a long time:
Code: Select all
// Zero origin loops
#define ZOL(v, limit) for (ui v = 0; v < (limit); v++)
#define ZOL64(v, limit) for (ui64 v = 0; v < (limit); v++)
mar
Posts: 2668 Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak
Post
by mar » Tue May 12, 2015 5:30 pm
Looks nice.
I have to say I'm not a big fan of unnecessary nesting though (CalcPosTerm) as it hurts readability IMHO.
As for your ZOL, I have seen similar macros elsewhere, it was called loopi(n), loopj(n) etc,
to be honest ZOL sounds weird and I would have no idea what it stands for (unless I'm familiar with your code) - why not simply call it FOR or LOOP or loop?
sje
Posts: 4675 Joined: Mon Mar 13, 2006 7:43 pm
Post
by sje » Tue May 12, 2015 6:08 pm
mar wrote: Looks nice.
I have to say I'm not a big fan of unnecessary nesting though (CalcPosTerm) as it hurts readability IMHO.
As for your ZOL, I have seen similar macros elsewhere, it was called loopi(n), loopj(n) etc,
to be honest ZOL sounds weird and I would have no idea what it stands for (unless I'm familiar with your code) - why not simply call it FOR or LOOP or loop?
mar
Posts: 2668 Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak
Post
by mar » Tue May 12, 2015 6:28 pm
Yes I've read that and I still think it's a weird name, using say XGQ would be equally descriptive
Not to mention that I don't understand why explicitly state it's a zero origin loop when 9 of 10 for loops start with 0, but it's your code of course, just my two cents.
Quoting Linus:
Code: Select all
if you need more than 3 levels of indentation, you're screwed anyway, and should fix your program
This is maybe a bit extreme but I agree that excessive nesting is bad, YMMV.