void Berry::makeTurn () { std::vector<MoveInfo> res = genMoves(); if (res.size() != 0) { MoveInfo *best = &res[0]; for (uint i=1; i<res.size(); i++) if (res[i].score() > best->score()) best = &res[i]; pin.doMove(*best); } else { pin.doPass(); } }
void MoveInfoTable::decode(ByteStream& strm) { MoveInfo m; unsigned index = 0; m_table.clear(); while (strm.remaining()) { unsigned skip = m.decode(strm); m_table.resize((index += skip) + 1); m_table[index].add(m); } }
void MonsterAI::makeMove() { vector<pair<MoveInfo, int>> moves; for (int i : All(behaviours)) { MoveInfo move = behaviours[i]->getMove(); move.value *= weights[i]; moves.emplace_back(move, weights[i]); if (pickItems) { for (auto elem : Item::stackItems(creature->getPickUpOptions())) { Item* item = elem.second[0]; if (!item->getShopkeeper() && creature->canPickUp(elem.second)) { MoveInfo pickupMove { behaviours[i]->itemValue(item) * weights[i], [=]() { creature->globalMessage(creature->getTheName() + " picks up " + elem.first, ""); creature->pickUp(elem.second); }}; moves.emplace_back(pickupMove, weights[i]); } } } } /*vector<Item*> inventory = creature->getEquipment().getItems([this](Item* item) { return !creature->getEquipment().isEquiped(item);}); for (Item* item : inventory) { bool useless = true; for (PBehaviour& behaviour : behaviours) if (behaviour->itemValue(item) > 0) useless = false; if (useless) moves.push_back({ 0.01, [=]() { creature->globalMessage(creature->getTheName() + " drops " + item->getAName(), ""); creature->drop({item}); }}); }*/ MoveInfo winner {0, nullptr}; for (int i : All(moves)) { MoveInfo& move = moves[i].first; if (move.value > winner.value) winner = move; if (i < moves.size() - 1 && move.value > moves[i + 1].second) break; } CHECK(winner.value > 0); winner.move(); }
void Search::notifyPV(const MoveInfo& info, int multiPVIndex) { if (info.depth <= 0) return; bool uBound = info.score() <= info.alpha; bool lBound = info.score() >= info.beta; int score = info.move.score(); if (verbose) { std::stringstream ss; ss << std::setw(6) << std::left << TextIO::moveToString(pos, info.move, false) << ' ' << std::setw(6) << std::right << score << ' ' << std::setw(6) << nodes << ' ' << std::setw(6) << qNodes; if (uBound) ss << " <="; else if (lBound) ss << " >="; else { std::string PV = TextIO::moveToString(pos, info.move, false) + " "; UndoInfo ui; pos.makeMove(info.move, ui); PV += tt.extractPV(pos); pos.unMakeMove(info.move, ui); ss << ' ' << PV; } std::cout << ss.str() << std::endl; } if (!listener) return; bool isMate = false; if (score > MATE0 / 2) { isMate = true; score = (MATE0 - score) / 2; } else if (score < -MATE0 / 2) { isMate = true; score = -((MATE0 + score - 1) / 2); } U64 tNow = currentTimeMillis(); int time = (int) (tNow - tStart); S64 totNodes = getTotalNodes(); int nps = (time > 0) ? (int)(totNodes / (time / 1000.0)) : 0; listener->notifyPV(info.depth/plyScale, score, time, totNodes, nps, isMate, uBound, lBound, info.pv, multiPVIndex); }
void Decoder::decodeVariation(ByteStream& data) { unsigned pieceNum = 0; // satisfies the compiler Move move; while (true) { Byte b; while ((b = m_strm.get()) > token::End_Marker) { if (move) m_position.doMove(move, pieceNum); pieceNum = decodeMove(b, move); MoveNode* node = new MoveNode(move); m_currentNode->setNext(node); m_currentNode = node; } switch (b) { case token::End_Marker: { MoveNode* node = new MoveNode; m_currentNode->setNext(node); switch (m_strm.get()) { case token::Comment: node->setCommentFlag(data.get()); break; case token::End_Marker: break; default: return; //IO_RAISE(Game, Corrupted, "unexpected token"); } } return; case token::Start_Marker: { MoveNode* current = m_currentNode; m_position.push(); m_position.board().undoMove(move); current->addVariation(m_currentNode = new MoveNode); decodeVariation(data); m_currentNode = current; m_position.pop(); } break; case token::Nag: static_assert(Annotation::Max_Nags >= 7, "Scidb needs at least seven entries"); { nag::ID nag = nag::ID(m_strm.get()); if (nag == 0) move.setLegalMove(false); else m_currentNode->addAnnotation(nag); } break; case token::Mark: if (data.peek() & 0x80) { MoveInfo info; info.decode(data); m_currentNode->addMoveInfo(info); } else { Mark mark; mark.decode(data); m_currentNode->addMark(mark); } break; case token::Comment: m_currentNode->setCommentFlag(data.get()); break; } } }
Move Search::iterativeDeepening(const MoveGen::MoveList& scMovesIn, int maxDepth, U64 initialMaxNodes, bool verbose, int maxPV) { tStart = currentTimeMillis(); totalNodes = 0; if (scMovesIn.size <= 0) return Move(); // No moves to search std::vector<MoveInfo> scMoves; { // If strength is < 10%, only include a subset of the root moves. // At least one move is always included though. std::vector<bool> includedMoves(scMovesIn.size); U64 rndL = pos.zobristHash() ^ randomSeed; includedMoves[(int)(rndL % scMovesIn.size)] = true; double pIncl = (strength < 100) ? strength * strength * 1e-4 : 1.0; for (int mi = 0; mi < scMovesIn.size; mi++) { rndL = 6364136223846793005ULL * rndL + 1442695040888963407ULL; double rnd = ((rndL & 0x7fffffffffffffffULL) % 1000000000) / 1e9; if (!includedMoves[mi] && (rnd < pIncl)) includedMoves[mi] = true; } for (int mi = 0; mi < scMovesIn.size; mi++) { if (includedMoves[mi]) { const Move& m = scMovesIn[mi]; scMoves.push_back(MoveInfo(m, 0)); } } } logFile.open("/home/petero/treelog.dmp", pd, threadNo); const U64 rootNodeIdx = logFile.logPosition(pos, 0, 0, 0); kt.clear(); pd.npsInfo.reset(); // pd.wq.resetStat(); const bool smp = pd.numHelperThreads() > 0; maxNodes = initialMaxNodes; nodesToGo = 0; Position origPos(pos); bool firstIteration = true; Move bestMove = scMoves[0].move; // bestMove is != scMoves[0].move when there is an unresolved fail high this->verbose = verbose; if ((maxDepth < 0) || (maxDepth > MAX_SEARCH_DEPTH)) maxDepth = MAX_SEARCH_DEPTH; maxPV = std::min(maxPV, (int)scMoves.size()); for (size_t i = 0; i < COUNT_OF(searchTreeInfo); i++) searchTreeInfo[i].allowNullMove = true; ht.reScale(); try { for (int depthS = plyScale; ; depthS += plyScale, firstIteration = false) { initNodeStats(); if (listener) listener->notifyDepth(depthS/plyScale); int aspirationDelta = 0; int alpha = 0; UndoInfo ui; bool needMoreTime = false; for (int mi = 0; mi < (int)scMoves.size(); mi++) { if (mi < maxPV) aspirationDelta = (std::abs(scMoves[mi].score()) <= MATE0 / 2) ? aspirationWindow : 1000; if (firstIteration) alpha = -MATE0; else if (mi < maxPV) alpha = std::max(scMoves[mi].score() - aspirationDelta, -MATE0); else alpha = scMoves[maxPV-1].score(); searchNeedMoreTime = (mi > 0); Move& m = scMoves[mi].move; pd.topMove = m; if (currentTimeMillis() - tStart >= 1000) if (listener) listener->notifyCurrMove(m, mi + 1); nodes = qNodes = 0; posHashList[posHashListSize++] = pos.zobristHash(); bool givesCheck = MoveGen::givesCheck(pos, m); int beta; if (firstIteration) beta = MATE0; else if (mi < maxPV) beta = std::min(scMoves[mi].score() + aspirationDelta, MATE0); else beta = alpha + 1; int lmrS = 0; bool isCapture = (pos.getPiece(m.to()) != Piece::EMPTY); bool isPromotion = (m.promoteTo() != Piece::EMPTY); if ((depthS >= 3*plyScale) && !isCapture && !isPromotion && !givesCheck && !passedPawnPush(pos, m) && (mi >= rootLMRMoveCount + maxPV)) { lmrS = plyScale; } pos.makeMove(m, ui); SearchTreeInfo& sti = searchTreeInfo[0]; sti.currentMove = m; sti.currentMoveNo = mi; sti.lmr = lmrS; sti.nodeIdx = rootNodeIdx; int score = -negaScout(smp, -beta, -alpha, 1, depthS - lmrS - plyScale, -1, givesCheck); if ((lmrS > 0) && (score > alpha)) { sti.lmr = 0; score = -negaScout(smp, -beta, -alpha, 1, depthS - plyScale, -1, givesCheck); } U64 nodesThisMove = nodes + qNodes; posHashListSize--; pos.unMakeMove(m, ui); storeSearchResult(scMoves, mi, depthS, alpha, beta, score); if ((verbose && firstIteration) || (mi < maxPV) || (score > scMoves[maxPV-1].score())) notifyPV(scMoves, mi, maxPV); int betaRetryDelta = (mi == 0) ? aspirationDelta * 2 : aspirationDelta; int alphaRetryDelta = aspirationDelta * 2; while ((score >= beta) || ((mi < maxPV) && (score <= alpha))) { nodes = qNodes = 0; posHashList[posHashListSize++] = pos.zobristHash(); bool fh = score >= beta; if (fh) { if (score > MATE0 / 2) betaRetryDelta = MATE0; // Don't use aspiration window when searching for faster mate beta = std::min(score + betaRetryDelta, MATE0); betaRetryDelta = betaRetryDelta * 3 / 2; if (mi != 0) needMoreTime = true; bestMove = m; } else { // score <= alpha if (score < -MATE0 / 2) alphaRetryDelta = MATE0; // Don't use aspiration window when searching for faster mate alpha = std::max(score - alphaRetryDelta, -MATE0); alphaRetryDelta = alphaRetryDelta * 3 / 2; needMoreTime = searchNeedMoreTime = true; } pos.makeMove(m, ui); score = -negaScout(smp, -beta, -alpha, 1, depthS - plyScale, -1, givesCheck); nodesThisMove += nodes + qNodes; posHashListSize--; pos.unMakeMove(m, ui); storeSearchResult(scMoves, mi, depthS, alpha, beta, score); notifyPV(scMoves, mi, maxPV); } scMoves[mi].nodes += nodesThisMove; if ((mi < maxPV) || (score > scMoves[maxPV-1].move.score())) { MoveInfo tmp = scMoves[mi]; int i = mi; while ((i > 0) && (scMoves[i-1].score() < tmp.score())) { scMoves[i] = scMoves[i-1]; i--; } scMoves[i] = tmp; } bestMove = scMoves[0].move; if (!firstIteration) { S64 timeLimit = needMoreTime ? maxTimeMillis : minTimeMillis; if (timeLimit >= 0) { U64 tNow = currentTimeMillis(); if (tNow - tStart >= (U64)timeLimit) break; } } } S64 tNow = currentTimeMillis(); if (verbose) { static_assert(nodesByPly.minValue() == nodesByDepth.minValue(), "Incompatible histograms"); static_assert(nodesByPly.maxValue() == nodesByDepth.maxValue(), "Incompatible histograms"); for (int i = nodesByPly.minValue(); i < nodesByPly.maxValue(); i++) std::cout << std::setw(2) << i << ' ' << std::setw(7) << nodesByPly.get(i) << ' ' << std::setw(7) << nodesByDepth.get(i) << std::endl; std::stringstream ss; ss.precision(3); ss << std::fixed << "Time: " << ((tNow - tStart) * .001); ss.precision(2); ss << " depth:" << (depthS/(double)plyScale) << " nps:" << ((int)(getTotalNodes() / ((tNow - tStart) * .001))); std::cout << ss.str() << std::endl; } if (maxTimeMillis >= 0) if (tNow - tStart >= minTimeMillis) break; if (depthS >= maxDepth * plyScale) break; if (maxNodes >= 0) if (getTotalNodes() >= maxNodes) break; bool enoughDepth = true; for (int i = 0; i < maxPV; i++) { int plyToMate = MATE0 - std::abs(scMoves[i].score()); if (depthS < plyToMate * plyScale) enoughDepth = false; } if (enoughDepth) break; if (tNow > tStart) pd.npsInfo.setBaseNps(getTotalNodesThisThread() * 1000.0 / (tNow - tStart)); if (firstIteration) { std::stable_sort(scMoves.begin()+maxPV, scMoves.end(), MoveInfo::SortByScore()); } else { // Moves that were hard to search should be searched early in the next iteration std::stable_sort(scMoves.begin()+maxPV, scMoves.end(), MoveInfo::SortByNodes()); } // std::cout << "fhInfo depth:" << depthS / plyScale << std::endl; // pd.fhInfo.print(std::cout); // std::cout << "wqStats depth:" << depthS / plyScale << std::endl; // pd.wq.printStats(std::cout, pd.numHelperThreads() + 1); // log([&](std::ostream& os){pd.npsInfo.print(os, depthS / plyScale);}); } } catch (const StopSearch&) { pos = origPos; } notifyStats(); logFile.close(); return bestMove; }