Computer Chess Club Archives


Search

Terms

Messages

Subject: Re: Hashing of beta bound (long post)

Author: Michel Langeveld

Date: 08:49:11 12/06/01

Go up one level in this thread


On December 06, 2001 at 08:31:44, Tony Werten wrote:

>On December 06, 2001 at 07:58:14, Michel Langeveld wrote:
>
>>I have a problem in my program (nullmover).
>>
>>My program sees at an early ply (ply 7) already a mate in 5 with extensions and
>>qsearch. This with hashing of alpha bounds and truebounds.
>>
>>When I hash also my beta bounds nullmover does not see it anymore. Does anybody
>>has an idea how this could happen?
>
>Guessing from this little information: Your storing/retrieving of the
>checkmatescore goes wrong.
>
>Tony
>
>>
>>regards,
>>
>>Michel

Thanks for the tip. I have to debug that part then. To be more precise. Here's a
part of my source code. As you can see I have special parts for black and
special parts for white.

//////////////////////////////////////////////////////
//Qsearch of white
//Here only captures and promotions are searched
//////////////////////////////////////////////////////
scoreType qsearchWhite(scoreType alpha, scoreType beta)
{
   nodeCounter++;

   moveListType ml;

   log << cin.eof() << endl;
   log << cin.rdbuf()->in_avail() << endl;

   checkTest = attackedByBlack(p.whiteKingField);

   if (isWhiteCantMove() )
   {
      //mate or stalemate
      if (checkTest)  //checkTest is set in previous function
      {
         return -9999;
      }
      else
      {
         return 0;
      }
   }

   scoreType val = generatePositionScore2();

#ifndef QSEARCH
   return val;
#endif

   if (val >= beta)
   {
        return beta;
   }

   if (val > alpha)
   {
        alpha = val;
   }

   generateWhiteCapturesMoveList( &ml );
   sortMoveList( &ml );

   //just for debugging purposes
   if (ml.number > 39)
   {
      printPosition(cout, p, true);
      cerr << "!! new maximum found of: " << ml.number << endl;
      exit(1);
   }

   hashType oldHash = p.dynHash;
   char oldRights = p.castlingRights;
   fieldType oldEpField = p.epField;

   for (int i=0; i< ml.number; i++)
   {
      doWhiteMove(ml.moves[i]);
      val = -qsearchBlack(-beta, -alpha);
      restoreWhiteMove(ml.moves[i], oldEpField, oldRights, oldHash);

        if (val >= beta) return beta;
        if (val > alpha) alpha = val;
    }
    return alpha;
}


//////////////////////////////////////////////////////
//Qsearch of black
//Here only captures and promotions are searched
//////////////////////////////////////////////////////
scoreType qsearchBlack(scoreType alpha, scoreType beta)
{
   nodeCounter++;

   moveListType ml;

   checkTest = attackedByWhite(p.blackKingField);

   if (isBlackCantMove() )
   {
      //mate or stalemate
      if (checkTest) //checkTest is set in previous function
      {
         return -9999;
      }
      else
      {
         return 0;
      }
   }

   scoreType val = -generatePositionScore2();

#ifndef QSEARCH
   return val;
#endif

   if (val >= beta)
   {
        return beta;
   }

    if (val > alpha)
   {
        alpha = val;
   }

   generateBlackCapturesMoveList( &ml );
   sortMoveList( &ml );
   if (ml.number > 39)
   {
      printPosition(cout, p, true);
      cerr << "!! new maximum found of: " << ml.number << endl;
      exit(1);
   }

   hashType oldHash = p.dynHash;
   char oldRights = p.castlingRights;
   fieldType oldEpField = p.epField;

   for (int i=0; i< ml.number; i++)
   {
      doBlackMove(ml.moves[i]);
      val = -qsearchWhite(-beta, -alpha);
      restoreBlackMove(ml.moves[i], oldEpField, oldRights, oldHash);

        if (val >= beta) return beta;
        if (val > alpha) alpha = val;
   }

   return alpha;
}

