Example #1
0
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());
}
Example #3
0
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);
    }
}