Example #1
0
int Board::qsearch(int ply, int alpha, int beta)
{
	// quiescence search 

	int i, j, val;

	if (timedout) return 0;
	triangularLength[ply] = ply;
	if (isOwnKingAttacked()) return alphabetapvs(ply, 1, alpha, beta);
	
	if(EVAL_FUNC == 0){
		val = board.eval();
	}else {
		val = board.evalJL(PARAM_EVAL_MATERIAL, PARAM_EVAL_ESPACIAL, PARAM_EVAL_DINAMICA, PARAM_EVAL_POS_TABLERO, ply);
	}

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

	// generate captures & promotions:
	// captgen returns a sorted move list
	moveBufLen[ply+1] = captgen(moveBufLen[ply]);
	for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++)
	{
		makeMove(moveBuffer[i]);
		{
			if (!isOtherKingAttacked()) 
			{
				inodes++;
				if (--countdown <=0) readClockAndInput();
				val = -qsearch(ply+1, -beta, -alpha);
				unmakeMove(moveBuffer[i]);
				if (val >= beta) return val;
				if (val > alpha)
				{
					alpha = val;
					triangularArray[ply][ply] = moveBuffer[i];
					for (j = ply + 1; j < triangularLength[ply+1]; j++) 
					{
						triangularArray[ply][j] = triangularArray[ply+1][j];
					}
					triangularLength[ply] = triangularLength[ply+1];
				}
			}
			else unmakeMove(moveBuffer[i]);
		}
	}
	return alpha;
}
Example #2
0
int Board::alphabeta(int ply, int depth, int alpha, int beta)
{
  // Negascout
  
  int i, j, val;
  
  triangularLength[ply] = ply;
  if (depth == 0) return board.eval();
  
  moveBufLen[ply+1] = movegen(moveBufLen[ply]);
  for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++)
  {
    makeMove(moveBuffer[i]);
    {
      if (!isOtherKingAttacked())
      {
        inodes++;
        if (!ply) displaySearchStats(3, ply, i);
        val = -alphabeta(ply+1, depth-1, -beta, -alpha);
        unmakeMove(moveBuffer[i]);
        if (val >= beta)
        {
          return beta;
        }
        if (val > alpha)
        {
          alpha = val;                                                                              // both sides want to maximize from *their* perspective
          triangularArray[ply][ply] = moveBuffer[i];                                  // save this move
          for (j = ply + 1; j < triangularLength[ply + 1]; j++)
          {
            triangularArray[ply][j] = triangularArray[ply+1][j];   // and append the latest best PV from deeper plies
          }
          triangularLength[ply] = triangularLength[ply + 1];
          if (!ply)
          {
            msStop = timer.getms();
            displaySearchStats(2, depth, val);
          }
        }
      }
      else unmakeMove(moveBuffer[i]);
    }
  }
  return alpha;
}
Example #3
0
int Board::minimax(int ply, int depth)
{
  // Negamax
  
  int i, j, val, best;
  best = -LARGE_NUMBER;
  triangularLength[ply] = ply;
  if (depth == 0) return board.eval();
  moveBufLen[ply+1] = movegen(moveBufLen[ply]);
  for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++)
  {
    makeMove(moveBuffer[i]);
    {
      if (!isOtherKingAttacked())
      {
        inodes++;
        if (!ply) displaySearchStats(3, ply, i);
        val = -minimax(ply+1, depth-1);                                 // note the minus sign
        unmakeMove(moveBuffer[i]);
        if (val > best)                                                 // both sides want to maximize from *their* perspective
        {
          best = val;
          triangularArray[ply][ply] = moveBuffer[i];                                  // save this move
          for (j = ply + 1; j < triangularLength[ply + 1]; j++)
          {
            triangularArray[ply][j] = triangularArray[ply+1][j];   // and append the latest best PV from deeper plies
          }
          triangularLength[ply] = triangularLength[ply + 1];
          if (!ply)
          {
            msStop = timer.getms();
            displaySearchStats(2, depth, val);
          }
        }
      }
      else unmakeMove(moveBuffer[i]);
    }
  }
  return best;
}
Example #4
0
// This function should not be called by engines, it would be greatly inefficient
// It exists simply to find the legal moves for a particular
// square, such as is needed when running in a GUI
uchar Board0x88::generateMoves(uchar row, uchar col, MoveList & moveList)
{
  MoveList allMoves;
  uchar totalMoves = generateMoves(allMoves);

  for (uchar i = 0; i < totalMoves; i++) {
    if (allMoves[i].sourceRow == row && allMoves[i].sourceCol == col) {
      makeMove(allMoves[i]);
      if (!isCellAttacked(mKingIndex[!mSideToMove], mSideToMove) )
        moveList.addMove(allMoves[i]);
      unmakeMove(allMoves[i]);
    }
  }
  return moveList.size();
}
int BOARD::getPvLine(int depth)
{
	int c=0;
	int move=getPvMove();
	
	while(move != NOMOVE && c<depth)
	{
		PVARRAY[c++]=move;
		makeMove(move);					//Not Checked if the move was legal
		move=getPvMove();	
	}
	
	while(ply>0)
	unmakeMove();
	
	return c;	
}
Example #6
0
void displayPV()
{
	int i;
	char sanMove[12];

	for (i = 0; i < board.triangularLength[0]; i++) 
	{
		toSan(board.triangularArray[0][i], sanMove);
		std::cout << sanMove << " ";
		makeMove(board.triangularArray[0][i]);
	}
	for (i = board.triangularLength[0]-1; i >= 0; i--) 
	{
		unmakeMove(board.triangularArray[0][i]);
	}
	if ((!XB_MODE) && (i < 3)) std::cout << "     ";   // make sure to overwrite any remaining output of mode 3
	std::cout << std::endl;
	std::cout.flush();
}
int BOARD::quiescence(int alpha,int beta)
{
	if((info.nodes & 2047) ==0)
	check();

	info.nodes++;
	
	if(repeat() || fifty >=100)
	return 0;

	if(ply >MAXDEPTH -1)
	return evaluate();

	int value=evaluate();
	if(value >= beta)
	return beta;
	
	if(value >alpha)
	alpha=value;
	
	int legal=0;
	int oldalpha=alpha;
	int bestmove=NOMOVE;
	int score=-INFINITE;

	MOVE_LIST moveList;
	moveList.count=0;
	generateAllCap(moveList);
	
	
	std::sort(moveList.l,moveList.l+moveList.count,so);

	for(int i=0;i<moveList.count;i++)
	{
		//pickBest(i,moveList);
		if(!makeMove(moveList.l[i].move))
		continue;
		score=-quiescence(-beta,-alpha);
		unmakeMove();

		if(info.stopped)
		return 0;

		legal++;
		if(score >alpha)
		{
			if(score >=beta)
			{
				if(legal==1)
				info.fhf++;
				info.fh++;
			return beta;	//beta - cutoff
			}
			alpha=score;
			bestmove=moveList.l[i].move;
		}		
	}
	if(alpha != oldalpha)
	storePv(bestmove);
	
	return alpha;	
}
int BOARD::alphabeta(int depth,int alpha,int beta,bool donull)
{
	if(depth==0)
	{ 
		//info.nodes++;
		return quiescence(alpha,beta);
		//return evaluate();
	}

	
	if((info.nodes & 2047) == 0)
	check();
	info.nodes++;
	

	
	if((repeat() || fifty >=100) & ply)
	return 0;

	if(ply >MAXDEPTH -1)
	return evaluate();
	
		bool kingsafe;
		int pos=(int)log2(board[side+4] & -board[side+4]);
		kingsafe=isSafe(pos,side);
		if(!kingsafe)
		depth++;
	
	int score = -INFINITE;
	
/*	if(donull && ply && kingsafe && depth >4 && bigPiece[side] >0)
	{
		makeNullMove();
		score=-alphabeta(depth-4,-beta,-beta+1,false);
		unmakeNullMove();
		if(info.stopped)
		return 0;
		
		if(score>=beta)
		return beta;
	}*/
	
	int legal=0;
	int oldalpha=alpha;
	int bestmove=NOMOVE;
	score=-INFINITE;
	MOVE_LIST moveList;
	moveList.count=0;
	generateAllMoves(moveList);
	

	int pvMove=getPvMove();
	if(pvMove != NOMOVE)
	{
		for(int i=0;i<moveList.count;i++)
		{
			if(moveList.l[i].move == pvMove)
			{
				moveList.l[i].score=2000000;
				break;			
			}
		}
	}

	std::sort(moveList.l,moveList.l+moveList.count,so);

	for(int i=0;i<moveList.count;i++)
	{
		//pickBest(i,moveList);
		if(!makeMove(moveList.l[i].move))
		continue;
		legal++;
		score=-alphabeta(depth-1,-beta,-alpha,donull);
		unmakeMove();

		if(info.stopped)
		return 0;
		
		if(score >alpha)
		{
			if(score >=beta)
			{
				if(legal==1)
				info.fhf++;
				info.fh++;
			if(CAP(moveList.l[i].move) == EMPTY)
			{
				searchKillers[1][depth]=searchKillers[0][depth];
				searchKillers[0][depth]=moveList.l[i].move;				
			}
			return beta;	//beta - cutoff
			}
			alpha=score;
			bestmove=moveList.l[i].move;
			if(CAP(moveList.l[i].move) == EMPTY)
			searchHistory[cboard[FROM(bestmove)]][TO(bestmove)]+=depth;
		}		
	}

	if(legal == 0)
	{
		if(!kingsafe)
		return -MATE + ply;
		else
		return 0;
	}

	if(alpha != oldalpha)
	storePv(bestmove);
	
	return alpha;
}
Example #9
0
BOOLTYPE toSan(Move &move, char *sanMove)
{

//	===========================================================================
//	toSan will convert a move into non-ambiguous SAN-notation, returned in char sanMove[].
//  "move" must belong to the current "board". Returns true if successful.
//  The move is compared with other moves from the current board position. 
//  Ambiguities can arise if two (or more) identical pieces can move to the same square.
//  In such cases, the piece's initial is followed by (in this priority):
//  - the from file, if it's unique, 
//  - or else the from rank, if it's unique
//  - or else the from file and rank (this can happen after pawn promotions, 
//	  e.g. with 4 rooks on c3, c7, a5 and e5; they all can move to c5, and then move notation would be: R3c5
//  'e.p.' is added for an en-passant capture 
//  '+'is added for check, '#' is added for mate.
//	===========================================================================

	int i, j, k, ibuf, from, to, piece, capt, prom, ambigfile, ambigrank;
	int asciiShift;
	BOOLTYPE legal, check, mate, ambig;

	asciiShift    = (int)'a';
	piece = move.getPiec();
	from = move.getFrom();
	to = move.getTosq();
	capt = move.getCapt();
	prom = move.getProm();
	ibuf = 0;
	ambig = false;
	ambigfile = 0;
	ambigrank = 0;
	legal = false;
	check = false;
	mate = false;
	sprintf(sanMove, "");

//	Generate all pseudo-legal moves to be able to remove any ambiguities 
//	and check legality. Take the next free location in moveBufLen:
	while (board.moveBufLen[ibuf+1]) ibuf++;
	board.moveBufLen[ibuf+1] = movegen(board.moveBufLen[ibuf]);

//	Loop over the moves to see what kind(s) of ambiguities exist, if any:
	for (i = board.moveBufLen[ibuf]; i < board.moveBufLen[ibuf+1]; i++)
	{
		makeMove(board.moveBuffer[i]);
		if (!isOtherKingAttacked())
        {
			if (board.moveBuffer[i].moveInt == move.moveInt) 
			{
				legal = true;
				// it is check:
				if (isOwnKingAttacked()) 
				{
					check = true;
					// is it checkmate?
					k = 0;
					board.moveBufLen[ibuf+2] = movegen(board.moveBufLen[ibuf+1]);
					for (j = board.moveBufLen[ibuf+1]; j < board.moveBufLen[ibuf+2]; j++)
					{
						makeMove(board.moveBuffer[j]);
						if (!isOtherKingAttacked()) k++;
						unmakeMove(board.moveBuffer[j]);
					}
					if (!k) mate = true;
				}
			}
			// two same pieces can move to the same square:
			if ((board.moveBuffer[i].moveInt != move.moveInt) && (board.moveBuffer[i].getPiec() == piece) && (board.moveBuffer[i].getTosq() == to)) 
			{
				ambig = true;
				if (FILES[from] == FILES[board.moveBuffer[i].getFrom()]) ambigfile++; 
				if (RANKS[from] == RANKS[board.moveBuffer[i].getFrom()]) ambigrank++; 
			}
		}
		unmakeMove(board.moveBuffer[i]);
	}
//  cleanup:
	board.moveBufLen[ibuf+1] = 0;
	board.moveBufLen[ibuf+2] = 0;

//	construct the SAN string:
	if (!legal) 
	{
		strcpy(sanMove, "unknown");
		return false;
	}
	else
	{
		if (move.isCastleOO())
		{
			strcpy(sanMove, "O-O");
			return true;
		}	
		if (move.isCastleOOO())
		{
			strcpy(sanMove, "O-O-O");
			return true;
		}	
		// start building the string
		if (!move.isPawnmove()) 
		{
			sprintf(sanMove, "%s", PIECECHARS[piece]);
			if (ambig) 
			{
				if (ambigfile)
				{
					if (ambigrank) sprintf(sanMove, "%s%c%d", sanMove, FILES[from] + asciiShift - 1,RANKS[from]);
					else sprintf(sanMove, "%s%d", sanMove, RANKS[from]);
				}
				else
				{
					sprintf(sanMove, "%s%c", sanMove, FILES[from] + asciiShift - 1);
				}
			}
		}
		else
		{
			if (move.isCapture()) 
			{
				sprintf(sanMove, "%s%c", sanMove, FILES[from] + asciiShift - 1);
			}
		}
		if (move.isCapture()) sprintf(sanMove, "%sx", sanMove);
		sprintf(sanMove, "%s%c%d", sanMove, FILES[to] + asciiShift - 1, RANKS[to]);
		if (move.isEnpassant()) sprintf(sanMove, "%s e.p.", sanMove);
		if (move.isPromotion()) sprintf(sanMove, "%s=%s", sanMove, PIECECHARS[prom]);
		if (check)
		{
			if (mate) sprintf(sanMove, "%s#", sanMove); 
			else sprintf(sanMove, "%s+", sanMove);
		}
		return true;
	}
}
Example #10
0
int Board::alphabetapvs(int ply, int depth, int alpha, int beta)
{
  // PV search
  
  int i, j, movesfound, pvmovesfound, val;
  triangularLength[ply] = ply;
  if (depth == 0)
  {
    followpv = false;
    return qsearch(ply, alpha, beta);
  }
  
  // repetition check:
  if (repetitionCount() >= 3) return DRAWSCORE;
  movesfound = 0;
  pvmovesfound = 0;
  moveBufLen[ply+1] = movegen(moveBufLen[ply]);
  for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++)
  {
    selectmove(ply, i, depth, followpv);
    makeMove(moveBuffer[i]);
    {
      if (!isOtherKingAttacked())
      {
        inodes++;
        movesfound++;
        if (!ply) displaySearchStats(3, ply, i);
        if (pvmovesfound)
        {
          val = -alphabetapvs(ply+1, depth-1, -alpha-1, -alpha);
          if ((val > alpha) && (val < beta))
          {
            // in case of failure, proceed with normal alphabeta
            val = -alphabetapvs(ply+1, depth - 1, -beta, -alpha);                        
          }
        }
        // normal alphabeta
        else val = -alphabetapvs(ply+1, depth-1, -beta, -alpha);         
        unmakeMove(moveBuffer[i]);
        if (val >= beta)
        {
          // update the history heuristic
          if (nextMove)
            blackHeuristics[moveBuffer[i].getFrom()][moveBuffer[i].getTosq()] += depth*depth;
          else
            whiteHeuristics[moveBuffer[i].getFrom()][moveBuffer[i].getTosq()] += depth*depth;
          return beta;
        }
        if (val > alpha)
        {
          alpha = val;                                                      // both sides want to maximize from *their* perspective
          pvmovesfound++;
          triangularArray[ply][ply] = moveBuffer[i];                                  // save this move
          for (j = ply + 1; j < triangularLength[ply+1]; j++)
          {
            triangularArray[ply][j] = triangularArray[ply+1][j];   // and append the latest best PV from deeper plies
          }
          triangularLength[ply] = triangularLength[ply+1];
          if (!ply)
          {
            msStop = timer.getms();
            displaySearchStats(2, depth, val);
          }
        }
      }
      else unmakeMove(moveBuffer[i]);
    }
  }
  
  // update the history heuristic
  if (pvmovesfound)
  {
    if (nextMove)
      blackHeuristics[triangularArray[ply][ply].getFrom()][triangularArray[ply][ply].getTosq()] += depth*depth;
    else
      whiteHeuristics[triangularArray[ply][ply].getFrom()][triangularArray[ply][ply].getTosq()] += depth*depth;
  }
  
  //     50-move rule:
  if (fiftyMove >= 100) return DRAWSCORE;
  
  //     Checkmate/stalemate detection:
  if (!movesfound)
  {
    if (isOwnKingAttacked())  return (-CHECKMATESCORE+ply-1);
    else  return (STALEMATESCORE);
  }
  
  return alpha;
}
Example #11
0
BOOLTYPE Board::isEndOfgame(int &legalmoves, Move &singlemove)
{
  // Checks if the current position is end-of-game due to:
  // checkmate, stalemate, 50-move rule, or insufficient material
  
  int whiteknights, whitebishops, whiterooks, whitequeens, whitetotalmat;
  int blackknights, blackbishops, blackrooks, blackqueens, blacktotalmat;
  
  // are we checkmating the other side?
  int i;
  if (isOtherKingAttacked())
  {
    if (nextMove) std::cout << "1-0 {Black mates}" << std::endl;
    else std::cout << "1-0 {White mates}" << std::endl;
    return true;
  }
  
  // how many legal moves do we have?
  legalmoves = 0;
  moveBufLen[0] = 0;
  moveBufLen[1] = movegen(moveBufLen[0]);
  for (i = moveBufLen[0]; i < moveBufLen[1]; i++)
  {
    makeMove(moveBuffer[i]);
    if (!isOtherKingAttacked())
    {
      legalmoves++;
      singlemove = moveBuffer[i];
    }
    unmakeMove(moveBuffer[i]);
  }
  
  // checkmate or stalemate?
  if (!legalmoves)
  {
    if (isOwnKingAttacked())
    {
      if (nextMove) std::cout << "1-0 {White mates}" << std::endl;
      else std::cout << "1-0 {Black mates}" << std::endl;
    }
    else std::cout << "1/2-1/2 {stalemate}" << std::endl;
    return true;
  }
  
  // draw due to insufficient material:
  if (!whitePawns && !blackPawns)
  {
    whiteknights = bitCnt(whiteKnights);
    whitebishops = bitCnt(whiteBishops);
    whiterooks = bitCnt(whiteRooks);
    whitequeens = bitCnt(whiteQueens);
    whitetotalmat = 3 * whiteknights + 3 * whitebishops + 5 * whiterooks + 10 * whitequeens;
    blackknights = bitCnt(blackKnights);
    blackbishops = bitCnt(blackBishops);
    blackrooks = bitCnt(blackRooks);
    blackqueens = bitCnt(blackQueens);
    blacktotalmat = 3 * blackknights + 3 * blackbishops + 5 * blackrooks + 10 * blackqueens;
    
    // king versus king:
    if ((whitetotalmat == 0) && (blacktotalmat == 0))
    {
      std::cout << "1/2-1/2 {material}" << std::endl;
      return true;
    }
    
    // king and knight versus king:
    if (((whitetotalmat == 3) && (whiteknights == 1) && (blacktotalmat == 0)) ||
        ((blacktotalmat == 3) && (blackknights == 1) && (whitetotalmat == 0)))
    {
      std::cout << "1/2-1/2 {material}" << std::endl;
      return true;
    }
    
    // 2 kings with one or more bishops, all bishops on the same colour:
    if ((whitebishops + blackbishops) > 0)
    {
      if ((whiteknights == 0) && (whiterooks == 0) && (whitequeens == 0) &&
          (blackknights == 0) && (blackrooks == 0) && (blackqueens == 0))
      {
        if (!((whiteBishops | blackBishops) & WHITE_SQUARES) ||
            !((whiteBishops | blackBishops) & BLACK_SQUARES))
        {
          std::cout << "1/2-1/2 {material}" << std::endl;
          return true;
        }
      }
    }
  }
  
  // draw due to repetition:
  if (repetitionCount() >= 3)
  {
    std::cout << "1/2-1/2 {repetition}" << std::endl;
    return true;
    
  }
  
  // draw due to 50 move rule:
  if (fiftyMove >= 100)
  {
    std::cout << "1/2-1/2 {50-move rule}" << std::endl;
    return true;
  }
  
  return false;
}
Example #12
0
void Board::displaySearchStats(int mode, int depth, int score)
{
  // displays various search statistics
  // only to be used when ply = 0
  // mode = 1 : display header
  // mode = 2 : display full stats, including score and latest PV
  // mode = 3 : display current root move that is being searched
  //                     depth = ply, score = loop counter in the search move list
  
  char sanMove[12];
  U64 dt, hh, mm, ss;
  
  switch (mode)
  {
    case 1:
      std::cout << "depth  score   nodes     time  knps PV" << std::endl;
      break;
      
    case 2:
      dt = msStop - msStart;
      
      // depth
      printf("%5d ", depth);
      
      // score
      printf("%+6.2f ", float(score/100.0));
      
      // nodes searched
      if      (inodes > 100000000) printf("%6.0f%c ", float(inodes/1000000.0), 'M');
      else if (inodes > 10000000) printf("%6.2f%c ", float(inodes/1000000.0), 'M');
      else if (inodes > 1000000) printf("%6.0f%c ", float(inodes/1000.0),    'K');
      else if (inodes > 100000)  printf("%6.1f%c ", float(inodes/1000.0),    'K');
      else if (inodes > 10000)   printf("%6.2f%c ", float(inodes/1000.0),    'K');
      else                                     printf("%7d ", inodes);
      
      // search time
      if (dt > 3600000)
      {     
        hh = dt/3600000;
        mm = (dt - 3600000*hh)/60000;
        ss = (dt - 3600000*hh - 60000*mm)/1000;
        printf("%02d%c", hh, ':');
        printf("%02d%c", mm, ':');
        printf("%02d ", ss);
      }
      else if (dt > 60000)
      {     
        mm = dt/60000;
        ss = (dt - 60000*mm)/1000;
        printf("   %02d%c", mm, ':');
        printf("%02d ", ss);
      }
      else if (dt > 10000)       printf(" %6.1f%c ", float(dt/1000.0), 's');
      else if (dt > 1000)               printf(" %6.2f%c ", float(dt/1000.0), 's');
      else if (dt > 0)                  printf(" %5dms ", dt);
      else                                     printf("     0ms ");
      
      // search speed
      if (dt > 0) std::cout << std::setw(5) << (inodes/dt) << " ";
      else          std::cout << "    - ";
      
      // store this PV:
      rememberPV();
      
      // display the PV
      displayPV();
      break;
      
    case 3: // Note that the numbers refer to pseudo-legal moves:
      printf("%12s (%2d/%2d)%16s", " ", score+1, moveBufLen[depth+1]-moveBufLen[depth], " ");
      unmakeMove(moveBuffer[score]);
      toSan(moveBuffer[score], sanMove);
      std::cout << sanMove;
      makeMove(moveBuffer[score]);
      //                         printf("...    \n");
      printf("...    \r");
      std::cout.flush();
      break;
      
    default: break;
  }
  
  return;
}
Example #13
0
int Board::alphabetapvs(int ply, int depth, int alpha, int beta)
{
	// PV search

	int i, j, movesfound, pvmovesfound, val;

	triangularLength[ply] = ply;
	if (depth <= 0) 
	{
		followpv = false;
		return qsearch(ply, alpha, beta);
	}

	// repetition check:
	if (repetitionCount() >= 3) return DRAWSCORE;
	
	// now try a null move to get an early beta cut-off:
	if (!followpv && allownull)
	{
		if ((nextMove && (board.totalBlackPieces > NULLMOVE_LIMIT)) || (!nextMove && (board.totalWhitePieces > NULLMOVE_LIMIT)))
		{
			if (!isOwnKingAttacked())
			{
				allownull = false;
				inodes++;
				if (--countdown <=0) readClockAndInput();
				nextMove = !nextMove;
				hashkey ^= KEY.side; 
				val = -alphabetapvs(ply, depth - NULLMOVE_REDUCTION, -beta, -beta+1);
				nextMove = !nextMove;
				hashkey ^= KEY.side;
				if (timedout) return 0;
				allownull = true;
				if (val >= beta) return val;
			}
		}
	}
	allownull = true;

	movesfound = 0;
	pvmovesfound = 0;
	moveBufLen[ply+1] = movegen(moveBufLen[ply]);
	for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++)
	{
		selectmove(ply, i, depth, followpv); 
		makeMove(moveBuffer[i]);
		{
			if (!isOtherKingAttacked()) 
			{
				inodes++;
				if (--countdown <=0) readClockAndInput();
				movesfound++;
				if (!ply) displaySearchStats(3, ply, i); 
				if (pvmovesfound)
				{
					val = -alphabetapvs(ply+1, depth-1, -alpha-1, -alpha); 
		            if ((val > alpha) && (val < beta))
					{
						 // in case of failure, proceed with normal alphabeta
						val = -alphabetapvs(ply+1, depth - 1, -beta, -alpha);  		        
					}
				} 
				// normal alphabeta
	 			else val = -alphabetapvs(ply+1, depth-1, -beta, -alpha);	    
				unmakeMove(moveBuffer[i]);
				if (timedout) return 0;
				if (val >= beta)
				{
					// update the history heuristic
					if (nextMove) 
						blackHeuristics[moveBuffer[i].getFrom()][moveBuffer[i].getTosq()] += depth*depth;
					else 
						whiteHeuristics[moveBuffer[i].getFrom()][moveBuffer[i].getTosq()] += depth*depth;
					return beta;
				}
				if (val > alpha)
				{
					alpha = val;								    // both sides want to maximize from *their* perspective
					pvmovesfound++;
					triangularArray[ply][ply] = moveBuffer[i];					// save this move
					for (j = ply + 1; j < triangularLength[ply+1]; j++) 
					{
						triangularArray[ply][j] = triangularArray[ply+1][j];	// and append the latest best PV from deeper plies
					}
					triangularLength[ply] = triangularLength[ply+1];
					if (!ply) displaySearchStats(2, depth, val);
				}
			}
			else unmakeMove(moveBuffer[i]);
		}
	}

	// update the history heuristic
	if (pvmovesfound)
	{
		if (nextMove) 
			blackHeuristics[triangularArray[ply][ply].getFrom()][triangularArray[ply][ply].getTosq()] += depth*depth;
		else
			whiteHeuristics[triangularArray[ply][ply].getFrom()][triangularArray[ply][ply].getTosq()] += depth*depth;
	}

	//	50-move rule:
	if (fiftyMove >= 100) return DRAWSCORE;

	//	Checkmate/stalemate detection:
	if (!movesfound)
	{
		if (isOwnKingAttacked())  return (-CHECKMATESCORE+ply-1);
		else  return (STALEMATESCORE);
	}

	return alpha;
}
Example #14
0
void commands()
{

// ================================================================
// commands is used to read console input and execute the commands
// It also serves as winboard driver. 
// The code is based on H.G. Muller's model WinBoard protocol driver:
// http://www.open-aurec.com/wbforum/viewtopic.php?f=24&t=51739
// =================================================================

	int i, j, number;
	int fenhalfmoveclock;
	int fenfullmovenumber;
	char fen[100];
	char fencolor[1];      
	char fencastling[4];
	char fenenpassant[2];
	char sanMove[12];
	char command[80];
	char userinput[80];
	U64 msStart,msStop, perftcount;
	Timer timer;
	Move move, dummy;

	// =================================================================
	// infinite loop:
	// =================================================================

	while (1) 
	{ 

		fflush(stdout);                 

		// =================================================================
		// think & move
		// =================================================================

		if (XB_MODE)
		{
			if (XB_COMPUTER_SIDE == board.nextMove) 
			{
				#ifdef KENNY_DEBUG_WINBOARD
					std::cout << "#-KENNY : start think" << std::endl;
				#endif
				move = board.think();
				#ifdef KENNY_DEBUG_WINBOARD
					std::cout << "#-KENNY : exit think" << std::endl;
					std::cout << "#<KENNY : move " << SQUARENAME[move.getFrom()] << SQUARENAME[move.getTosq()]  << std::endl;
				#endif
				if (move.moveInt) 
				{
					printf("move "); printf("%s",SQUARENAME[move.getFrom()]); printf("%s",SQUARENAME[move.getTosq()]); 
					if (move.isPromotion()) printf("%s",PIECECHARS[move.getProm()]);
					printf("\n");
					makeMove(move);
					board.endOfGame++;
					board.endOfSearch = board.endOfGame;
				}
			}
			fflush(stdout); 

			// =================================================================
			// ponder
			// =================================================================

			if (XB_COMPUTER_SIDE != XB_NONE && XB_COMPUTER_SIDE != XB_ANALYZE && XB_PONDER && board.endOfGame)
			{
				XB_NO_TIME_LIMIT = true;
				#ifdef KENNY_DEBUG_WINBOARD
					std::cout << "#-KENNY : start ponder" << std::endl;
				#endif
				move = board.think();
				#ifdef KENNY_DEBUG_WINBOARD
					std::cout << "#-KENNY : exit ponder" << std::endl;
				#endif
				XB_NO_TIME_LIMIT = false;
			} 

			// =================================================================
			// analyze
			// =================================================================

			if (XB_COMPUTER_SIDE == XB_ANALYZE)
			{
				XB_NO_TIME_LIMIT = true;
				#ifdef KENNY_DEBUG_WINBOARD
					std::cout << "#-KENNY : start analyze" << std::endl;
				#endif
				move = board.think();
				#ifdef KENNY_DEBUG_WINBOARD
					std::cout << "#-KENNY : exit analyze" << std::endl;
				#endif
				XB_NO_TIME_LIMIT = false;
			} 
		}

noPonder:

		// =================================================================
		// display the command prompt
		// =================================================================

		if (!XB_MODE)
		{
			if (board.nextMove == WHITE_MOVE) std::cout << "wt> ";
			else std::cout << "bl> ";
			fflush(stdout);
		}

		// =================================================================
		// read input, but only after attending a pending command received during 
		// search/ponder/analyze:
		// =================================================================

		if (!XB_DO_PENDING)
		{
			#ifdef KENNY_DEBUG_WINBOARD
				if (XB_MODE)
					std::cout << "#-KENNY : COMPUTER_SIDE=" << (int)XB_COMPUTER_SIDE << " PONDER=" << XB_PONDER << " nextMove=" << (int)board.nextMove << std::endl;
			#endif

			for (CMD_BUFF_COUNT = 0; (CMD_BUFF[CMD_BUFF_COUNT] = getchar()) != '\n'; CMD_BUFF_COUNT++);
			CMD_BUFF[CMD_BUFF_COUNT+1] = '\0';

			#ifdef KENNY_DEBUG_WINBOARD
				if (XB_MODE) std::cout << "#>KENNY : " << CMD_BUFF << std::endl;
			#endif
		}
		#ifdef KENNY_DEBUG_WINBOARD
			else
			{
				if (XB_MODE) std::cout << "#>KENNY : " << CMD_BUFF << " (from peek)" << std::endl;
			}
		#endif
		XB_DO_PENDING = false;
	
		// =================================================================
		// ignore empty lines
		// =================================================================

		if (!CMD_BUFF_COUNT) continue; 

		// =================================================================
		// extract the first word
		// =================================================================

		sscanf(CMD_BUFF, "%s", command);

		// =================================================================
		// help, h or ?: show this help - list of CONSOLE-ONLY COMMANDS
		// =================================================================
		if ((!XB_MODE) && ((!strcmp(command, "help")) || (!strcmp(command, "h")) || (!strcmp(command, "?"))))
		{ 
			std::cout << std::endl << "help:" << std::endl;
			std::cout << "black               : BLACK to move" << std::endl;
			std::cout << "cc                  : play computer-to-computer " << std::endl;
			std::cout << "d                   : display board " << std::endl;
			std::cout << "eval                : show static evaluation of this position" << std::endl;
			std::cout << "exit                : exit program " << std::endl;
			std::cout << "game                : show game moves " << std::endl;
			std::cout << "go                  : computer next move " << std::endl;
			std::cout << "help, h, or ?       : show this help " << std::endl;
			std::cout << "info                : display variables (for testing purposes)" << std::endl;
			std::cout << "ini                 : read the initialization file" << std::endl;
			std::cout << "memory n            : max memory to use (in MB)" << std::endl;
			std::cout << "move e2e4, or h7h8q : enter a move (use this format)" << std::endl;
			std::cout << "moves               : show all legal moves" << std::endl;
			std::cout << "new                 : start new game" << std::endl;
			std::cout << "perft n             : calculate raw number of nodes from here, depth n " << std::endl;
			#ifdef KENNY_VERBOSE_SEE
				std::cout << "qsearch             : shows sorted capture movelist" << std::endl;
			#endif
			std::cout << "quit                : exit program " << std::endl;
			std::cout << "r                   : rotate board " << std::endl;
			std::cout << "readfen filename n  : reads #-th FEN position from filename" << std::endl;
			std::cout << "sd n                : set the search depth to n" << std::endl;
			std::cout << "setup               : setup board... " << std::endl;
			std::cout << "test filename       : starts search on all FEN position in 'filename'" << std::endl;
			std::cout << "                      using current time & search depth parameters" << std::endl;
			std::cout << "                      output is written in test.log" << std::endl;
			std::cout << "time s              : time per move in seconds" << std::endl;
			std::cout << "undo                : take back last move" << std::endl;
			std::cout << "white               : WHITE to move" << std::endl;
			std::cout << std::endl;
			continue; 
		}

		// =================================================================
		// accepted: in reply to the "feature" command
		// =================================================================

		if (XB_MODE && !strcmp(command, "accepted")) continue; 

		// =================================================================
		// analyze: enter analyze mode
		// =================================================================

		if (XB_MODE && !strcmp(command, "analyze")) 
		{ 
			XB_COMPUTER_SIDE = XB_ANALYZE;			
			continue; 
		}

		// =================================================================
		// black: BLACK to move
		// =================================================================

		if (!XB_MODE && !strcmp(command, "black") && board.nextMove == WHITE_MOVE)
		{ 
			board.hashkey ^= KEY.side;
			board.endOfSearch = 0; 
			board.endOfGame = 0;
			board.nextMove = BLACK_MOVE;
			continue; 
		}

		// =================================================================
		// bk: show book moves from this position, if any
		// =================================================================

		if (XB_MODE && !strcmp(command, "bk")) continue; 

		// =================================================================
		// cc: play computer-to-computer
		// =================================================================

		if (!XB_MODE && !strcmp(command, "cc"))    
		{ 
			while (!_kbhit() && !board.isEndOfgame(i, dummy))
			{
				move = board.think();
				if (move.moveInt) 
				{
					makeMove(move);
					board.endOfGame++;
					board.endOfSearch = board.endOfGame;
					board.display();
				}
			}
			continue; 
		}

		// =================================================================
		// computer: the opponent is also a computer chess engine
		// =================================================================

		if (XB_MODE && !strcmp(command, "computer")) continue; 

		// =================================================================
		// cores n: informs the engine on how many CPU cores it is allowed to use maximally
		// =================================================================

		if (XB_MODE && !strcmp(command, "cores")) continue; 

		// =================================================================
		// d: display board
		// =================================================================

		if (!XB_MODE && !strcmp(command, "d"))
		{
			board.display();
			continue; 
		}

		// =================================================================
		// easy: turn off pondering
		// =================================================================

		if (XB_MODE && !strcmp(command, "easy"))    
		{ 
			XB_PONDER = false;
			continue; 
		}

		// =================================================================
		// egtpath type path: informs the engine in which directory it can find end-game tables
		// =================================================================

		if (XB_MODE && !strcmp(command, "egtpath")) continue; 

		// =================================================================
		// eval: show static evaluation of this position
		// =================================================================

		if (!XB_MODE && !strcmp(command, "eval"))    
		{ 
			number = board.eval();
			std::cout << "eval score = " << number << std::endl;
			#ifdef KENNY_DEBUG_EVAL
				board.mirror();
				board.display();
				i = board.eval();
				std::cout << "eval score = " << i << std::endl;
				board.mirror();
				if (number != i) std::cout << "evaluation is not symmetrical! " << number << std::endl;
				else std::cout << "evaluation is symmetrical" << std::endl;
			#endif
			continue; 
		}

		// =================================================================
		// exit: leave analyze mode / exit program (if not in WB)
		// =================================================================

		if (!strcmp(command, "exit"))    
		{ 
			if (XB_MODE)
			{
				XB_COMPUTER_SIDE = XB_NONE;			
				continue; 
			}
			else break;
		}

		// =================================================================
		// force: Set the engine to play neither color
		// =================================================================

		if (XB_MODE && !strcmp(command, "force"))   
		{ 
			XB_COMPUTER_SIDE = XB_NONE;
			continue; 
		}

		// =================================================================
		// game: show game moves
		// =================================================================

		if (!XB_MODE && !strcmp(command, "game"))   
		{ 
			if (board.endOfGame)
			{
				// make a temporary copy of board.gameLine[];
				number = board.endOfGame;
				GameLineRecord *tmp = new GameLineRecord[number];
				memcpy(tmp, board.gameLine, number * sizeof(GameLineRecord));

				// unmake all moves:
				for (i = number-1 ; i >= 0 ; i--) 
				{ 
					unmakeMove(tmp[i].move);
					board.endOfSearch = --board.endOfGame;
				}

				// redo all moves:
				j = board.nextMove;
				for (i = 0 ; i < number; i++)
				{
					// move numbering:
					if (!((i+j+2)%2)) std::cout << (i+2*j+2)/2 << ". ";
					else if (!i) std::cout << "1. ... ";

					// construct the move string
					toSan(tmp[i].move, sanMove);
					std::cout << sanMove; 

					// output CRLF, or space:
					if (!((i+j+1)%2)) std::cout << std::endl;
					else std::cout << " ";

					// make the move:
					makeMove(tmp[i].move);
					board.endOfSearch = ++board.endOfGame;
				}
				std::cout << std::endl;

				// delete the temporary copy:
				delete[] tmp;
			} 
			else
			{
				std::cout << "there are no game moves" << std::endl;        
			}
			continue; 
		}

		// =================================================================
		// go: leave force mode and set the engine to play the color that is on move
		// =================================================================

		if (!strcmp(command, "go"))      
		{
			if (XB_MODE)
			{
				XB_COMPUTER_SIDE = board.nextMove;  
				continue; 
			}
			else
			{
				if (!board.isEndOfgame(i, dummy))
				{
					move = board.think();
					if (move.moveInt) 
					{
						makeMove(move);
						board.endOfGame++;
						board.endOfSearch = board.endOfGame;
					}
					board.display();
					board.isEndOfgame(i, dummy);
					CMD_BUFF_COUNT = '\0';
				}
				else
				{
					board.display();
					CMD_BUFF_COUNT = '\0';
				}
			}
			continue;
		}

		// =================================================================
		// hard: turn on pondering
		// =================================================================

		if (XB_MODE && !strcmp(command, "hard"))    
		{ 
			XB_PONDER = true;  
			continue; 
		}

		// =================================================================
		// hint: respond with "Hint: xxx", where xxx is a suggested move
		// =================================================================

		if (XB_MODE && !strcmp(command, "hint")) 
		{
				continue; 
		}

		// =================================================================
		// ics hostname: the engine is playing on an Internet Chess Server (ICS) with the given hostname
		// =================================================================

		if (XB_MODE && !strcmp(command, "ics"))     { continue; }

		// =================================================================
		// info: display variables (for testing purposes)
		// =================================================================

		if (!XB_MODE && !strcmp(command, "info"))    
		{ 
			info();
			continue; 
		}

		// =================================================================
		// ini: read the initialization file
		// =================================================================

		if (!XB_MODE && !strcmp(command, "ini"))    
		{ 
			readIniFile();
			continue; 
		}

		// =================================================================
		// level mps base inc: set time controls
		// =================================================================

		if (XB_MODE && !strcmp(command, "level"))   
		{
			sscanf(CMD_BUFF, "level %d %d %d", &XB_MPS, &XB_MIN, &XB_INC) == 3 ||  
			sscanf(CMD_BUFF, "level %d %d:%d %d", &XB_MPS, &XB_MIN, &XB_SEC, &XB_INC);
			XB_INC *= 1000;
			continue;
		}

		// =================================================================
		// memory n: informs the engine on how much memory it is allowed to use maximally, in MB
		// =================================================================

		if (XB_MODE && !strcmp(command, "memory")) continue; 

		// =================================================================
		// moves: show all legal moves
		// =================================================================

		if (!XB_MODE && !strcmp(command, "moves"))    
		{ 
			board.moveBufLen[0] = 0;
			board.moveBufLen[1] = movegen(board.moveBufLen[0]);
			std::cout << std::endl << "moves from this position:" << std::endl;
			number = 0;
			for (i = board.moveBufLen[0]; i < board.moveBufLen[1]; i++)
			{
				makeMove(board.moveBuffer[i]);
				if (isOtherKingAttacked())
				{
					unmakeMove(board.moveBuffer[i]);
				}
				else
				{
					unmakeMove(board.moveBuffer[i]);
					toSan(board.moveBuffer[i], sanMove);
					std::cout << ++number << ". " << sanMove << std::endl;
				}
			}
			continue; 
		}

		// =================================================================
		// move: enter a move (use this format: move e2e4, or h7h8q)
		// =================================================================

		if (!XB_MODE && !strcmp(command, ""))    
		{
			sscanf(CMD_BUFF,"%s",userinput);
			// generate the pseudo-legal move list
			board.moveBufLen[0] = 0;
			board.moveBufLen[1] = movegen(board.moveBufLen[0]);
 
			if (isValidTextMove(userinput, move))        // check to see if the user move is also found in the pseudo-legal move list
			{
				makeMove(move);
 
				if (isOtherKingAttacked())              // post-move check to see if we are leaving our king in check
				{
					unmakeMove(move);
					std::cout << "    invalid move, leaving king in check: " << userinput << std::endl;
				}
				else
				{
					board.endOfGame++;
					board.endOfSearch = board.endOfGame;
					board.display();
				}
			}
			else
			{
				std::cout << "    move is invalid or not recognized: " << userinput << std::endl;
			}
			continue; 
		}

		// =================================================================
		// name <something>: informs the engine of its opponent's name
		// =================================================================

		if (XB_MODE && !strcmp(command, "name")) continue; 

		// =================================================================
		// new: reset the board to the standard chess starting position
		// =================================================================

		if (!strcmp(command, "new"))     
		{
			board.init(); 
			if (XB_MODE) 
			{
				XB_COMPUTER_SIDE = BLACK_MOVE;
				board.searchDepth = MAX_PLY;
			}
			continue; 
		}

		// =================================================================
		// nopost: turn off thinking/pondering output
		// =================================================================

		if (XB_MODE && !strcmp(command, "nopost"))  
		{ 
			XB_POST = false;
			continue; 
		}

		// =================================================================
		// otim n: set a clock that belongs to the opponent, in centiseconds
		// =================================================================
		if (XB_MODE && !strcmp(command, "otim"))    
		{ 
			// do not start pondering after receiving time commands, as a move will follow immediately
			sscanf(CMD_BUFF, "otim %d", &XB_OTIM);
			XB_OTIM *= 10;  // convert to miliseconds;
			goto noPonder; 
		} 

		// =================================================================
		// option name[=value]: setting of an engine-define option
		// =================================================================
		if (XB_MODE && !strcmp(command, "option"))  continue;

		// =================================================================
		// perft: calculate raw number of nodes from here, depth n 
		// =================================================================
		if (!XB_MODE && !strcmp(command, "perft"))  
		{ 
			sscanf(CMD_BUFF,"perft %d", &number);
			std::cout << "    starting perft " << number << "..." << std::endl;
			timer.init();
			board.moveBufLen[0] = 0;
 
			#ifdef KENNY_DEBUG_PERFT
				ICAPT = 0;
				IEP = 0;
				IPROM = 0;
				ICASTLOO = 0;
				ICASTLOOO = 0;
				ICHECK = 0;
			#endif
 
			msStart = timer.getms();
			perftcount = perft(0, number);
			msStop = timer.getms();
 
			std::cout << "nodes        = " << perftcount << ", " << msStop - msStart << " ms, ";
			if ((msStop - msStart) > 0)
			std::cout << (perftcount/(msStop - msStart)) << " knods/s";
			std::cout << std::endl;
			CMD_BUFF_COUNT = '\0';
 
			#ifdef KENNY_DEBUG_PERFT
				std::cout << "captures     = " << ICAPT << std::endl;
				std::cout << "en-passant   = " << IEP << std::endl;
				std::cout << "castlings    = " << ICASTLOO + ICASTLOOO << std::endl;
				std::cout << "promotions   = " << IPROM << std::endl;
				std::cout << "checks       = " << ICHECK << std::endl;
			#endif
			continue; 
		}

		// =================================================================
		// ping n: reply by sending the string pong n
		// =================================================================

		if (XB_MODE && !strcmp(command, "ping"))    
		{ 
			sscanf(CMD_BUFF,"ping %d", &number);
			std::cout << "pong " << number << std::endl; 
			continue; 
		}

		// =================================================================
		// post: turn on thinking/pondering output
		// =================================================================

		if (XB_MODE && !strcmp(command, "post"))    
		{ 
			XB_POST = true; 
			continue; 
		}

		// =================================================================
		// protover n: protocol version
		// =================================================================

		if (XB_MODE && !strcmp(command, "protover")) 
		{
			std::cout << "feature ping=1" << std::endl;
			std::cout << "feature setboard=1" << std::endl;
			std::cout << "feature colors=0" << std::endl;
			std::cout << "feature usermove=1" << std::endl;
			std::cout << "feature memory=1" << std::endl;
			std::cout << "feature debug=1" << std::endl;
			std::cout << "feature done=1" << std::endl;

			continue;
		}

		#ifdef KENNY_VERBOSE_SEE
		// =================================================================
		// qsearch: shows sorted capture movelist
		// =================================================================
				if (!XB_MODE && !strcmp(command, "qsearch"))  
				{ 
					board.moveBufLen[0] = 0;
					board.moveBufLen[1] = captgen(board.moveBufLen[0]);
					std::cout << std::endl << "sorted capturing moves from this position:" << std::endl;
					std::cout << std::endl << "        score:" << std::endl;
					number = 0;
					for (i = board.moveBufLen[0]; i < board.moveBufLen[1]; i++)
					{
						makeMove(board.moveBuffer[i]);
						if (isOtherKingAttacked())
						{
							unmakeMove(board.moveBuffer[i]);
						}
						else
						{
							unmakeMove(board.moveBuffer[i]);
							std::cout << ++number << ". "; 
							displayMove(board.moveBuffer[i]);
							std::cout << "   " << board.moveBuffer[i + OFFSET].moveInt << std::endl;
						}
					}
					continue; 
				}
		#endif

		// =================================================================
		// quit: exit program
		// =================================================================

		if (!strcmp(command, "quit")) break; 

		// =================================================================
		// r: rotate board
		// =================================================================

		if (!XB_MODE && !strcmp(command, "r"))  
		{ 
			board.viewRotated = !board.viewRotated;
			continue; 
		}

		// =================================================================
		// random: ignored
		// =================================================================

		if (XB_MODE && !strcmp(command, "random")) continue;

		// =================================================================
		// rating: ICS opponent's rating
		// =================================================================

		if (XB_MODE && !strcmp(command, "rating")) continue;

		// =================================================================
		// readfen filename n: reads #-th FEN position from filename
		// =================================================================

		if (!XB_MODE && !strcmp(command, "readfen"))  
		{ 
			sscanf(CMD_BUFF,"readfen %s %d", userinput, &number);
			board.init();
			readFen(userinput, number);
			board.display();
			continue; 
		}

		// =================================================================
		// rejected: feature is rejected
		// =================================================================

		if (XB_MODE && !strcmp(command, "rejected")) continue;

		// =================================================================
		// remove: undo the last two moves (one for each player) and continue playing the same color.
		// =================================================================

		if (XB_MODE && !strcmp(command, "remove"))  
		{ 
			if (board.endOfGame)
			{
				unmakeMove(board.gameLine[--board.endOfGame].move);
				board.endOfSearch = board.endOfGame;
			}
			if (board.endOfGame)
			{
				unmakeMove(board.gameLine[--board.endOfGame].move);
				board.endOfSearch = board.endOfGame;
			}
			continue; 
		}

		// =================================================================
		// result string {comment}: end the each game, e.g.: result 1-0 {White mates}
		// =================================================================

		if (XB_MODE && !strcmp(command, "result"))  
		{ 
			XB_COMPUTER_SIDE = XB_NONE;
			continue; 
		}

		// =================================================================
		// sd n: set the search depth to n
		// =================================================================

		if (!strcmp(command, "sd"))      
		{ 
			sscanf(CMD_BUFF,"sd %d", &board.searchDepth);
			if (board.searchDepth < 1) board.searchDepth = 1;
			if (board.searchDepth > MAX_PLY) board.searchDepth = MAX_PLY;
			std::cout << "KENNY> search depth " << board.searchDepth << std::endl;
			continue; 
		}

		// =================================================================
		// setboard fen: set up the board/position 
		// =================================================================

		if (XB_MODE && !strcmp(command, "setboard"))
		{ 
			XB_COMPUTER_SIDE = XB_NONE;
			sscanf(CMD_BUFF, "setboard %s %s %s %s %d %d", fen, fencolor, fencastling, fenenpassant, &fenhalfmoveclock, &fenfullmovenumber);
			setupFen(fen, fencolor, fencastling, fenenpassant, fenhalfmoveclock, fenfullmovenumber);
			continue; 
		}

		// =================================================================
		// setup: setup board... 
		// =================================================================

		if (!XB_MODE && !strcmp(command, "setup"))
		{ 
			setup();
			continue; 
		}

		// =================================================================
		// stopfrac (0-100%): undocumented command to interactively change this 
		// parameter (e.g. for running testsuites), default value is 60
		// Don't start a new iteration if STOPFRAC fraction of the max search time 
		// has passed
		// =================================================================

		if (!XB_MODE && !strcmp(command, "stopfrac"))      
		{ 
			number = (int)(STOPFRAC * 100);
			sscanf(CMD_BUFF, "stopfrac %d", &number);
			if (number < 1) number = 1;
			if (number > 100) number = 100;
			STOPFRAC = (float)(number/100.0);
			std::cout << "KENNY> stopfrac " << 100*STOPFRAC << std::endl;
			continue; 
		}

		// =================================================================
		// st time: set time controls
		// =================================================================

		if (XB_MODE && !strcmp(command, "st"))      
		{ 
			sscanf(CMD_BUFF, "st %d", &board.maxTime);
			board.maxTime *= board.maxTime;  // convert to ms
			continue; 
		}

		// =================================================================
		// test filename: starts search on all FEN position in 'filename
		// =================================================================

		if (!XB_MODE && !strcmp(command, "test"))      
		{ 
			sscanf(CMD_BUFF,"test %s", userinput);
			board.init();
			test(userinput);
			continue; 
		}

		// =================================================================
		// time: set a clock that belongs to the engine
		// =================================================================

		if (!strcmp(command, "time"))    
		{ 
			number = (int)board.maxTime / 1000;
			sscanf(CMD_BUFF,"time %d", &number);
			if (number < 1) number = 1;
			if (!XB_MODE) std::cout << "KENNY> search time " << number << " seconds" << std::endl;
			if (XB_MODE)
			{
				XB_CTIM = number * 10;
				board.maxTime = number * 10; // conversion to ms
			}
			else
			{
				board.maxTime = number * 1000; // conversion to ms
			}
			goto noPonder; 
		}

		// =================================================================
		// undo: take back last move
		// =================================================================

		if (!strcmp(command, "undo"))    
		{ 
			if (board.endOfGame)
			{
				unmakeMove(board.gameLine[--board.endOfGame].move);
				board.endOfSearch = board.endOfGame;
				if (!XB_MODE) board.display();
			}
			else if (!XB_MODE) std::cout << "already at start of game" << std::endl;
			continue; 
		}

		// =================================================================
		// usermove move: do a move
		// =================================================================

		if (XB_MODE && !strcmp(command, "usermove"))
		{
			sscanf(CMD_BUFF,"usermove %s",userinput);

			// generate the pseudo-legal move list
			board.moveBufLen[0] = 0;
			board.moveBufLen[1] = movegen(board.moveBufLen[0]);
 
			if (isValidTextMove(userinput, move))        // check to see if the user move is also found in the pseudo-legal move list
			{
				makeMove(move);
				if (isOtherKingAttacked())              // post-move check to see if we are leaving our king in check
				{
					#ifdef KENNY_DEBUG_WINBOARD
						std::cout << "#-KENNY : usermove illegal" << std::endl;
					#endif
					unmakeMove(move);
				}
				else
				{
					#ifdef KENNY_DEBUG_WINBOARD
						std::cout << "#-KENNY : usermove " << userinput << " made" << std::endl;
					#endif
					board.endOfGame++;
					board.endOfSearch = board.endOfGame;
				}
			}
			else
			{
				#ifdef KENNY_DEBUG_WINBOARD
						std::cout << "#-KENNY : usermove illegal" << std::endl;
				#endif
			} 
			continue;
		}

		// =================================================================
		// variant: the game is not standard chess
		// =================================================================

		if(XB_MODE && !strcmp(command, "variant")) continue; 

		// =================================================================
		// white: WHITE to move
		// =================================================================

		if (!XB_MODE && !strcmp(command, "white") && board.nextMove == BLACK_MOVE)    
		{ 
			board.hashkey ^= KEY.side;
			board.endOfSearch = 0; 
			board.endOfGame = 0;
			board.nextMove = WHITE_MOVE;
			continue; 
		}

		// =================================================================
		// xboard: put the engine into "xboard mode", stop all unsolicited output
		// =================================================================

		if (!XB_MODE && !strcmp(command, "xboard"))  
		{ 
			#ifdef KENNY_DEBUG_WINBOARD
				if (XB_MODE) std::cout << "#>KENNY : xboard" << std::endl;
			#endif

			std::cout << std::endl;
			XB_COMPUTER_SIDE = XB_NONE;
			XB_MODE = true;
			XB_POST = false;
			board.init();
			continue; 
		}

		// =================================================================
		// unknown command: 
		// =================================================================

		printf("Error: unknown command: %s\n", command);
		#ifdef KENNY_DEBUG_WINBOARD
			if (XB_MODE) std::cout << "#<KENNY : Error: unknown command: " << command << std::endl;
		#endif
	}
}