//////////////////////////////////////////////////////
//Look in the hashtable if our move is present.
//////////////////////////////////////////////////////
scoreType probeHash(int depth, scoreType alpha, scoreType beta /*, moveType
&bestMove*/)
{
   hashTableType *pHashRecord = &hashTable[p.dynHash & HASHMASK];

   if (pHashRecord->key == p.dynHash)
   {
      if (pHashRecord->depth >= depth)
      {
         switch(pHashRecord->flags)
         {
            case HASH_FLAGS_EXACT:
            {
               return pHashRecord->value;
            }
            case HASH_FLAGS_ALPHA:
            {
               if (pHashRecord->value <= alpha)
               {
                  return alpha;
               }
               break;
            }
            case HASH_FLAGS_BETA:
            {
               if (pHashRecord->value >= beta)
               {
                  return beta;
               }
               break;
            }
         }
      }
   }

   return HASH_UNKNOWN_VALUE;
}


//////////////////////////////////////////////////////
//Record the hashmove
//////////////////////////////////////////////////////
void recordHash(int depth, scoreType value, int flags /*, moveType &bestMove*/)
{
    hashTableType *pHashRecord = &hashTable[p.dynHash & HASHMASK];

    if (depth >= pHashRecord->depth)
    {
       pHashRecord->key = p.dynHash;
       pHashRecord->value = value;
       pHashRecord->flags = flags;
       pHashRecord->depth = depth;
       //pHashRecord->bestMove = bestMove;
    }
}

