Revised source for the random game generator

Discussion of chess software programming and technical issues.

Moderators: Harvey Williamson, bob, hgm

Forum rules
This textbox is used to restore diagrams posted with the [d] tag before the upgrade.
Post Reply
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 6:43 pm

Revised source for the random game generator

Post by sje » Tue May 12, 2015 2: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&#41; && &#40;index < rgrptr->limit&#41;)
  &#123;
    const PosTerm posterm = position.RandomGame&#40;prng&#41;;
    const ui plycount = position.GetPlyCount&#40;);

    rgrptr->ptvec&#91;posterm&#93;++;
    rgrptr->totalply += plycount;
    rgrptr->plymax = std&#58;&#58;max&#40;plycount, rgrptr->plymax&#41;;
    index++;
  &#125;;
  return 0;
&#125;

void IntCoPro&#58;&#58;DoRandomGames&#40;void&#41;
&#123;
  const ui64 limit = &#40;ui64&#41; strtoll&#40;tokens.Arg&#40;1&#41;.c_str&#40;), 0, 10&#41;;
  const ui distthreadcount = DIPtr->GetDistThreadCount&#40;);
  const ui64 splitlimit = limit / distthreadcount;

  Thread *threadptrvec&#91;DistThreadLen&#93;;
  RgRec rgvec&#91;DistThreadLen&#93;;
  ui64 ptvec&#91;PosTermLen&#93;;
  ui64 totalply = 0;
  ui plymax = 0;

  // Reset the reportable results

  ForEachPosTerm&#40;posterm&#41;
    ptvec&#91;posterm&#93; = 0;

  // Create the threads

  ZOL&#40;index, distthreadcount&#41;
  &#123;
    RgRec * const rgrptr = &rgvec&#91;index&#93;;

    rgrptr->stopptr = &DIPtr->PendingSignals&#91;SignalInt&#93;;

    if &#40;index < &#40;distthreadcount - 1&#41;)
      rgrptr->limit = splitlimit;
    else
      rgrptr->limit = limit - &#40;splitlimit * &#40;distthreadcount - 1&#41;);

    // Start the thread

    threadptrvec&#91;index&#93; = new Thread&#40;RgTask, rgrptr&#41;;
  &#125;;

  // Destroy the threads

  ZOL&#40;index, distthreadcount&#41;
  &#123;
    const RgRec * const rgrptr = &rgvec&#91;index&#93;;

    // End the thread

    delete threadptrvec&#91;index&#93;;

    ForEachPosTerm&#40;posterm&#41;
      ptvec&#91;posterm&#93; += rgrptr->ptvec&#91;posterm&#93;;
    totalply += rgrptr->totalply;
    plymax = std&#58;&#58;max&#40;rgrptr->plymax, plymax&#41;;
  &#125;;

  // In case of interrupt signal

  DIPtr->PendingSignals&#91;SignalInt&#93; = false;

  // Calculate actual game count

  ui64 gamecount = 0;

  ForEachPosTerm&#40;posterm&#41;
    gamecount += ptvec&#91;posterm&#93;;

  // Report

  ForEachPosTerm&#40;posterm&#41;
    if &#40;posterm != PosTermUnterminated&#41;
    &#123;
      const ui64 count = ptvec&#91;posterm&#93;;
      const double ratio = count / &#40;double&#41; gamecount;
      std&#58;&#58;ostringstream oss;

      oss <<
        std&#58;&#58;setw&#40;12&#41; << PosTermNameVec&#91;posterm&#93; << " " <<
        std&#58;&#58;setw&#40;8&#41; << count << " " <<
        std&#58;&#58;setprecision&#40;6&#41; << ratio;
      WriteLn&#40;oss.str&#40;));
    &#125;;

  WriteLn&#40;"Average ply length&#58; " + EncodeDouble&#40;totalply / &#40;double&#41; gamecount&#41;);
  WriteLn&#40;"Maximum ply length&#58; " + EncodeUi&#40;plymax&#41;);
&#125;

User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 6:43 pm

For making a single random game

Post by sje » Tue May 12, 2015 2:53 pm

For making a single random game, some code from the Position class:

Code: Select all

Move Position&#58;&#58;SelectRandomMove&#40;PRNG& prng&#41; const
&#123;
  Move move;
  GMVec gmvec;

  Generate&#40;gmvec&#41;;
  if &#40;gmvec.GetCount&#40;) == 0&#41;
    move.SetVoid&#40;);
  else
    move = gmvec&#91;prng.Pick&#40;gmvec.GetCount&#40;))&#93;.GetMove&#40;);
  return move;
&#125;

PosTerm Position&#58;&#58;RandomGame&#40;PRNG& prng&#41;
&#123;
  RetractAll&#40;);
  while (!IsGameOver&#40;))
    Execute&#40;SelectRandomMove&#40;prng&#41;);
  return CalcPosTerm&#40;);
&#125;
And:

Code: Select all

PosTerm Position&#58;&#58;CalcPosTerm&#40;void&#41; const
&#123;
  PosTerm posterm;

  if &#40;HasNoMoves&#40;))
  &#123;
    if &#40;inch&#41;
      posterm = PosTermCheckmate;
    else
      posterm = PosTermStalemate;
  &#125;
  else
  &#123;
    if &#40;IsDrawnFiftyMoves&#40;))
      posterm = PosTermFiftyMoves;
    else
    &#123;
      if &#40;IsDrawnInsufficient&#40;))
        posterm = PosTermInsufficient;
      else
      &#123;
        if &#40;IsDrawnRepetition&#40;))
          posterm = PosTermRepetition;
        else
          posterm = PosTermUnterminated;
      &#125;;
    &#125;;
  &#125;;
  return posterm;
&#125;
Note in the above routine the priority assigned to the five different game terminations, something not completely specified by FIDE.

User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 6:43 pm

Forgot to mention

Post by sje » Tue May 12, 2015 3: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&#40;v, limit&#41;   for &#40;ui   v = 0; v < &#40;limit&#41;; v++)
#define ZOL64&#40;v, limit&#41; for &#40;ui64 v = 0; v < &#40;limit&#41;; v++)

mar
Posts: 2185
Joined: Fri Nov 26, 2010 1:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: For making a single random game

Post by mar » Tue May 12, 2015 3: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? :)

User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 6:43 pm

Re: For making a single random game

Post by sje » Tue May 12, 2015 4: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? :)

Code: Select all

// Zero origin loops &#40;ZOL&#41;

mar
Posts: 2185
Joined: Fri Nov 26, 2010 1:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: For making a single random game

Post by mar » Tue May 12, 2015 4:28 pm

sje wrote:

Code: Select all

// Zero origin loops &#40;ZOL&#41;
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.

Post Reply