TEST( ConcurrentMapTests, SimpleTest ) { ConcurrentMap<int, string> stringMap; ASSERT_EQ(0u, stringMap.count(5)); ASSERT_EQ("", stringMap.get(5)); ASSERT_EQ(0u, stringMap.count(6)); stringMap.set(6, "hello"); ASSERT_EQ(1u, stringMap.count(6)); ASSERT_EQ("hello", stringMap.get(6)); stringMap.erase(6); ASSERT_EQ(0u, stringMap.count(6)); ASSERT_EQ("", stringMap.get(6)); ASSERT_EQ(1u, stringMap.count(6)); }
TEST_F(ConcurrentContainersTest, concurrentMapTest) { ConcurrentMap<std::string, uint32_t> map; run_on_samples([&map](const std::vector<uint32_t>& sample) { for (size_t i = 0; i < sample.size(); ++i) { std::string s = std::to_string(sample[i]); map.insert({s, sample[i]}); EXPECT_EQ(1, map.count(s)); } }); EXPECT_EQ(m_data_set.size(), map.size()); for (uint32_t x : m_data) { std::string s = std::to_string(x); EXPECT_EQ(1, map.count(s)); auto it = map.find(s); EXPECT_NE(map.end(), it); EXPECT_EQ(s, it->first); EXPECT_EQ(x, it->second); } std::unordered_map<uint32_t, size_t> occurrences; for (uint32_t x : m_data) { ++occurrences[x]; } run_on_samples([&map](const std::vector<uint32_t>& sample) { for (size_t i = 0; i < sample.size(); ++i) { std::string s = std::to_string(sample[i]); map.update( s, [&s, i](const std::string& key, uint32_t& value, bool key_exists) { EXPECT_EQ(s, key); EXPECT_TRUE(key_exists); ++value; }); } }); EXPECT_EQ(m_data_set.size(), map.size()); for (uint32_t x : m_data) { std::string s = std::to_string(x); EXPECT_EQ(1, map.count(s)); auto it = map.find(s); EXPECT_NE(map.end(), it); EXPECT_EQ(s, it->first); EXPECT_EQ(x + occurrences[x], it->second); } run_on_subset_samples([&map](const std::vector<uint32_t>& sample) { for (size_t i = 0; i < sample.size(); ++i) { map.erase(std::to_string(sample[i])); } }); for (uint32_t x : m_subset_data) { std::string s = std::to_string(x); EXPECT_EQ(0, map.count(s)); EXPECT_EQ(map.end(), map.find(s)); } run_on_samples([&map](const std::vector<uint32_t>& sample) { for (size_t i = 0; i < sample.size(); ++i) { map.erase(std::to_string(sample[i])); } }); EXPECT_EQ(0, map.size()); for (uint32_t x : m_data) { std::string s = std::to_string(x); EXPECT_EQ(0, map.count(s)); EXPECT_EQ(map.end(), map.find(s)); } map.insert({{"a", 1}, {"b", 2}, {"c", 3}}); EXPECT_EQ(3, map.size()); map.clear(); EXPECT_EQ(0, map.size()); }
void OracleBuilder::exploreNode(ConcurrentMap<string, HashTable *> &tables, NodeStack &nodes, Color playFor, int commId) { Position pos; Comm::UCICommunicatorPool &pool = Comm::UCICommunicatorPool::getInstance(); Options &opt = Options::getInstance(); HashTable *oracle = tables[""]; pool.sendOption(commId, "MultiPV", to_string(opt.getMaxMoves())); //Main loop Node *current = nullptr; while ((current = nodes.poptop())) { string iterationOutput; const string currentPos = current->getPos(); Line bestLine; /*Set the chessboard to current pos*/ pos.set(currentPos); Color active = pos.side_to_move(); string signature = pos.signature(); uint64_t curHash = pos.hash(); bool insertCopyInSignTable = false; /*Lookup in signature tables*/ if (signature.length() <= opt.getMaxPiecesEnding()) { HashTable *table = nullptr; if (tables.count(signature) == 0) { string filename = opt.getTableFolder() + "/" + signature + ".autosave." + opt.getVariantAsString() + to_string(opt.getCutoffThreshold()) + ".bin"; table = new HashTable(filename); if (tables.findOrInsert(signature, table) != table) { delete table; table = tables[signature]; } } else { table = tables[signature]; } Node *s = nullptr; if ((s = table->findVal(curHash))) { current->updateStatus((Node::StatusFlag) (s->getStatus() | Node::SIGNATURE_TABLE)); if (current->getStatus() & Node::THEM) { OracleBuilder::displayNodeHistory(current); Out::output("Iteration output for error :\n" + iterationOutput); Err::handle("A node has gone from draw to mate, this is an error" " until we decide on what to do, and if it's a bug" " in the engine."); } if (oracle->findOrInsert(curHash, current) != current) delete current; continue; } else { insertCopyInSignTable = true; } } /*Try to find the position and insert it if not found*/ if (oracle->findOrInsert(curHash, current) != current) { Out::output(iterationOutput, "Position already in table.\n", 1); delete current; continue; } /* TODO rethink this, as we should probably remove the position if cut *if (OracleBuilder::cutNode(pos, current)) { * Out::output("Node cut by user-defined function", 1); * continue; *} */ Out::output(iterationOutput, "[" + color_to_string(active) + "] Proceed size : " + to_string(nodes.size()) + "\n"); /*Clear cut*/ if (!pos.hasSufficientMaterial()) { current->updateStatus(Node::DRAW); Out::output(iterationOutput, "[" + color_to_string(active) + "] Insuficient material.\n", 2); //Proceed to next node... continue; } Out::output(iterationOutput, pos.pretty(), 2); /*Hit statistic*/ __atomic_fetch_add(&OracleFinder::signStat_[signature], 1, __ATOMIC_SEQ_CST); string position = "position fen "; position += pos.fen(); pool.send(commId, position); if (playFor != active) { /*We are on a node where the opponent has to play*/ current->updateStatus(Node::AGAINST); /*proceedAgainstNode(pos, current);*/ vector<Move> all = gen_all(pos); /* * Push all node for the side we "play for". * eg: if we are building an oracle for white, we need to push all * possible white positions when computing a black node. */ Out::output(iterationOutput, "Push all lines : ", 2); for (Move m : all) { string uciMv = move_to_string(m); Out::output(iterationOutput, "+", 2); if (!pos.tryAndApplyMove(m)) Err::handle("Illegal move pushed ! (While proceeding against Node)"); string fen = pos.fen(); pos.undoLastMove(); Node *next = new Node(current, fen, Node::PENDING); nodes.push(next); MoveNode move(uciMv, next); current->safeAddMove(move); } Out::output(iterationOutput, "\n", 2); continue; } /**********************************************/ /*Here we are on a node with "playfor" to play*/ /**********************************************/ const vector<Line> &lines = pool.getResultLines(commId); /*Thinking according to the side the engine play for*/ int moveTime = opt.getPlayforMovetime(); Out::output(iterationOutput, "[" + color_to_string(active) + "] Thinking... (" + to_string(moveTime) + ")\n", 1); string cmd = "go "; switch (opt.getSearchMode()) { case DEPTH: cmd += "depth " + to_string(opt.getSearchDepth()); break; case MIXED://Intentional no-break /*TODO: implement mixed search ? likely not, * early multipv in stockfish*/ case TIME: default: cmd += "movetime " + to_string(moveTime); break; } //Send go and wait for engine to finish thinking pool.sendAndWaitBestmove(commId, cmd); Out::output(iterationOutput, Utils::getPrettyLines(pos, lines), 2); bestLine = lines[0]; bool skipThisNode = false; if (bestLine.empty()) { //STALEMATE current->updateStatus(Node::STALEMATE); Out::output(iterationOutput, "[" + color_to_string(active) + "] Bestline is stalemate (cut)\n", 2); //Proceed to next node... skipThisNode = true; } else if (bestLine.isMat()) { Out::output(iterationOutput, "[" + color_to_string(active) + "] Bestline is mate (cut)\n", 2); /*Eval is always negative if it's bad for us*/ if (bestLine.getEval() < 0) { current->updateStatus((Node::StatusFlag)(Node::MATE | Node::THEM)); Out::output("Iteration output for error :\n" + iterationOutput); OracleBuilder::displayNodeHistory(current); Err::handle("A node has gone from draw to mate, this is an error" " until we decide on what to do, and if it's a bug" " in the engine."); } else { current->updateStatus((Node::StatusFlag)(Node::MATE | Node::US)); } skipThisNode = true; } else if (fabs(bestLine.getEval()) > opt.getCutoffThreshold()) { Out::output(iterationOutput, "[" + color_to_string(active) + "] Bestline is above threshold (cut)\n", 2); if (bestLine.getEval() < 0) { current->updateStatus((Node::StatusFlag)(Node::THRESHOLD | Node::THEM)); Out::output("Iteration output for error :\n" + iterationOutput); OracleBuilder::displayNodeHistory(current); Err::handle("A node has gone from draw to threshold, this is an error" " until we decide on what to do, and if it's a bug" " in the engine."); } else { current->updateStatus((Node::StatusFlag)(Node::THRESHOLD | Node::US)); } skipThisNode = true; } /*Trick to avoid code duplicaton for inserting copy in table*/ if (!skipThisNode) { current->updateStatus(Node::DRAW); /* If we are not in fullBuild mode, just insert a pending node in * table if the signature is low enough */ if (!opt.fullBuild() && signature.length() <= opt.getMaxPiecesEnding()) { if (oracle->findOrInsert(curHash, current) != current) delete current; else current->updateStatus((Node::StatusFlag) (current->getStatus() | Node::PENDING)); continue; } } if (insertCopyInSignTable) { Node *cpy = current->lightCopy(); if (tables[signature]->findOrInsert(curHash, cpy) != cpy) delete cpy; } if (skipThisNode) continue; /*If we are here, bestLine is "draw", and we should continue to explore*/ vector<Line> playableLines; /*Select all the candidate lines (bestmove +- deviation)*/ for (Line l : lines) { if (!(l.empty() || l.isMat()) && (fabs(bestLine.getEval() - l.getEval()) <= opt.getBestmoveDeviation() || l.getEval() >= -1)) playableLines.push_back(l); } /* *Then proceed the balanced lines according to the side the engine *play for. */ if (playFor != active) Err::handle("Current side should be the engine plays for"); /* * General Idea : try to force repetition by trying to find a playable * position in the hashtable. If not sort the playable moves * according to a user defined comparator */ Node *next = NULL; /*The elected move*/ string mv; /*Try to find a position in the table*/ /* There must be a reason to go trough it backward, but I can't * remember it right now. */ Line l; for (auto rit = playableLines.rbegin(); rit != playableLines.rend(); ++rit) { l = *rit; mv = l.firstMove(); if (!pos.tryAndApplyMove(mv)) { Err::output(pos.pretty()); Err::output("Move : " + mv); Err::handle("Illegal move while proceeding a draw node"); } /*This is the next pos*/ uint64_t hashpos = pos.hash(); pos.undoLastMove(); //Jean Louis' idea to force finding positions in oracle next = oracle->findVal(hashpos); if (next) { next->safeAddParent(current); break; } } /*No repetition found, sort the playable lines*/ if (!next) { std::sort(playableLines.begin(), playableLines.end(), [&pos](const Line &lhs, const Line &rhs) { return pos.compareLines(lhs, rhs); }); l = playableLines[0]; mv = l.firstMove(); pos.tryAndApplyMove(mv); string fenpos = pos.fen(); pos.undoLastMove(); //no next position in the table, push the node to stack next = new Node(current, fenpos, Node::PENDING); Out::output(iterationOutput, "[" + color_to_string(active) + "] Pushed first line (" + mv + ") : " + fenpos + "\n", 2); nodes.push(next); } /*Whatever the move is, add it to our move list*/ MoveNode move(mv, next); current->safeAddMove(move); Out::output(iterationOutput, "-----------------------\n", 1); /*Send the whole iteration output*/ Out::output(iterationOutput); } }