//////////////////////////////////////////////////////
//Alphabeta for white
//////////////////////////////////////////////////////
#ifdef LINE
scoreType alphaBetaWhite(int depth, scoreType alpha, scoreType beta,struct
historyListType* pline)
#else
scoreType alphaBetaWhite(int depth, scoreType alpha, scoreType beta)
#endif
{
   nodeCounter++;

   moveListType ml;
#ifdef LINE
   struct historyListType line;
#endif

#ifdef DOHASHWHITE
   //moveType bestHashMove;
   scoreType hashvalue = probeHash(depth, alpha, beta/*, bestHashMove*/);
   int hashflags;

   if (hashvalue != HASH_UNKNOWN_VALUE)
   {
#ifdef LINE
      pline->number = 0;
#endif
      return hashvalue;
   }
#endif

   if (depth <= 0)
   {
      nodeCounter--; //don't add the nodecounter too much
      scoreType qsearchvalue = qsearchWhite(alpha, beta);
#ifdef HASHQSEARCH
      if (qsearchvalue <= alpha)
      {
         recordHash(depth, alpha, HASH_FLAGS_ALPHA/*, bestHashMove*/);
      }
      else if (qsearchvalue >= beta)
      {
         recordHash(depth, beta, HASH_FLAGS_BETA/*, bestHashMove*/);
      }
      else
      {
         recordHash(depth, qsearchvalue, HASH_FLAGS_EXACT/*, bestHashMove*/);
      }
#endif
#ifdef LINE
      pline->number = 0;
#endif
      return qsearchvalue;
   }

   checkTest = attackedByBlack(p.whiteKingField);

   if (isWhiteCantMove() )
   {
      //mate or stalemate
      if (checkTest)  //checkTest is set in previous function
      {
#ifdef LINE
         pline->number = 0;
#endif
         return -9999;
      }
      else
      {
#ifdef LINE
         pline->number = 0;
#endif
         return 0;
      }
   }

   scoreType val;

   hashType oldHash = p.dynHash;
   char oldRights = p.castlingRights;
   fieldType oldEpField = p.epField;

#ifdef NULLMOVE_ON
   //don't do this if:
   //1) We have already done a nullmove
   //2) We come into qsearch immediatly
   //3) We are in check
   if (!nullmoved && depth - 1 - R >= 0 && !checkTest )
   {
      //do nullmove
      doWhiteNullMove();
#ifdef LINE
      val = -alphaBetaWhite(depth - 1 - R, -beta, -beta+1, &line);
#else
      val = -alphaBetaWhite(depth - 1 - R, -beta, -beta+1);
#endif

      restoreWhiteNullMove(oldEpField, oldHash);

      if (val >= beta)
      {
         //set move so we  can use this as best move
         moveType m;
         m.moveKind = NULLMOVE;

#ifdef LINE
         pline->moves[0] = m;
         memcpy((struct moveType*)&pline->moves[1], line.moves, line.number *
sizeof(moveType));
         pline->number = line.number+1;
#endif

         return beta;
      }
   }
#endif

   generateWhiteMoveList( &ml );
   sortMoveList( &ml );

   int orgDepth = depth;

   //extend moves that walk away from check
   if (checkTest)
   {
      depth++;
   }
   //extend single moves
   else if (ml.number == 1)
   {
      depth++;
   }
   //pawn push extension
   //else if (p.board[p.history.moves[p.history.number-1].toField] == BLACKPAWN)
   //
   //   depth++;
   //}

#ifdef SEARCHINCDEPTH_ON_WHITE
   scoreType orgAlpha = alpha;
   scoreType orgBeta  = beta;

   for (int incdepth=1; incdepth<=depth; incdepth++)
   {
      alpha = orgAlpha;
      beta = orgBeta;
#else
   int incdepth=depth;
   {
#endif
      hashflags = HASH_FLAGS_ALPHA;

      for (int i=0; i< ml.number; i++)
      {
         doWhiteMove(ml.moves[i]);
          val = -alphaBetaBlack(incdepth-1, -beta, -alpha
#ifdef LINE
            , &line
#endif
            );
         restoreWhiteMove(ml.moves[i], oldEpField, oldRights, oldHash);

         if (incdepth < depth)
         {
            if (val > alpha)
            {
               #ifdef DOHASHWHITEINC
                  hashflags = HASH_FLAGS_EXACT;
               #endif
               alpha = val;

               #ifdef TOP_ON
                  topMoveList(&ml, i);
               #endif
            }
            if (val >= beta)
            {
               #ifdef HASHBETASINC
                  hashflags = HASH_FLAGS_BETA;
                  recordHash(incdepth, beta, HASH_FLAGS_BETA);
               #endif
               break;
            }
         }
         else
         {
            if (val >= beta)
            {
               #ifdef HASHBETAS
                  recordHash(depth, beta, HASH_FLAGS_BETA /*, ml.moves[i]*/);
               #endif

#ifdef LINE
               pline->moves[0] = ml.moves[i];
               memcpy((struct moveType*)&pline->moves[1], line.moves,
line.number * sizeof(moveType));
               pline->number = line.number+1;
#endif

               return beta;
            }
            if (val > alpha)
            {
               #ifdef DOHASHWHITE
                  hashflags = HASH_FLAGS_EXACT;
               #endif
               alpha = val;

#ifdef LINE
               pline->moves[0] = ml.moves[i];
               memcpy((struct moveType*)&pline->moves[1], line.moves,
line.number * sizeof(moveType));
               pline->number = line.number+1;
#endif
            }
         }
      }
   }

   #ifdef DOHASHWHITE
      //moveType move;
      recordHash(depth, alpha, hashflags/*, move*/);
   #endif


   return alpha;
}

//////////////////////////////////////////////////////
//Alphabeta for black
//////////////////////////////////////////////////////
scoreType alphaBetaBlack(int depth,
                   scoreType alpha,
                   scoreType beta
#ifdef LINE
                  ,struct historyListType* pline
#endif
                  )

{
   nodeCounter++;
   moveListType ml;
#ifdef LINE
   struct historyListType line;
#endif

#ifdef DOHASHBLACK
   //moveType bestHashMove;
   scoreType hashvalue = probeHash(depth, alpha, beta/*, bestHashMove*/);
   int hashflags;

   if (hashvalue != HASH_UNKNOWN_VALUE)
   {
#ifdef LINE
      pline->number = 0;
#endif
      return hashvalue;
   }
#endif

   if (depth <= 0)
   {
      nodeCounter--; //don't add the nodecounter too much
      scoreType qsearchvalue = qsearchBlack(alpha, beta);
#ifdef HASHQSEARCH
      if (qsearchvalue <= alpha)
      {
         recordHash(depth, alpha, HASH_FLAGS_ALPHA/*, bestHashMove*/);
      }
      else if (qsearchvalue >= beta)
      {
         recordHash(depth, beta, HASH_FLAGS_BETA/*, bestHashMove*/);
      }
      else
      {
         recordHash(depth, qsearchvalue, HASH_FLAGS_EXACT/*, bestHashMove*/);
      }
#endif
#ifdef LINE
      pline->number = 0;
#endif
      return qsearchvalue;
   }

   checkTest = attackedByWhite(p.blackKingField);

   if (isBlackCantMove() )
   {
      //mate or stalemate
      if (checkTest) //checkTest is set in previous function
      {
#ifdef LINE
         pline->number = 0;
#endif
         return -9999;
      }
      else
      {
#ifdef LINE
         pline->number = 0;
#endif
         return 0;
      }
   }

   scoreType val;

   hashType oldHash = p.dynHash;
   char oldRights = p.castlingRights;
   fieldType oldEpField = p.epField;

#ifdef NULLMOVE_ON
   //don't do this if:
   //1) We have already done a nullmove
   //2) We come into qsearch immediatly
   //3) We are in check
   if (!nullmoved && depth - 1 - R >= 0 && !checkTest )
   {
      //do nullmove
      doBlackNullMove();

      val = -alphaBetaWhite(depth - 1 - R, -beta, -beta+1
#ifdef LINE
         , &line
#endif
         );
      restoreBlackNullMove(oldEpField, oldHash);
      if (val >= beta)
      {
         //set move so we  can use this as best move
         moveType m;
         m.moveKind = NULLMOVE;

         pline->moves[0] = m;
         memcpy((struct moveType*)&pline->moves[1], line.moves, line.number *
sizeof(moveType));
         pline->number = line.number+1;

         return beta;
      }
   }
#endif

   checkTest = attackedByWhite(p.blackKingField);
   generateBlackMoveList( &ml );
   sortMoveList( &ml );
   int orgDepth = depth;

   //extend moves that walk away from check
   if (checkTest)
   {
      depth++;
   }
   //extend single moves
   else if (ml.number == 1)
   {
      depth++;
   }
   //else if (p.board[p.history.moves[p.history.number-1].toField] == WHITEPAWN)
   //{
      //depth++;
   //}

#ifdef SEARCHINCDEPTH_ON_BLACK
   scoreType orgAlpha = alpha;
   scoreType orgBeta  = beta;

   for (int incdepth=1; incdepth<=depth; incdepth++)
   {

      alpha = orgAlpha;
      beta = orgBeta;
#else
   int incdepth=depth;
   {
#endif
      hashflags = HASH_FLAGS_ALPHA;

      for (int i=0; i< ml.number; i++)
      {
         doBlackMove(ml.moves[i]);
#ifdef LINE
          val = -alphaBetaWhite(incdepth-1, -beta, -alpha, &line);
#else
          val = -alphaBetaWhite(incdepth-1, -beta, -alpha);
#endif
         restoreBlackMove(ml.moves[i], oldEpField,oldRights,  oldHash);

         if (incdepth < depth)
         {
            if (val > alpha)
            {
               #ifndef DOHASHBLACKINC
                  hashflags = HASH_FLAGS_EXACT;
               #endif
               alpha = val;

               #ifdef TOP_ON
                  topMoveList(&ml, i);
               #endif
            }

            if (val >= beta)
            {
               #ifdef HASHBETASINC
                  hashflags = HASH_FLAGS_BETA;
               #endif
               break;
            }
         }
         else
         {
            if (val >= beta)
            {
               #ifdef HASHBETAS
                  hashflags = HASH_FLAGS_BETA;
                  recordHash(depth, beta, HASH_FLAGS_BETA /*, ml.moves[i]*/);
               #endif

#ifdef LINE
               pline->moves[0] = ml.moves[i];
               memcpy((struct moveType*)&pline->moves[1], line.moves,
line.number * sizeof(moveType));
               pline->number = line.number+1;
#endif

               return beta;
            }

            if (val > alpha)
            {
               #ifdef DOHASHBLACK
                  hashflags = HASH_FLAGS_EXACT;
               #endif
               alpha = val;

#ifdef LINE
               pline->moves[0] = ml.moves[i];
               memcpy((struct moveType*)&pline->moves[1], line.moves,
line.number * sizeof(moveType));
               pline->number = line.number+1;
#endif
            }
         }
      }
   }

   #ifdef DOHASHBLACK
      //moveType move;
      recordHash(depth, alpha, hashflags/*, move*/);
   #endif

   return alpha;
}

//////////////////////////////////////////////////////
//Alphabeta for the root node
//////////////////////////////////////////////////////
scoreType alphaBetaRoot(int depth, moveType* bestMove, bool printTrueScore)
{
   TimeStamp t;
   t.startTimer();

   nodeCounter = 1;

   moveListType ml;
#ifdef LINE
   struct historyListType bestline;
   struct historyListType line;
#endif

   //look if the side to move is in check
   //if so set a global variable checkTest which is used in the move generator.
   if (p.moveColor == WHITE)
   {
      checkTest = attackedByBlack(p.whiteKingField);
   }
   else
   {
      checkTest = attackedByWhite(p.blackKingField);
   }

   //Are we already mated?
   if (isMate())
   {
#ifdef LINE
      bestline.number = 0;
#endif
      return -9999;
   }

   //Are we stalemated?
   if (isStaleMate())
   {
#ifdef LINE
      bestline.number = 0;
#endif
      return 0;
   }

   //generate the movelist
   if (p.moveColor == WHITE)
   {
      generateWhiteMoveList( &ml );
   }
   else
   {
      generateBlackMoveList( &ml );
   }

   //sort the movelist
   //first are the captures
   sortMoveList(&ml);

   scoreType orgAlpha = -9999;
   scoreType orgBeta = 9999;
   scoreType alpha = orgAlpha;
   scoreType beta = orgBeta;
   char strScore[100];
   int incdepth;

   //we have only 1 move
   //searching makes no sense then
   if (ml.number == 1)
   {
      *bestMove = ml.moves[0];
      alpha = 0;
   }
   else
   {
      scoreType val;
      hashType oldHash = p.dynHash;
      char oldRights = p.castlingRights;
      fieldType oldEpField = p.epField;

      //loop all moves until depth-1 so that the depth is one smaller
      //than the one we give to this function

#ifdef SEARCHINCDEPTH_ON_ROOT
      for (incdepth=1; 1; incdepth++)
#else
      for (incdepth=depth; ; incdepth++)
#endif
      {

#ifdef ASP_WINDOW
         scoreType lastWindowAlpha = alpha;
         scoreType lastWindowBeta  = beta;
#endif

         if (!xboardflag)
         {
            cout << "search on ply: " << incdepth << " alpha = " << alpha << ","
<< "beta = " << beta << endl;
         }
         else
         {
            log << "search on ply: " << incdepth << " alpha = " << alpha << ","
<< "beta = " << beta << endl;
         }

         bool bestMoveFound = false;


         for (int i=0; i< ml.number; i++)
         {
            //set alpha and beta
            //for printTrueScore we do this each time
            //So that the searched move is allways between alpha and beta.
            if (printTrueScore)
            {
               alpha = -9999;
               beta = 9999;
            }

            if (p.moveColor == WHITE)
            {
               doWhiteMove(ml.moves[i]);
#ifdef LINE
               val = -alphaBetaBlack(incdepth-1, -beta, -alpha, &line);
#else
               val = -alphaBetaBlack(incdepth-1, -beta, -alpha);
#endif
               restoreWhiteMove(ml.moves[i], oldEpField, oldRights, oldHash);
            }
            else
            {
                 doBlackMove(ml.moves[i]);
#ifdef LINE
               val = -alphaBetaWhite(incdepth-1, -beta, -alpha, &line);
#else
               val = -alphaBetaWhite(incdepth-1, -beta, -alpha);
#endif
               restoreBlackMove(ml.moves[i], oldEpField, oldRights, oldHash);
            }

            p.dynHash = oldHash;
            p.castlingRights = oldRights;
            p.epField = oldEpField;



            //Set the score so we can print it later
            ml.moves[i].score = val;

            //we use !bestMoveFound here so that if the position is lost
            //and result val = -9999 we still store a move
            //so that we play always something
            if (val > alpha || !bestMoveFound)
            {
               bestMoveFound = true;
               alpha = val;
               *bestMove = ml.moves[i];

#ifdef LINE
               bestline.moves[0] = ml.moves[i];
               memcpy(&bestline.moves[1], line.moves, line.number *
sizeof(moveType));
               bestline.number = line.number+1;
#endif

               /*
               if (!xboardflag)
               {
                  printMove(cout, *bestMove, true);
                  cout << endl;
               }
               else
               {
                  printMove(log, *bestMove, true);
                  log << endl;
               }*/

#ifdef TOP_ON
               topMoveList(&ml, i);
#endif

               sprintf(strScore, "%d", alpha);
            }

            //first set move... then do a break
            //so this is an different order as the normal alpha beta
            if (val >= beta)
            {
               break;
            }
         } //end moves
#ifdef ASP_WINDOW

/*
         if (!xboardflag)
         {
            printAlphas(cout, alpha, beta, lastWindowAlpha, lastWindowBeta,
orgAlpha, orgBeta);
         }
         else
         {
            printAlphas(log, alpha, beta, lastWindowBeta, lastWindowAlpha,
orgAlpha, orgBeta);
         }
*/

         if (alpha > lastWindowAlpha && alpha < lastWindowBeta)
         {
            if (incdepth != depth)
            {
               //alpha is between orginal boundries, so we have an exact score
               beta = alpha + VAL_WINDOW;
               alpha = alpha - VAL_WINDOW;
            }
         }
         else
         {
            int decDepth = incdepth-1;
            if (alpha >= lastWindowBeta)
            {
               if (lastWindowBeta != orgBeta)
               {
                  //alpha = lastWindowBeta;
                  //beta = orgBeta;
                  alpha = -9999;
                  beta = 9999;
                  incdepth = decDepth;
                  strcpy(strScore, "++");
               }
               else
               {
                  strcpy(strScore, "WIN");
                  break;
               }
            }
            else if (alpha <= lastWindowAlpha)
            {
               if (lastWindowAlpha != orgAlpha)
               {
                  //beta  = lastWindowAlpha+30;
                  //alpha = orgAlpha;
                  alpha = -9999;
                  beta = 9999;
                  incdepth = decDepth;
                  strcpy(strScore, "--");
               }
               else
               {
                  strcpy(strScore, "LOSS");
                  break;
               }
            }
         }
#else
         if (incdepth != depth)
         {
            alpha = orgAlpha;
            beta = orgBeta;
         }
#endif

         //
         //print line to winboard
         //
         t.stopTimer();
         char strTime[100];
         sprintf(strTime, "%.0f", 100*t.diffTime()); //time is centiseconds

         char strNodeCounter[100];
         sprintf(strNodeCounter, "%I64u", nodeCounter);

         cout << incdepth << " ";                  //depth
         cout << strScore << " ";              //score
         cout << strTime << " ";
         cout << strNodeCounter << " ";        //nodecounter

         #ifdef LINE
            printPV(cout, (struct moveListType*)&bestline); //bestline
         #endif
         cout << endl << flush;

         //do we use the time to stop or the clock?
         if (depth <= 0)
         {
            if (chessclock.haveToPlay()) break;
         }
         else
         {
            if (incdepth == depth) break;
         }
      } //end inc depth
   }


   /////////////////////////////////////////////
   //Log a few counters
   /////////////////////////////////////////////
   t.stopTimer();
   double tijd = t.diffTime();

   int nodes = (int)nodeCounter;

   char strDepth[100];
   sprintf(strDepth, "%d", incdepth);
   if (!xboardflag)
   {
      cout << "    // Depth: ";
      cout << strDepth << endl;
   }
   log << strDepth << endl;

   char strNodes[100];
   sprintf(strNodes, "%I64u", nodeCounter);
   if (!xboardflag)
   {
      cout << "    // Nodes: ";
      cout << strNodes << endl;
   }
   log << strNodes << endl;

   char strTime[100];
   sprintf(strTime, "%.2fs", tijd );
   if (!xboardflag)
   {
      cout << "    // Time: ";
      cout << strTime << endl;
   }
   log << strTime << endl;

   char strNps[100];
   if (tijd == 0)
   {
      sprintf(strNps, "%s", "INFINITE");
   }
   else
   {
      sprintf(strNps, "%.2f", nodes / tijd );
   }

   if (!xboardflag)
   {
      cout << "    // Nps: ";
      cout << strNps << endl;
   }
   log << strNps << endl;

   if (!xboardflag)
   {
      cout << "    // Bestmove: ";
      printMove(cout, *bestMove, true);
      cout << endl;
   }

   if (xboardflag)
   {
       cout << "tellics whisper ";
       cout << "depth:" << strDepth << ' ';
       cout << "score:" << strScore << ' ';
       cout << "time:" << strTime << ' ';
       cout << "nps:" << strNps << ' ';
       cout << "pv:";
       printPV(cout, (struct moveListType*)&bestline); //bestline
       cout << endl << flush;
   }

   log << "    //bestmove : ";
   printMove(log, *bestMove, true);
   log << endl;

   return alpha;
}







This page took 0 seconds to execute

Last modified: Thu, 15 Apr 21 08:11:13 -0700

Current Computer Chess Club Forums at Talkchess. This site by Sean Mintz.