// principal variation search
int SimplePVSearch::pvSearch(Board& board, SearchInfo& si) {
	if (stop(si)) {
		return 0;
	}
	if (si.ply>maxPlySearched) {
		maxPlySearched=si.ply;
	}
	if (si.depth<=0) {
		si.update(0,PV_NODE);
		return qSearch(board, si);
	}
	if	(board.isDraw() || si.ply >= maxSearchPly-1) {
		return drawScore;
	}
	const int oldAlpha = si.alpha;
	int score = -maxScore;
	int currentScore = -maxScore;
	bool okToPrune=false;
	si.alpha = std::max(-maxScore+si.ply, si.alpha);
	si.beta = std::min(maxScore-(si.ply+1), si.beta);
	if (si.alpha>=si.beta) {
		return si.alpha;
	}
	TranspositionTable::HashData hashData;
	MoveIterator::Move hashMove;
	const Key key = si.partialSearch?board.getPartialSearchKey():board.getKey();
	// tt retrieve & prunning
	bool hashOk = agent->hashGet(okToPrune, key, hashData, si.ply, si.depth);
	if (hashOk) {
		hashMove = hashData.move();
		if (okToPrune) {
			return hashData.value();
		}
	}
	const bool isKingAttacked = board.isInCheck();
	bool isLazyEval=false;
	if (!isKingAttacked) {
		if (hashOk && (hashData.flag() & TranspositionTable::NODE_EVAL)) {
			currentScore = hashData.evalValue();
		} else {
			currentScore = evaluator.evaluate(board,si.alpha,si.beta);
			isLazyEval = evaluator.isLazyEval();
		}
	}
	//iid
	if (si.depth > allowIIDAtPV &&	hashMove.none()) {
		SearchInfo newSi(false,emptyMove,si.alpha,si.beta,si.depth-2,si.ply,PV_NODE,si.splitPoint);
		score = pvSearch(board,newSi);
		hashOk = agent->hashGet(key, hashData, si.ply);
		if (hashOk) {
			hashMove = hashData.move();
		}
	}
	MoveIterator moves = MoveIterator();
	MoveIterator::Move bestMove=emptyMove;
	MoveIterator::Move move;
	int moveCounter=0;
	int bestScore=-maxScore;
	bool nmMateScore=false;
	while (true) {
		move = selectMove<false>(board, moves, hashMove, si.ply, si.depth);
		if (move.none()) {
			break;
		}
		if (si.partialSearch && move == si.move) {
			continue;
		}
		const bool isHashMove = move.type==MoveIterator::TT_MOVE;
		int extension=0;
		if (isKingAttacked) {
			extension++;
		} else if (isHashMove && si.depth > sePVDepth && hashOk && !hashMove.none()
				&& !si.partialSearch && hashData.depth() >= si.depth-3 &&
				(hashData.flag() & TranspositionTable::LOWER)) {
			if (abs(hashData.value()) < winningScore) {
				const int seValue = hashData.value() - seMargin;
				SearchInfo seSi(false, hashMove, true, seValue-1, seValue, si.depth/2,si.ply,
						si.nodeType, si.splitPoint);
				const int partialScore = zwSearch(board,seSi);
				if (partialScore < seValue) {
					extension++;
				}
			}
		}
		MoveBackup backup;
		board.doMove(move,backup);
		moveCounter++;
		const bool givingCheck = board.setInCheck(board.getSideToMove());
		const bool passedPawnPush = isPawnPush(board,move.to);
		const bool pawnOn7thRank = move.promotionPiece!=EMPTY;
		int newDepth=si.depth-1+extension;
		SearchInfo newSi(true,move,-si.beta,-si.alpha,newDepth,si.ply+1,PV_NODE,si.splitPoint);
		if (moveCounter==1 || isHashMove) {
			newSi.update(newDepth,PV_NODE,-si.beta,-si.alpha,move);
			score = -pvSearch(board, newSi);
		} else {
			int reduction=0;
			if (extension==0 && !isKingAttacked && !givingCheck && !passedPawnPush && !pawnOn7thRank &&
					si.depth>lmrDepthThreshold && move.type == MoveIterator::NON_CAPTURE &&
					killer[si.ply][0] != move && killer[si.ply][1] != move) {
				reduction=getReduction(true, si.depth, moveCounter);
				if (agent->getHistory(board.getPiece(move.from), move.to) <= 0) {
					reduction++;
				}
			}
			newSi.update(newDepth-reduction,CUT_NODE,-si.beta,-si.alpha,move);
			score = -zwSearch(board, newSi);
			if (score > si.alpha && score < si.beta) {
				if (reduction>0) {
					bool research=true;
					if (reduction>2) {
						newSi.update(newDepth-1,CUT_NODE,-si.beta,-si.alpha,move);
						score = -zwSearch(board, newSi);
						research=(score >= si.beta);
					}
					if (research) {
						newSi.update(newDepth,CUT_NODE,-si.beta,-si.alpha,move);
						score = -zwSearch(board, newSi);
					}
				}
				if (score > si.alpha && score < si.beta) {
					newSi.update(newDepth,PV_NODE,-si.beta,-si.alpha,move);
					score = -pvSearch(board, newSi);
				}
			}
		}
		board.undoMove(backup);
		if (stop(si)) {
			return 0;
		}
		nodes++;
		if (score>=si.beta) {
			bestScore=score;
			bestMove=move;
			break;
		}
		if (score>bestScore) {
			bestScore=score;
			if(score>si.alpha ) {
				si.alpha = score;
				bestMove=move;
			}
		}
		if (!stop(si) && agent->getThreadNumber()>1 &&
				agent->getFreeThreads()>0 && si.depth>minSplitDepth) {
			if (agent->spawnThreads(board, &si, getThreadId(), &moves, &move, &hashMove,
					&bestScore, &si.alpha, &currentScore, &moveCounter, &nmMateScore)) {
				if (bestScore>=si.beta) {
					break;
				}
			}
		}
	}
	if (!moveCounter) {
		return si.partialSearch?oldAlpha:isKingAttacked?-maxScore+si.ply:drawScore;
	}
	TranspositionTable::NodeFlag flag;
	if (bestScore>=si.beta) {
		flag = currentScore!=-maxScore && !isLazyEval?
				TranspositionTable::LOWER_EVAL:TranspositionTable::LOWER ;
		agent->updateHistory(board,bestMove,si.depth);
		updateKillers(board,bestMove,si.ply);
	} else if (bestScore>oldAlpha) {
		flag = currentScore!=-maxScore && !isLazyEval?
				TranspositionTable::EXACT_EVAL:TranspositionTable::EXACT;
	} else {
		flag = currentScore!=-maxScore && !isLazyEval?
				TranspositionTable::UPPER_EVAL:TranspositionTable::UPPER;
		bestMove=emptyMove;
	}
	agent->hashPut(key,bestScore,currentScore,si.depth,si.ply,flag,bestMove);
	return bestScore;
}
// zero window search - non pv nodes
int SimplePVSearch::zwSearch(Board& board, SearchInfo& si) {
	if (stop(si)) {
		return 0;
	}
	if (si.ply>maxPlySearched) {
		maxPlySearched=si.ply;
	}
	if (si.depth<=0) {
		si.update(0,si.nodeType,si.beta-1, si.beta, si.move);
		return qSearch(board, si);
	}
	if	(board.isDraw() || si.ply >= maxSearchPly-1) {
		return drawScore;
	}
	if (-maxScore+si.ply >= si.beta) {
		return si.beta;
	}
	if (maxScore-(si.ply+1) < si.beta) {
		return si.beta-1;
	}
	const bool isKingAttacked = board.isInCheck();
	bool isLazyEval = false;
	bool nmMateScore=false;
	bool okToPrune=false;
	int score = 0;
	int currentScore = -maxScore;
	TranspositionTable::HashData hashData;
	MoveIterator::Move hashMove;
	const Key key = si.partialSearch?board.getPartialSearchKey():board.getKey();
	// tt retrieve & prunning
	bool hashOk = agent->hashGet(okToPrune, key, hashData, si.ply, si.depth,
			si.allowNullMove, si.beta-1, si.beta);
	if (hashOk) {
		hashMove = hashData.move();
		if (okToPrune) {
			return hashData.value();
		}
	}
	if (!isKingAttacked) {
		if (hashOk && (hashData.flag() & TranspositionTable::NODE_EVAL)) {
			currentScore = hashData.evalValue();
		} else {
			currentScore = evaluator.evaluate(board,si.beta-1,si.beta);
			isLazyEval = evaluator.isLazyEval();
		}
	}
	//razoring
	if (si.depth < razorDepth && hashMove.none() && si.allowNullMove &&
			!isKingAttacked && !isMateScore(si.beta) &&
			!board.isPawnPromoting() && !si.move.none() &&
			currentScore < si.beta-getRazorMargin(si.depth)) {
		const int newBeta = si.beta-getRazorMargin(si.depth);
		SearchInfo newSi(si.allowNullMove,si.move,newBeta-1,newBeta,0,
				si.ply,CUT_NODE,si.splitPoint);
		score = qSearch(board, newSi);
		if (score < newBeta) {
			return score;
		}
	}
	//futility
	if (!isKingAttacked && si.allowNullMove &&
			si.depth < futilityDepth && !board.isPawnFinal() &&
			!isMateScore(si.beta) && !si.move.none() &&
			currentScore >= si.beta+getFutilityMargin(si.depth,0)) {
		return currentScore-getFutilityMargin(si.depth,0);
	}
	// null move
	if (si.depth>1 && !isKingAttacked && si.allowNullMove &&
			!board.isPawnFinal() && !isMateScore(si.beta) &&
			currentScore >= si.beta-(si.depth>=nullMoveDepth?nullMoveMargin:0)) {
		const int reduction = 3 + (si.depth > 4 ? si.depth/4 : 0);
		MoveBackup backup;
		board.doNullMove(backup);
		SearchInfo newSi(false,emptyMove,si.beta,1-si.beta, si.depth-reduction,
				si.ply+1,CUT_NODE,si.splitPoint);
		score = -zwSearch(board, newSi);
		board.undoNullMove(backup);
		if (stop(si)) {
			return 0;
		}
		if (score >= si.beta) {
			if (score >= maxScore-maxSearchPly) {
				score = si.beta;
			}
			bool okToPrune = true;
			if (si.depth>11) {
				SearchInfo newSi(false,emptyMove,si.alpha,si.beta,si.depth-reduction,
						si.ply+1,CUT_NODE,si.splitPoint);
				const int newScore = zwSearch(board, newSi);
				if (newScore<si.beta) {
					okToPrune = false;
				}
			}
			if (okToPrune) {
				const TranspositionTable::NodeFlag flag = currentScore!=-maxScore && !isLazyEval?
						TranspositionTable::NM_LOWER_EVAL:TranspositionTable::NM_LOWER;
				agent->hashPut(key,score,currentScore,si.depth,si.ply,flag,emptyMove);
				return score;
			}
		} else {
			if (score == -maxScore+si.ply+2) {
				nmMateScore=true;
			}
		}
	}
	//iid
	if (si.depth > allowIIDAtNormal &&	hashMove.none() &&
			currentScore >= si.beta-iidMargin) {
		SearchInfo newSi(false,emptyMove,si.alpha,si.beta,si.depth/2,si.ply,ALL_NODE,si.splitPoint);
		score = zwSearch(board,newSi);
		hashOk=agent->hashGet(key, hashData, si.ply);
		if (hashOk) {
			hashMove = hashData.move();
		}
	}
	MoveIterator moves = MoveIterator();
	MoveIterator::Move move;
	MoveIterator::Move bestMove=emptyMove;
	int moveCounter=0;
	int bestScore=-maxScore;
	while (true) {
		move = selectMove<false>(board, moves, hashMove, si.ply, si.depth);
		if (move.none()) {
			break;
		}
		if (si.partialSearch && move == si.move) {
			continue;
		}
		const bool isHashMove = move.type==MoveIterator::TT_MOVE;
		int extension=0;
		if (isKingAttacked) {
			extension++;
		} else if (isHashMove && si.depth > seNonPVDepth && hashOk && !hashMove.none() &&
				!si.partialSearch && hashData.depth() >= si.depth-3 &&
				(hashData.flag() & TranspositionTable::LOWER)) {
			if (abs(hashData.value()) < winningScore) {
				const int seValue = hashData.value() - seMargin;
				SearchInfo seSi(false, hashMove, true, seValue-1, seValue, si.depth/2, si.ply,
						si.nodeType, si.splitPoint);
				const int partialScore = zwSearch(board,seSi);
				if (partialScore < seValue) {
					extension++;
				}
			}
		}
		MoveBackup backup;
		board.doMove(move,backup);
		moveCounter++;
		const bool givingCheck = board.setInCheck(board.getSideToMove());
		const bool passedPawnPush = isPawnPush(board,move.to);
		const bool pawnOn7thRank = move.promotionPiece!=EMPTY;
		//futility
		if  (	move.type == MoveIterator::NON_CAPTURE &&
				!isKingAttacked &&
				!givingCheck &&
				!passedPawnPush &&
				!pawnOn7thRank &&
				!nmMateScore &&
				extension == 0) {

			if (getMoveCountMargin(si.depth) < moveCounter &&
					si.depth < moveCountDepth && !isMateScore(bestScore) ) {
				board.undoMove(backup);
				continue;
			}
			if (si.depth < futilityDepth) {
				const int futilityScore = currentScore + getFutilityMargin(si.depth,moveCounter);
				if (futilityScore < si.beta) {
					if (futilityScore>bestScore) {
						bestScore=futilityScore;
					}
					board.undoMove(backup);
					continue;
				}
			}
		}
		//reductions
		int reduction=0;
		if (extension==0 && !isKingAttacked && !givingCheck && si.depth>lmrDepthThreshold &&
				!nmMateScore && move.type == MoveIterator::NON_CAPTURE &&
				!isHashMove && moveCounter != 1 && killer[si.ply][0] != move && killer[si.ply][1] != move) {
			reduction=getReduction(false, si.depth, moveCounter);
			if (si.nodeType == CUT_NODE ||
					agent->getHistory(board.getPiece(move.from), move.to) <= 0) {
				reduction++;
			}
		}
		int newDepth=si.depth-1+extension;
		SearchInfo newSi(true,move,si.beta,1-si.beta,newDepth-reduction,
				si.ply+1,si.nodeType==CUT_NODE?ALL_NODE:CUT_NODE,si.splitPoint);
		score = -zwSearch(board, newSi);
		if (score >= si.beta && reduction>0) {
			bool research=true;
			if (reduction>2) {
				newSi.update(newDepth-1,si.nodeType==CUT_NODE?ALL_NODE:CUT_NODE,
						si.beta,1-si.beta,move);
				score = -zwSearch(board, newSi);
				research=(score >= si.beta);
			}
			if (research) {
				newSi.update(newDepth,si.nodeType==CUT_NODE?ALL_NODE:CUT_NODE,
						si.beta,1-si.beta,move);
				score = -zwSearch(board, newSi);
			}
		}
		board.undoMove(backup);
		if (stop(si)) {
			return 0;
		}
		nodes++;
		if (score>=si.beta) {
			bestScore=score;
			bestMove=move;
			break;
		}
		if (score>bestScore) {
			bestScore=score;
			bestMove=move;
		}
		if (!stop(si) && agent->getThreadNumber()>1 &&
				agent->getFreeThreads()>0 && si.depth>minSplitDepth) {
			if (agent->spawnThreads(board, &si, getThreadId(), &moves, &move, &hashMove,
					&bestScore, &si.alpha, &currentScore, &moveCounter, &nmMateScore)) {
				if (bestScore>=si.beta) {
					break;
				}
			}
		}
	}
	if (!moveCounter) {
		return si.partialSearch?si.beta-1:isKingAttacked?-maxScore+si.ply:drawScore;
	}
	TranspositionTable::NodeFlag flag;
	if (bestScore>=si.beta) {
		flag = currentScore!=-maxScore && !isLazyEval?
				TranspositionTable::LOWER_EVAL:TranspositionTable::LOWER;
		agent->updateHistory(board,bestMove,si.depth);
		updateKillers(board,bestMove,si.ply);
	} else {
		flag = currentScore!=-maxScore && !isLazyEval?
				TranspositionTable::UPPER_EVAL:TranspositionTable::UPPER;
		bestMove=emptyMove;
	}
	agent->hashPut(key,bestScore,currentScore,si.depth,si.ply,flag,bestMove);
	return bestScore;
}
Exemple #3
0
int Solve (int nodeType, Position& board, int ply, int alpha, int beta, int depth) {

	// return score for terminal state
	if (board.HasWon(board.get_arrayOfBitboard((board.get_nPlies() - 1) & 1))) {
		return -WIN + ply;
	} else if (board.get_nPlies() == 42) {
		return DRAW;
	}

	// Mate distance pruning
	alpha = std::max (ply - WIN, alpha);
	beta = std::min (WIN - (ply + 1), beta);
	if (alpha >= beta) {
		return alpha;
	}

	TTEntry entry = TranspositionTable.probeTTable(board.get_key());
		
	if (entry.flag == EXACT
		|| entry.flag == L_BOUND && entry.evaluationScore >= beta
		|| entry.flag == U_BOUND && entry.evaluationScore <= alpha) {

		if (entry.evaluationScore >= beta) {
			updateKillers(entry.move, ply);
		}
		return entry.evaluationScore;
	}

	int hashMove = (entry.flag == L_BOUND && entry.evaluationScore < beta) ? entry.move : NO_MOVE;
	int bestScore = -INF;
	int movesMade = 0;
	bool raisedAlpha = false;
	MovePicker mPicker(board, ply, hashMove);
	int bestMove = NO_MOVE;
	
	for (int i=0; i < 7; i++) {
			
		int move = mPicker.getNextMove();
		
		if (move == NO_MOVE) {
			break;
		}

		board.MakeMove(move);
		int score = -Solve(NON_ROOT, board, ply + 1, -beta, -alpha, depth - 1);
		board.UnmakeMove();
		nodesVisited++;
		movesMade++;

		if (score >= beta) {
			TTEntry newEntry = {board.get_key(), L_BOUND, depth, score, move};
			TranspositionTable.storeTTable(board.get_key(), newEntry);
			updateKillers(move, ply);
			updateHistory(depth, ply, move);

			if (movesMade == 1) {
				fh1++;
			} else {
				fh++;
			}
			return score;
		} else if (score > bestScore) {
			bestScore = score;
			bestMove = move;
			if (score > alpha) {
				alpha = score;
				raisedAlpha = true;
			}
		} 
	}

	if (raisedAlpha) {
		TTEntry newEntry = {board.get_key(), EXACT, depth, alpha, bestMove};
		TranspositionTable.storeTTable(board.get_key(), newEntry);
	} else {
		TTEntry newEntry = {board.get_key(), U_BOUND, depth, bestScore, NO_MOVE};
		TranspositionTable.storeTTable(board.get_key(), newEntry);
	}
	
	return bestScore; 
}