Revised source for the random game generator

Discussion of chess software programming and technical issues.

Moderator: Ras

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

Revised source for the random game generator

Post by sje »

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));
}
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

For making a single random game

Post by sje »

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.
User avatar
sje
Posts: 4675
Joined: Mon Mar 13, 2006 7:43 pm

Forgot to mention

Post by sje »

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

Re: For making a single random game

Post by mar »

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 7:43 pm

Re: For making a single random game

Post by sje »

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 (ZOL)
mar
Posts: 2668
Joined: Fri Nov 26, 2010 2:00 pm
Location: Czech Republic
Full name: Martin Sedlak

Re: For making a single random game

Post by mar »

sje wrote:

Code: Select all

// Zero origin loops (ZOL)
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.