<commit_before>#include "paxos/logging.hpp"
#include "paxos/roles.hpp"

void
RegisterProposer(
    std::shared_ptr<Receiver> receiver,
    std::shared_ptr<Sender> sender,
    std::shared_ptr<ProposerContext> context)
{
    using namespace std::placeholders;

    receiver->RegisterCallback(
        Callback(std::bind(HandleRequest, std::placeholders::_1, context, sender)),
        MessageType::RequestMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandlePromise, std::placeholders::_1, context, sender)),
        MessageType::PromiseMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandleNackTie, std::placeholders::_1, context, sender)),
        MessageType::NackTieMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandleNack, std::placeholders::_1, context, sender)),
        MessageType::NackMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandleAccepted, std::placeholders::_1, context, sender)),
        MessageType::AcceptedMessage
    );
}

void
RegisterAcceptor(
    std::shared_ptr<Receiver> receiver,
    std::shared_ptr<Sender> sender,
    std::shared_ptr<AcceptorContext> context)
{
    using namespace std::placeholders;

    receiver->RegisterCallback(
        Callback(std::bind(HandlePrepare, std::placeholders::_1, context, sender)),
        MessageType::PrepareMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandleAccept, std::placeholders::_1, context, sender)),
        MessageType::AcceptMessage
    );
}

void
RegisterLearner(
    std::shared_ptr<Receiver> receiver,
    std::shared_ptr<Sender> sender,
    std::shared_ptr<LearnerContext> context)
{
    using namespace std::placeholders;

    receiver->RegisterCallback(
        Callback(std::bind(HandleProclaim, std::placeholders::_1, context, sender)),
        MessageType::AcceptedMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandleUpdated, std::placeholders::_1, context, sender)),
        MessageType::UpdatedMessage
    );
}

void
RegisterUpdater(
    std::shared_ptr<Receiver> receiver,
    std::shared_ptr<Sender> sender,
    std::shared_ptr<UpdaterContext> context)
{
    using namespace std::placeholders;

    receiver->RegisterCallback(
        Callback(std::bind(HandleUpdate, std::placeholders::_1, context, sender)),
        MessageType::UpdateMessage
    );
}

void
HandleRequest(
    Message message,
    std::shared_ptr<ProposerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleRequest | " << Serialize(message);
    if (!message.decree.content.empty())
    {
        context->requested_values.push_back(
            std::make_tuple(message.decree.content, message.decree.type));
    }

    if (!context->in_progress.test_and_set())
    {
        Message response = Response(message, MessageType::PrepareMessage);
        if (context->ledger->Size() > 0)
        {
            response.decree.number = context->ledger->Tail().number + 1;
        }
        else
        {
            response.decree.number = 1;
        }
        response.decree.content = "";

        sender->ReplyAll(response);
    }
}

void
HandlePromise(
    Message message,
    std::shared_ptr<ProposerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandlePromise | " << Serialize(message);

    if (IsDecreeHigher(message.decree,
                       context->highest_proposed_decree.Value()) &&
        context->replicaset->Contains(message.from))
    {
        //
        // If the messaged decree is higher than any previoiusly promised
        // decree and the sender is a known sender in our replica set then
        // remember the sent decree as the highest promised decree.
        //
        context->highest_proposed_decree = message.decree;
    }

    if (context->promise_map.find(message.decree) == context->promise_map.end())
    {
        //
        // If there is no entry for the messaged decree then make an entry.
        //
        context->promise_map[message.decree] = std::make_shared<ReplicaSet>();
    }

    if (IsDecreeEqual(message.decree, context->highest_proposed_decree.Value()))
    {
        bool duplicate = context->promise_map[message.decree]
                             ->Contains(message.from);
        //
        // If the messaged decree is the highest promised decree then update
        // our promised decree map and calculate if majority of replicas have
        // sent promises for the decree.
        //
        context->promise_map[message.decree]->Add(message.from);

        int minimum_quorum = context->replicaset->GetSize() / 2 + 1;
        int received_promises = context->promise_map[message.decree]
                                       ->Intersection(context->replicaset)
                                       ->GetSize();

        //
        // If the messaged decree is a duplicate message allow a possible
        // resend of accept message.
        //
        if (received_promises == minimum_quorum ||
            (received_promises >= minimum_quorum && duplicate))
        {
            if (context->highest_proposed_decree.Value().content.empty() &&
                !context->requested_values.empty())
            {
                //
                // If the following are true...
                //
                //     i)   the majority of replicas have sent promises
                //     ii)  the highest proposed decree is empty
                //     iii) we have pending requested values
                //
                // ... then we should update the highest proposed decree with
                // the requested values.
                //
                Decree updated_highest_proposed_decree =
                    context->highest_proposed_decree.Value();
                updated_highest_proposed_decree.content = std::get<0>(
                    context->requested_values[0]);
                updated_highest_proposed_decree.type = std::get<1>(
                    context->requested_values[0]);

                context->highest_proposed_decree = updated_highest_proposed_decree;
                context->requested_values.erase(
                    context->requested_values.begin());
            }
            message.decree = context->highest_proposed_decree.Value();

            if (!message.decree.content.empty())
            {
                sender->ReplyAll(Response(message, MessageType::AcceptMessage));
            }
        }
    }

}

void
HandleNackTie(
    Message message,
    std::shared_ptr<ProposerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleNackTie | " << Serialize(message);

    Message nack_response(
        message.decree,
        message.to,
        message.to,
        MessageType::PrepareMessage
    );
    nack_response.decree.number += 1;
    sender->ReplyAll(nack_response);
}

void
HandleNack(
    Message message,
    std::shared_ptr<ProposerContext> context,
    std::shared_ptr<Sender> sender)
{
    context->ignore_handler(message.decree.content);
}

void
HandleAccepted(
    Message message,
    std::shared_ptr<ProposerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleAccepted| " << Serialize(message);

    if (context->promise_map.find(message.decree) != context->promise_map.end()
        && context->promise_map[message.decree]
                  ->Intersection(context->replicaset)
                  ->GetSize() >= context->replicaset->GetSize())
    {
        //
        // If we have received an accepted message from every replica in the
        // replicaset for the given decree, then we are unlikely to receive
        // them again. Therefore we will reclaim memory.
        //
        context->promise_map.erase(message.decree);
    }

    std::atomic_flag_clear(&context->in_progress);

    if (context->requested_values.size() > 0)
    {
        //
        // Setup next round for pending proposals.
        //
        sender->Reply(
            Message(
                Decree(),
                message.to,
                message.to,
                MessageType::RequestMessage
            )
        );
    }
}

void
HandlePrepare(
    Message message,
    std::shared_ptr<AcceptorContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandlePrepare | " << Serialize(message);

    if (IsDecreeHigher(message.decree, context->promised_decree.Value()) ||
        IsDecreeIdentical(message.decree, context->promised_decree.Value()))
    {
        //
        // If the messaged decree is higher than any decree seen before or the
        // messaged decree is identical to the current promised decree then
        // save it on persistent storage and send a promised message.
        //
        context->promised_decree = message.decree;
        sender->Reply(Response(message, MessageType::PromiseMessage));
    }
    else if (IsDecreeEqual(message.decree, context->promised_decree.Value()))

    {
        //
        // If the messaged decree is equal to current promised decree and
        // sent from a different replica then there may be dueling
        // proposers. Send a NackTie and let the proposer handle it.
        //
        sender->Reply(Response(message, MessageType::NackTieMessage));
    }
    else
    {
        //
        // If the messaged decree is lower than current promised decree then.
        // the other proposer is behind. Send a Nack.
        //
        sender->Reply(Response(message, MessageType::NackMessage));
    }
}

void
HandleAccept(
    Message message,
    std::shared_ptr<AcceptorContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleAccept  | " << Serialize(message);

    if (IsDecreeHigherOrEqual(message.decree, context->promised_decree.Value()))
    {
        if (IsDecreeHigher(message.decree, context->accepted_decree.Value()))
        {
            context->accepted_decree = message.decree;
        }
        sender->ReplyAll(Response(message, MessageType::AcceptedMessage));
    }
}

void
HandleProclaim(
    Message message,
    std::shared_ptr<LearnerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleProclaim| " << Serialize(message);

    std::lock_guard<std::mutex> lock(context->mutex);

    if (context->accepted_map.find(message.decree) == context->accepted_map.end())
    {
        context->accepted_map[message.decree] = std::shared_ptr<ReplicaSet>(
                                                    new ReplicaSet());
    }
    if (context->replicaset->Contains(message.from))
    {
        context->accepted_map[message.decree]->Add(message.from);
    }

    int minimum_quorum = context->replicaset->GetSize() / 2 + 1;
    int accepted_quorum = context->accepted_map[message.decree]
                                 ->Intersection(context->replicaset)
                                 ->GetSize();

    if (accepted_quorum >= minimum_quorum)
    {
        if (IsDecreeOrdered(context->ledger->Tail(), message.original_decree)
            && !context->is_observer)
        {
            //
            // If decree is ordered then append to ledger. We check the decree
            // because decree may be incremented in response to NACK.
            //
            context->ledger->Append(message.decree);
        }
        else if (IsDecreeLower(context->ledger->Tail(), message.decree))
        {
            //
            // Save the decree in memory if the decree is a future decree that
            // has not yet been written to our ledger.
            //
            context->tracked_future_decrees.push(message.decree);
        }
        if (context->tracked_future_decrees.size() > 0 &&
            IsDecreeOrdered(context->ledger->Tail(),
                            context->tracked_future_decrees.top()) &&
            !context->is_observer)
        {
            Decree current_decree, previous_decree = context->ledger->Tail();
            while (context->tracked_future_decrees.size() > 0)
            {
                current_decree = context->tracked_future_decrees.top();
                if (IsDecreeOrdered(previous_decree, current_decree))
                {
                    //
                    // If tracked_future_decrees contains the next ordered
                    // decree then append to the ledger.
                    //
                    context->ledger->Append(current_decree);
                    previous_decree = current_decree;
                    context->tracked_future_decrees.pop();
                }
                else
                {
                    //
                    // Else tracked_future_decrees doesn't contain any more
                    // decrees of interest.
                    //
                    break;
                }
            }
        }
        else if (context->tracked_future_decrees.size() > 0 &&
                 !IsDecreeOrdered(context->ledger->Tail(),
                                  context->tracked_future_decrees.top()))
        {
            //
            // If the decree is not in order with the last decree recorded in
            // our ledger then there must be holes in our ledger.
            //
            Message response = Response(message, MessageType::UpdateMessage);
            response.decree = context->ledger->Tail();
            response.to = message.decree.author;
            sender->Reply(response);
        }
    }

    if (accepted_quorum == context->replicaset->GetSize())
    {
        //
        // All votes for decree have been accounted for. Now clean up memory.
        //
        context->accepted_map.erase(message.decree);
    }
}

void
HandleUpdated(
    Message message,
    std::shared_ptr<LearnerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleUpdated | " << Serialize(message);

    if (IsDecreeOrdered(context->ledger->Tail(), message.decree))
    {
        //
        // Append the decree iff the decree is in order with the last decree
        // recorded in our ledger.
        //
        context->ledger->Append(message.decree);

        Decree current_decree, previous_decree = message.decree;
        while (context->tracked_future_decrees.size() > 0)
        {
            current_decree = context->tracked_future_decrees.top();

            if (IsDecreeOrdered(previous_decree, current_decree))
            {
                //
                // If tracked_future_decrees contains the next ordered decree
                // then append to the ledger.
                //
                context->ledger->Append(current_decree);
                previous_decree = current_decree;
                context->tracked_future_decrees.pop();
            }
            else
            {
                //
                // Else tracked_future_decrees doesn't contain any more decrees
                // of interest.
                //
                break;
            }
        }

        if (IsDecreeEqual(message.decree, previous_decree))
        {
            //
            // If the tracked_future_decrees did not contain the next ordered
            // decree then ask the message sender to send us more updates.
            //
            sender->Reply(Response(message, MessageType::UpdateMessage));
        }
    }
}

void
HandleUpdate(
    Message message,
    std::shared_ptr<UpdaterContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleUpdate| " << Serialize(message);

    //
    // Check if our ledger has the next logical ordered decree.
    //
    Decree next = context->ledger->Next(message.decree);

    if (IsDecreeOrdered(message.decree, next))
    {
        message.decree = next;
        sender->Reply(Response(message, MessageType::UpdatedMessage));
    }
}
<commit_msg>Update role logs to show decree number first<commit_after>#include "paxos/logging.hpp"
#include "paxos/roles.hpp"

void
RegisterProposer(
    std::shared_ptr<Receiver> receiver,
    std::shared_ptr<Sender> sender,
    std::shared_ptr<ProposerContext> context)
{
    using namespace std::placeholders;

    receiver->RegisterCallback(
        Callback(std::bind(HandleRequest, std::placeholders::_1, context, sender)),
        MessageType::RequestMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandlePromise, std::placeholders::_1, context, sender)),
        MessageType::PromiseMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandleNackTie, std::placeholders::_1, context, sender)),
        MessageType::NackTieMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandleNack, std::placeholders::_1, context, sender)),
        MessageType::NackMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandleAccepted, std::placeholders::_1, context, sender)),
        MessageType::AcceptedMessage
    );
}

void
RegisterAcceptor(
    std::shared_ptr<Receiver> receiver,
    std::shared_ptr<Sender> sender,
    std::shared_ptr<AcceptorContext> context)
{
    using namespace std::placeholders;

    receiver->RegisterCallback(
        Callback(std::bind(HandlePrepare, std::placeholders::_1, context, sender)),
        MessageType::PrepareMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandleAccept, std::placeholders::_1, context, sender)),
        MessageType::AcceptMessage
    );
}

void
RegisterLearner(
    std::shared_ptr<Receiver> receiver,
    std::shared_ptr<Sender> sender,
    std::shared_ptr<LearnerContext> context)
{
    using namespace std::placeholders;

    receiver->RegisterCallback(
        Callback(std::bind(HandleProclaim, std::placeholders::_1, context, sender)),
        MessageType::AcceptedMessage
    );
    receiver->RegisterCallback(
        Callback(std::bind(HandleUpdated, std::placeholders::_1, context, sender)),
        MessageType::UpdatedMessage
    );
}

void
RegisterUpdater(
    std::shared_ptr<Receiver> receiver,
    std::shared_ptr<Sender> sender,
    std::shared_ptr<UpdaterContext> context)
{
    using namespace std::placeholders;

    receiver->RegisterCallback(
        Callback(std::bind(HandleUpdate, std::placeholders::_1, context, sender)),
        MessageType::UpdateMessage
    );
}

void
HandleRequest(
    Message message,
    std::shared_ptr<ProposerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleRequest | " << message.decree.number << "|"
                        << Serialize(message);
    if (!message.decree.content.empty())
    {
        context->requested_values.push_back(
            std::make_tuple(message.decree.content, message.decree.type));
    }

    if (!context->in_progress.test_and_set())
    {
        Message response = Response(message, MessageType::PrepareMessage);
        if (context->ledger->Size() > 0)
        {
            response.decree.number = context->ledger->Tail().number + 1;
        }
        else
        {
            response.decree.number = 1;
        }
        response.decree.content = "";

        sender->ReplyAll(response);
    }
}

void
HandlePromise(
    Message message,
    std::shared_ptr<ProposerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandlePromise | " << message.decree.number << "|"
                        << Serialize(message);

    if (IsDecreeHigher(message.decree,
                       context->highest_proposed_decree.Value()) &&
        context->replicaset->Contains(message.from))
    {
        //
        // If the messaged decree is higher than any previoiusly promised
        // decree and the sender is a known sender in our replica set then
        // remember the sent decree as the highest promised decree.
        //
        context->highest_proposed_decree = message.decree;
    }

    if (context->promise_map.find(message.decree) == context->promise_map.end())
    {
        //
        // If there is no entry for the messaged decree then make an entry.
        //
        context->promise_map[message.decree] = std::make_shared<ReplicaSet>();
    }

    if (IsDecreeEqual(message.decree, context->highest_proposed_decree.Value()))
    {
        bool duplicate = context->promise_map[message.decree]
                             ->Contains(message.from);
        //
        // If the messaged decree is the highest promised decree then update
        // our promised decree map and calculate if majority of replicas have
        // sent promises for the decree.
        //
        context->promise_map[message.decree]->Add(message.from);

        int minimum_quorum = context->replicaset->GetSize() / 2 + 1;
        int received_promises = context->promise_map[message.decree]
                                       ->Intersection(context->replicaset)
                                       ->GetSize();

        //
        // If the messaged decree is a duplicate message allow a possible
        // resend of accept message.
        //
        if (received_promises == minimum_quorum ||
            (received_promises >= minimum_quorum && duplicate))
        {
            if (context->highest_proposed_decree.Value().content.empty() &&
                !context->requested_values.empty())
            {
                //
                // If the following are true...
                //
                //     i)   the majority of replicas have sent promises
                //     ii)  the highest proposed decree is empty
                //     iii) we have pending requested values
                //
                // ... then we should update the highest proposed decree with
                // the requested values.
                //
                Decree updated_highest_proposed_decree =
                    context->highest_proposed_decree.Value();
                updated_highest_proposed_decree.content = std::get<0>(
                    context->requested_values[0]);
                updated_highest_proposed_decree.type = std::get<1>(
                    context->requested_values[0]);

                context->highest_proposed_decree = updated_highest_proposed_decree;
                context->requested_values.erase(
                    context->requested_values.begin());
            }
            message.decree = context->highest_proposed_decree.Value();

            if (!message.decree.content.empty())
            {
                sender->ReplyAll(Response(message, MessageType::AcceptMessage));
            }
        }
    }

}

void
HandleNackTie(
    Message message,
    std::shared_ptr<ProposerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleNackTie | " << message.decree.number << "|"
                        << Serialize(message);

    Message nack_response(
        message.decree,
        message.to,
        message.to,
        MessageType::PrepareMessage
    );
    nack_response.decree.number += 1;
    sender->ReplyAll(nack_response);
}

void
HandleNack(
    Message message,
    std::shared_ptr<ProposerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleTie | " << message.decree.number << "|"
                        << Serialize(message);

    context->ignore_handler(message.decree.content);
}

void
HandleAccepted(
    Message message,
    std::shared_ptr<ProposerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleAccepted| " << message.decree.number << "|"
                        << Serialize(message);

    if (context->promise_map.find(message.decree) != context->promise_map.end()
        && context->promise_map[message.decree]
                  ->Intersection(context->replicaset)
                  ->GetSize() >= context->replicaset->GetSize())
    {
        //
        // If we have received an accepted message from every replica in the
        // replicaset for the given decree, then we are unlikely to receive
        // them again. Therefore we will reclaim memory.
        //
        context->promise_map.erase(message.decree);
    }

    std::atomic_flag_clear(&context->in_progress);

    if (context->requested_values.size() > 0)
    {
        //
        // Setup next round for pending proposals.
        //
        sender->Reply(
            Message(
                Decree(),
                message.to,
                message.to,
                MessageType::RequestMessage
            )
        );
    }
}

void
HandlePrepare(
    Message message,
    std::shared_ptr<AcceptorContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandlePrepare | " << message.decree.number << "|"
                        << Serialize(message);

    if (IsDecreeHigher(message.decree, context->promised_decree.Value()) ||
        IsDecreeIdentical(message.decree, context->promised_decree.Value()))
    {
        //
        // If the messaged decree is higher than any decree seen before or the
        // messaged decree is identical to the current promised decree then
        // save it on persistent storage and send a promised message.
        //
        context->promised_decree = message.decree;
        sender->Reply(Response(message, MessageType::PromiseMessage));
    }
    else if (IsDecreeEqual(message.decree, context->promised_decree.Value()))

    {
        //
        // If the messaged decree is equal to current promised decree and
        // sent from a different replica then there may be dueling
        // proposers. Send a NackTie and let the proposer handle it.
        //
        sender->Reply(Response(message, MessageType::NackTieMessage));
    }
    else
    {
        //
        // If the messaged decree is lower than current promised decree then.
        // the other proposer is behind. Send a Nack.
        //
        sender->Reply(Response(message, MessageType::NackMessage));
    }
}

void
HandleAccept(
    Message message,
    std::shared_ptr<AcceptorContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleAccept  | " << message.decree.number << "|"
                        << Serialize(message);

    if (IsDecreeHigherOrEqual(message.decree, context->promised_decree.Value()))
    {
        if (IsDecreeHigher(message.decree, context->accepted_decree.Value()))
        {
            context->accepted_decree = message.decree;
        }
        sender->ReplyAll(Response(message, MessageType::AcceptedMessage));
    }
}

void
HandleProclaim(
    Message message,
    std::shared_ptr<LearnerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleProclaim| " << message.decree.number << "|"
                        << Serialize(message);

    std::lock_guard<std::mutex> lock(context->mutex);

    if (context->accepted_map.find(message.decree) == context->accepted_map.end())
    {
        context->accepted_map[message.decree] = std::shared_ptr<ReplicaSet>(
                                                    new ReplicaSet());
    }
    if (context->replicaset->Contains(message.from))
    {
        context->accepted_map[message.decree]->Add(message.from);
    }

    int minimum_quorum = context->replicaset->GetSize() / 2 + 1;
    int accepted_quorum = context->accepted_map[message.decree]
                                 ->Intersection(context->replicaset)
                                 ->GetSize();

    if (accepted_quorum >= minimum_quorum)
    {
        if (IsDecreeOrdered(context->ledger->Tail(), message.original_decree)
            && !context->is_observer)
        {
            //
            // If decree is ordered then append to ledger. We check the decree
            // because decree may be incremented in response to NACK.
            //
            context->ledger->Append(message.decree);
        }
        else if (IsDecreeLower(context->ledger->Tail(), message.decree))
        {
            //
            // Save the decree in memory if the decree is a future decree that
            // has not yet been written to our ledger.
            //
            context->tracked_future_decrees.push(message.decree);
        }
        if (context->tracked_future_decrees.size() > 0 &&
            IsDecreeOrdered(context->ledger->Tail(),
                            context->tracked_future_decrees.top()) &&
            !context->is_observer)
        {
            Decree current_decree, previous_decree = context->ledger->Tail();
            while (context->tracked_future_decrees.size() > 0)
            {
                current_decree = context->tracked_future_decrees.top();
                if (IsDecreeOrdered(previous_decree, current_decree))
                {
                    //
                    // If tracked_future_decrees contains the next ordered
                    // decree then append to the ledger.
                    //
                    context->ledger->Append(current_decree);
                    previous_decree = current_decree;
                    context->tracked_future_decrees.pop();
                }
                else
                {
                    //
                    // Else tracked_future_decrees doesn't contain any more
                    // decrees of interest.
                    //
                    break;
                }
            }
        }
        else if (context->tracked_future_decrees.size() > 0 &&
                 !IsDecreeOrdered(context->ledger->Tail(),
                                  context->tracked_future_decrees.top()))
        {
            //
            // If the decree is not in order with the last decree recorded in
            // our ledger then there must be holes in our ledger.
            //
            Message response = Response(message, MessageType::UpdateMessage);
            response.decree = context->ledger->Tail();
            response.to = message.decree.author;
            sender->Reply(response);
        }
    }

    if (accepted_quorum == context->replicaset->GetSize())
    {
        //
        // All votes for decree have been accounted for. Now clean up memory.
        //
        context->accepted_map.erase(message.decree);
    }
}

void
HandleUpdated(
    Message message,
    std::shared_ptr<LearnerContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleUpdated | " << message.decree.number << "|"
                        << Serialize(message);

    if (IsDecreeOrdered(context->ledger->Tail(), message.decree))
    {
        //
        // Append the decree iff the decree is in order with the last decree
        // recorded in our ledger.
        //
        context->ledger->Append(message.decree);

        Decree current_decree, previous_decree = message.decree;
        while (context->tracked_future_decrees.size() > 0)
        {
            current_decree = context->tracked_future_decrees.top();

            if (IsDecreeOrdered(previous_decree, current_decree))
            {
                //
                // If tracked_future_decrees contains the next ordered decree
                // then append to the ledger.
                //
                context->ledger->Append(current_decree);
                previous_decree = current_decree;
                context->tracked_future_decrees.pop();
            }
            else
            {
                //
                // Else tracked_future_decrees doesn't contain any more decrees
                // of interest.
                //
                break;
            }
        }

        if (IsDecreeEqual(message.decree, previous_decree))
        {
            //
            // If the tracked_future_decrees did not contain the next ordered
            // decree then ask the message sender to send us more updates.
            //
            sender->Reply(Response(message, MessageType::UpdateMessage));
        }
    }
}

void
HandleUpdate(
    Message message,
    std::shared_ptr<UpdaterContext> context,
    std::shared_ptr<Sender> sender)
{
    LOG(LogLevel::Info) << "HandleUpdate| " << message.decree.number << "|"
                        << Serialize(message);

    //
    // Check if our ledger has the next logical ordered decree.
    //
    Decree next = context->ledger->Next(message.decree);

    if (IsDecreeOrdered(message.decree, next))
    {
        message.decree = next;
        sender->Reply(Response(message, MessageType::UpdatedMessage));
    }
}
<|endoftext|>


<commit_before>#include "defs.h"
#include "search.h"
#include "eval.h"
#include "movegen.h"
#include "transptable.h"
#include <string>
#include <algorithm>
#include <time.h>
#include <iostream>
#include <functional>

Search::Search(const Board& board, bool logUci) {
  _logUci = logUci;
  _board = board;
  _bestScore = 0;
}

void Search::perform(int depth) {
  _rootMax(_board, depth);

  MoveList pv = _getPv(_board, depth);
  if (_logUci) {
    _logUciInfo(pv, depth, _bestMove, _bestScore);
  }
}

void Search::_logUciInfo(const MoveList& pv, int depth, Move bestMove, int bestScore) {
  std::string pvString;
  for(auto move : pv) {
    pvString += move.getNotation() + " ";
  }

  std::string scoreString;
  if (bestScore == INF) {
    scoreString = "mate " + std::to_string(pv.size());
  } else if (_bestScore == -INF) {
    scoreString = "mate -" + std::to_string(pv.size());
  } else {
    scoreString = "cp " + std::to_string(bestScore);
  }

  std::cout << "info depth " + std::to_string(depth) + " ";
  std::cout << "score " + scoreString + " ";
  std::cout << "pv " + pvString;
  std::cout << std::endl;
}

MoveList Search::_getPv(const Board& board, int depth) {
  int bestScore = INF;
  Move bestMove;
  Board bestBoard;

  bool foundBest = false;
  Board movedBoard;

  for (auto move : MoveGen(board).getLegalMoves()) {
    movedBoard = board;
    movedBoard.doMove(move);

    ZKey movedZKey = movedBoard.getZKey();

    // Due to the way negamax works, bestScore is the lowest score
    if (_tt.contains(movedZKey) && _tt.getFlag(movedZKey) == TranspTable::EXACT && _tt.getScore(movedZKey) < bestScore) {
      foundBest = true;
      bestScore = _tt.getScore(movedZKey);
      bestMove = move;
      bestBoard = movedBoard;
    }
  }

  if (!foundBest || depth == 0) {
    return MoveList();
  } else {
    MoveList pvList = _getPv(bestBoard, depth-1);
    pvList.insert(pvList.begin(), bestMove);
    return pvList;
  }
}

Move Search::getBestMove() {
  return _bestMove;
}

int Search::getBestScore() {
  return _bestScore;
}

void Search::_rootMax(const Board& board, int depth) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();
  _orderMoves(board, legalMoves);

  int bestScore = -INF;
  int currScore;

  Move bestMove;
  Board movedBoard;
  for (auto move : legalMoves) {
    movedBoard = board;
    movedBoard.doMove(move);

    currScore = -_negaMax(movedBoard, depth-1, -INF, -bestScore);

    if (currScore > bestScore) {
      bestMove = move;
      bestScore = currScore;

      // Break if we've found a checkmate
      if (bestScore == INF) {
        break;
      }
    }
  }

  // If we couldn't find a path other than checkmate, just pick the first legal move
  if (bestMove.getFlags() & Move::NULL_MOVE) {
    bestMove = legalMoves.at(0);
  }

  _tt.set(board.getZKey(), bestScore, depth, TranspTable::EXACT);

  _bestMove = bestMove;
  _bestScore = bestScore;
}

void Search::_orderMoves(const Board& board, MoveList& moveList) {
  // Order moves by tt score
  std::sort(moveList.begin(), moveList.end(), std::bind(&Search::_compareMovesTt, this, board, std::placeholders::_1, std::placeholders::_2));

  // Ending index of sorted section of moveList
  unsigned int i;

  // Order captures by MVV/LVA
  Board movedBoard;
  for (i=0;i<moveList.size();i++) {
    movedBoard = board;
    movedBoard.doMove(moveList.at(i));

    if (!_tt.contains(movedBoard.getZKey())) {
      break;
    }
  }

  std::sort(moveList.begin() + i, moveList.end(), std::bind(&Search::_compareMovesMvvLva, this, std::placeholders::_1, std::placeholders::_2));

  // Order promotions by promotion value
  for(;i<moveList.size();i++) {
    if (!(moveList.at(i).getFlags() & Move::CAPTURE)) {
      break;
    }
  }
  std::sort(moveList.begin() + i, moveList.end(), std::bind(&Search::_compareMovesPromotionValue, this, std::placeholders::_1, std::placeholders::_2));
}

void Search::_orderMovesQSearch(MoveList& moveList) {
  std::sort(moveList.begin(), moveList.end(), std::bind(&Search::_compareMovesMvvLva, this, std::placeholders::_1, std::placeholders::_2));
}

bool Search::_compareMovesTt(Board board, Move a, Move b) {
  Board aBoard = board;
  Board bBoard = board;

  aBoard.doMove(a);
  bBoard.doMove(b);

  int aScore = _tt.contains(aBoard.getZKey()) ? _tt.getScore(aBoard.getZKey()) : -INF;
  int bScore = _tt.contains(bBoard.getZKey()) ? _tt.getScore(bBoard.getZKey()) : -INF;

  return aScore < bScore;
}

bool Search::_compareMovesMvvLva(Move a, Move b) {
  bool aIsCapture = a.getFlags() & Move::CAPTURE;
  bool bIsCapture = b.getFlags() & Move::CAPTURE;

  if (aIsCapture && !bIsCapture) {
    return true;
  } else if (bIsCapture && !aIsCapture) {
    return false;
  } else { // Both captures
    int aCaptureValue = _getPieceValue(a.getCapturedPieceType());
    int bCaptureValue = _getPieceValue(b.getCapturedPieceType());

    if (aCaptureValue != bCaptureValue) {
      return aCaptureValue > bCaptureValue;
    } else { // Captured piece type value is the same, order by attacker value
      int aPieceValue = _getPieceValue(a.getPieceType());
      int bPieceValue = _getPieceValue(b.getPieceType());

      return aPieceValue < bPieceValue;
    }
  }
}

bool Search::_compareMovesPromotionValue(Move a, Move b) {
  bool aIsPromotion = a.getFlags() & Move::PROMOTION;
  bool bIsPromotion = b.getFlags() & Move::PROMOTION;

  if (aIsPromotion && !bIsPromotion) {
    return true;
  } else if (bIsPromotion && !aIsPromotion) {
    return false;
  } else { // Both promotions
    int aPromotionValue = _getPieceValue(a.getPromotionPieceType());
    int bPromotionValue = _getPieceValue(b.getPromotionPieceType());

    return aPromotionValue < bPromotionValue;
  }
}

int Search::_getPieceValue(PieceType pieceType) {
  int score = 0;

  switch(pieceType) {
    case PAWN: score = 1;
      break;
    case KNIGHT: score = 3;
      break;
    case BISHOP: score = 3;
      break;
    case ROOK: score = 5;
      break;
    case QUEEN: score = 9;
      break;
    default: break;
  }

  return score;
}

int Search::_negaMax(const Board& board, int depth, int alpha, int beta) {
  int alphaOrig = alpha;

  ZKey zKey = board.getZKey();
  // Check transposition table cache
  if (_tt.contains(zKey) && (_tt.getDepth(zKey) >= depth)) {
    switch(_tt.getFlag(zKey)) {
      case TranspTable::EXACT:
        return _tt.getScore(zKey);
      case TranspTable::UPPER_BOUND:
        if (_tt.getScore(zKey) <= alpha) {
          return alpha;
        }
        break;
      case TranspTable::LOWER_BOUND:
        if (_tt.getScore(zKey) >= beta) {
          return beta;
        }
        break;
    }
  }

  // Transposition table lookups are inconclusive, generate moves and recurse
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate and stalemate
  if (legalMoves.size() == 0) {
    int score = board.colorIsInCheck(board.getActivePlayer()) ? -INF : 0; // -INF = checkmate, 0 = stalemate (draw)

    _tt.set(board.getZKey(), score, depth, TranspTable::EXACT);
    return score;
  }

  // Eval if depth is 0
  if (depth == 0) {
    int score = _qSearch(board, alpha, beta);
    _tt.set(board.getZKey(), score, 0, TranspTable::EXACT);
    return score;
  }

  _orderMoves(board, legalMoves);

  int bestScore = -INF;
  Board movedBoard;
  for (auto move : legalMoves) {
    movedBoard = board;
    movedBoard.doMove(move);

    bestScore = std::max(bestScore, -_negaMax(movedBoard, depth-1, -beta, -alpha));

    alpha = std::max(alpha, bestScore);
    if (alpha > beta) {
      break;
    }
  }

  // Store bestScore in transposition table
  TranspTable::Flag flag;
  if (bestScore < alphaOrig) {
    flag = TranspTable::UPPER_BOUND;
  } else if (bestScore >= beta) {
    flag = TranspTable::LOWER_BOUND;
  } else {
    flag = TranspTable::EXACT;
  }
  _tt.set(zKey, bestScore, depth, flag);

  return bestScore;
}

int Search::_qSearch(const Board& board, int alpha, int beta) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate / stalemate
  if (legalMoves.size() == 0) {
    if (board.colorIsInCheck(board.getActivePlayer())) { // Checkmate
      return -INF;
    } else { // Stalemate
      return 0;
    }
  }

  _orderMovesQSearch(legalMoves);
  int standPat = Eval(board, board.getActivePlayer()).getScore();

  // If node is quiet, just return eval
  if (!(legalMoves.at(0).getFlags() & Move::CAPTURE)) {
    return standPat;
  }

  if (standPat >= beta) {
    return beta;
  }
  if (alpha < standPat) {
    alpha = standPat;
  }

  Board movedBoard;
  for (auto move : legalMoves) {
    if ((move.getFlags() & Move::CAPTURE) == 0) {
      break;
    }

    movedBoard = board;
    movedBoard.doMove(move);

    int score = -_qSearch(movedBoard, -beta, -alpha);

    if (score >= beta) {
      return beta;
    }
    if (score > alpha) {
      alpha = score;
    }
  }
  return alpha;
}
<commit_msg>Fix incorrect tt move ordering<commit_after>#include "defs.h"
#include "search.h"
#include "eval.h"
#include "movegen.h"
#include "transptable.h"
#include <string>
#include <algorithm>
#include <time.h>
#include <iostream>
#include <functional>

Search::Search(const Board& board, bool logUci) {
  _logUci = logUci;
  _board = board;
  _bestScore = 0;
}

void Search::perform(int depth) {
  _rootMax(_board, depth);

  MoveList pv = _getPv(_board, depth);
  if (_logUci) {
    _logUciInfo(pv, depth, _bestMove, _bestScore);
  }
}

void Search::_logUciInfo(const MoveList& pv, int depth, Move bestMove, int bestScore) {
  std::string pvString;
  for(auto move : pv) {
    pvString += move.getNotation() + " ";
  }

  std::string scoreString;
  if (bestScore == INF) {
    scoreString = "mate " + std::to_string(pv.size());
  } else if (_bestScore == -INF) {
    scoreString = "mate -" + std::to_string(pv.size());
  } else {
    scoreString = "cp " + std::to_string(bestScore);
  }

  std::cout << "info depth " + std::to_string(depth) + " ";
  std::cout << "score " + scoreString + " ";
  std::cout << "pv " + pvString;
  std::cout << std::endl;
}

MoveList Search::_getPv(const Board& board, int depth) {
  int bestScore = INF;
  Move bestMove;
  Board bestBoard;

  bool foundBest = false;
  Board movedBoard;

  for (auto move : MoveGen(board).getLegalMoves()) {
    movedBoard = board;
    movedBoard.doMove(move);

    ZKey movedZKey = movedBoard.getZKey();

    // Due to the way negamax works, bestScore is the lowest score
    if (_tt.contains(movedZKey) && _tt.getFlag(movedZKey) == TranspTable::EXACT && _tt.getScore(movedZKey) < bestScore) {
      foundBest = true;
      bestScore = _tt.getScore(movedZKey);
      bestMove = move;
      bestBoard = movedBoard;
    }
  }

  if (!foundBest || depth == 0) {
    return MoveList();
  } else {
    MoveList pvList = _getPv(bestBoard, depth-1);
    pvList.insert(pvList.begin(), bestMove);
    return pvList;
  }
}

Move Search::getBestMove() {
  return _bestMove;
}

int Search::getBestScore() {
  return _bestScore;
}

void Search::_rootMax(const Board& board, int depth) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();
  _orderMoves(board, legalMoves);

  int bestScore = -INF;
  int currScore;

  Move bestMove;
  Board movedBoard;
  for (auto move : legalMoves) {
    movedBoard = board;
    movedBoard.doMove(move);

    currScore = -_negaMax(movedBoard, depth-1, -INF, -bestScore);

    if (currScore > bestScore) {
      bestMove = move;
      bestScore = currScore;

      // Break if we've found a checkmate
      if (bestScore == INF) {
        break;
      }
    }
  }

  // If we couldn't find a path other than checkmate, just pick the first legal move
  if (bestMove.getFlags() & Move::NULL_MOVE) {
    bestMove = legalMoves.at(0);
  }

  _tt.set(board.getZKey(), bestScore, depth, TranspTable::EXACT);

  _bestMove = bestMove;
  _bestScore = bestScore;
}

void Search::_orderMoves(const Board& board, MoveList& moveList) {
  // Order moves by tt score
  std::sort(moveList.begin(), moveList.end(), std::bind(&Search::_compareMovesTt, this, board, std::placeholders::_1, std::placeholders::_2));

  // Ending index of sorted section of moveList
  unsigned int i;

  // Order captures by MVV/LVA
  Board movedBoard;
  for (i=0;i<moveList.size();i++) {
    movedBoard = board;
    movedBoard.doMove(moveList.at(i));

    if (!_tt.contains(movedBoard.getZKey())) {
      break;
    }
  }

  std::sort(moveList.begin() + i, moveList.end(), std::bind(&Search::_compareMovesMvvLva, this, std::placeholders::_1, std::placeholders::_2));

  // Order promotions by promotion value
  for(;i<moveList.size();i++) {
    if (!(moveList.at(i).getFlags() & Move::CAPTURE)) {
      break;
    }
  }
  std::sort(moveList.begin() + i, moveList.end(), std::bind(&Search::_compareMovesPromotionValue, this, std::placeholders::_1, std::placeholders::_2));
}

void Search::_orderMovesQSearch(MoveList& moveList) {
  std::sort(moveList.begin(), moveList.end(), std::bind(&Search::_compareMovesMvvLva, this, std::placeholders::_1, std::placeholders::_2));
}

bool Search::_compareMovesTt(Board board, Move a, Move b) {
  Board aBoard = board;
  Board bBoard = board;

  aBoard.doMove(a);
  bBoard.doMove(b);

  int aScore = _tt.contains(aBoard.getZKey()) ? _tt.getScore(aBoard.getZKey()) : -INF;
  int bScore = _tt.contains(bBoard.getZKey()) ? _tt.getScore(bBoard.getZKey()) : -INF;

  return aScore > bScore;
}

bool Search::_compareMovesMvvLva(Move a, Move b) {
  bool aIsCapture = a.getFlags() & Move::CAPTURE;
  bool bIsCapture = b.getFlags() & Move::CAPTURE;

  if (aIsCapture && !bIsCapture) {
    return true;
  } else if (bIsCapture && !aIsCapture) {
    return false;
  } else { // Both captures
    int aCaptureValue = _getPieceValue(a.getCapturedPieceType());
    int bCaptureValue = _getPieceValue(b.getCapturedPieceType());

    if (aCaptureValue != bCaptureValue) {
      return aCaptureValue > bCaptureValue;
    } else { // Captured piece type value is the same, order by attacker value
      int aPieceValue = _getPieceValue(a.getPieceType());
      int bPieceValue = _getPieceValue(b.getPieceType());

      return aPieceValue < bPieceValue;
    }
  }
}

bool Search::_compareMovesPromotionValue(Move a, Move b) {
  bool aIsPromotion = a.getFlags() & Move::PROMOTION;
  bool bIsPromotion = b.getFlags() & Move::PROMOTION;

  if (aIsPromotion && !bIsPromotion) {
    return true;
  } else if (bIsPromotion && !aIsPromotion) {
    return false;
  } else { // Both promotions
    int aPromotionValue = _getPieceValue(a.getPromotionPieceType());
    int bPromotionValue = _getPieceValue(b.getPromotionPieceType());

    return aPromotionValue < bPromotionValue;
  }
}

int Search::_getPieceValue(PieceType pieceType) {
  int score = 0;

  switch(pieceType) {
    case PAWN: score = 1;
      break;
    case KNIGHT: score = 3;
      break;
    case BISHOP: score = 3;
      break;
    case ROOK: score = 5;
      break;
    case QUEEN: score = 9;
      break;
    default: break;
  }

  return score;
}

int Search::_negaMax(const Board& board, int depth, int alpha, int beta) {
  int alphaOrig = alpha;

  ZKey zKey = board.getZKey();
  // Check transposition table cache
  if (_tt.contains(zKey) && (_tt.getDepth(zKey) >= depth)) {
    switch(_tt.getFlag(zKey)) {
      case TranspTable::EXACT:
        return _tt.getScore(zKey);
      case TranspTable::UPPER_BOUND:
        if (_tt.getScore(zKey) <= alpha) {
          return alpha;
        }
        break;
      case TranspTable::LOWER_BOUND:
        if (_tt.getScore(zKey) >= beta) {
          return beta;
        }
        break;
    }
  }

  // Transposition table lookups are inconclusive, generate moves and recurse
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate and stalemate
  if (legalMoves.size() == 0) {
    int score = board.colorIsInCheck(board.getActivePlayer()) ? -INF : 0; // -INF = checkmate, 0 = stalemate (draw)

    _tt.set(board.getZKey(), score, depth, TranspTable::EXACT);
    return score;
  }

  // Eval if depth is 0
  if (depth == 0) {
    int score = _qSearch(board, alpha, beta);
    _tt.set(board.getZKey(), score, 0, TranspTable::EXACT);
    return score;
  }

  _orderMoves(board, legalMoves);

  int bestScore = -INF;
  Board movedBoard;
  for (auto move : legalMoves) {
    movedBoard = board;
    movedBoard.doMove(move);

    bestScore = std::max(bestScore, -_negaMax(movedBoard, depth-1, -beta, -alpha));

    alpha = std::max(alpha, bestScore);
    if (alpha > beta) {
      break;
    }
  }

  // Store bestScore in transposition table
  TranspTable::Flag flag;
  if (bestScore < alphaOrig) {
    flag = TranspTable::UPPER_BOUND;
  } else if (bestScore >= beta) {
    flag = TranspTable::LOWER_BOUND;
  } else {
    flag = TranspTable::EXACT;
  }
  _tt.set(zKey, bestScore, depth, flag);

  return bestScore;
}

int Search::_qSearch(const Board& board, int alpha, int beta) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate / stalemate
  if (legalMoves.size() == 0) {
    if (board.colorIsInCheck(board.getActivePlayer())) { // Checkmate
      return -INF;
    } else { // Stalemate
      return 0;
    }
  }

  _orderMovesQSearch(legalMoves);
  int standPat = Eval(board, board.getActivePlayer()).getScore();

  // If node is quiet, just return eval
  if (!(legalMoves.at(0).getFlags() & Move::CAPTURE)) {
    return standPat;
  }

  if (standPat >= beta) {
    return beta;
  }
  if (alpha < standPat) {
    alpha = standPat;
  }

  Board movedBoard;
  for (auto move : legalMoves) {
    if ((move.getFlags() & Move::CAPTURE) == 0) {
      break;
    }

    movedBoard = board;
    movedBoard.doMove(move);

    int score = -_qSearch(movedBoard, -beta, -alpha);

    if (score >= beta) {
      return beta;
    }
    if (score > alpha) {
      alpha = score;
    }
  }
  return alpha;
}
<|endoftext|>


<commit_before>#include "defs.h"
#include "search.h"
#include "eval.h"
#include "movegen.h"
#include "transptable.h"
#include "movepicker.h"
#include "generalmovepicker.h"
#include "capturemovepicker.h"
#include <string>
#include <algorithm>
#include <time.h>
#include <iostream>

Search::Search(const Board& board, bool logUci) : _orderingInfo(OrderingInfo(const_cast<TranspTable*>(&_tt))) {
  _logUci = logUci;
  _board = board;
  _bestScore = 0;
  _orderingInfo = OrderingInfo(const_cast<TranspTable*>(&_tt));
}

void Search::perform(int depth) {
  _rootMax(_board, depth);

  if (_logUci) {
    _logUciInfo(_getPv(depth), depth, _bestMove, _bestScore, _nodes);
  }
}

MoveList Search::_getPv(int length) {
  MoveList pv;
  Board currBoard = _board;
  const TranspTableEntry* currEntry;
  int currLength = 0;

  while (currLength++ < length && (currEntry = _tt.getEntry(currBoard.getZKey()))) {
    if (currEntry->getFlag() == TranspTableEntry::EXACT) {
      pv.push_back(currEntry->getBestMove());
      currBoard.doMove(currEntry->getBestMove());
    } else {
      break;
    }
  }

  return pv;
}

void Search::_logUciInfo(const MoveList& pv, int depth, Move bestMove, int bestScore, int nodes) {
  std::string pvString;
  for(auto move : pv) {
    pvString += move.getNotation() + " ";
  }

  std::string scoreString;
  if (bestScore == INF) {
    scoreString = "mate " + std::to_string(pv.size());
  } else if (_bestScore == -INF) {
    scoreString = "mate -" + std::to_string(pv.size());
  } else {
    scoreString = "cp " + std::to_string(bestScore);
  }

  std::cout << "info depth " + std::to_string(depth) + " ";
  std::cout << "nodes " + std::to_string(nodes) + " ";
  std::cout << "score " + scoreString + " ";
  std::cout << "pv " + pvString;
  std::cout << std::endl;
}

Move Search::getBestMove() {
  return _bestMove;
}

int Search::getBestScore() {
  return _bestScore;
}

void Search::_rootMax(const Board& board, int depth) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();
  _nodes = 0;

  // If no legal moves are avaliable, just return, setting bestmove to a null move
  if (legalMoves.size() == 0) {
    _bestMove = Move();
    _bestScore = -INF;
    return;
  }

  GeneralMovePicker movePicker(const_cast<OrderingInfo*>(&_orderingInfo), const_cast<Board*>(&board), const_cast<MoveList*>(&legalMoves));

  int alpha = -INF;
  int beta = INF;

  int currScore;

  Move bestMove;
  Board movedBoard;
  while (movePicker.hasNext()) {
    Move move = movePicker.getNext();
    movedBoard = board;
    movedBoard.doMove(move);

    _orderingInfo.incrementPly();
    currScore = -_negaMax(movedBoard, depth-1, -beta, -alpha);
    _orderingInfo.deincrementPly();

    // If the current score is better than alpha, or this is the first move in the loop
    if (currScore > alpha || (alpha == -INF)) {
      bestMove = move;
      alpha = currScore;

      // Break if we've found a checkmate
      if (currScore == INF) {
        break;
      }
    }
  }

  TranspTableEntry ttEntry(alpha, depth, TranspTableEntry::EXACT, bestMove);
  _tt.set(board.getZKey(), ttEntry);

  _bestMove = bestMove;
  _bestScore = alpha;
}

int Search::_negaMax(const Board& board, int depth, int alpha, int beta) {
  int alphaOrig = alpha;

  const TranspTableEntry* ttEntry = _tt.getEntry(board.getZKey());
  // Check transposition table cache
  if (ttEntry && (ttEntry->getDepth() >= depth)) {
    switch(ttEntry->getFlag()) {
      case TranspTable::EXACT:
        return ttEntry->getScore();
      case TranspTable::UPPER_BOUND:
        beta = std::min(beta, ttEntry->getScore());
        break;
      case TranspTable::LOWER_BOUND:
        alpha = std::max(alpha, ttEntry->getScore());
        break;
    }

    if (alpha >= beta) {
      return ttEntry->getScore();
    }
  }

  // Transposition table lookups are inconclusive, generate moves and recurse
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate and stalemate
  if (legalMoves.size() == 0) {
    int score = board.colorIsInCheck(board.getActivePlayer()) ? -INF : 0; // -INF = checkmate, 0 = stalemate (draw)
    return score;
  }

  // Eval if depth is 0
  if (depth == 0) {
    return _qSearch(board, alpha, beta);
  }

  GeneralMovePicker movePicker(const_cast<OrderingInfo*>(&_orderingInfo), const_cast<Board*>(&board), const_cast<MoveList*>(&legalMoves));

  Move bestMove;
  Board movedBoard;
  while (movePicker.hasNext()) {
    Move move = movePicker.getNext();

    movedBoard = board;
    movedBoard.doMove(move);

    _orderingInfo.incrementPly();
    int score = -_negaMax(movedBoard, depth-1, -beta, -alpha);
    _orderingInfo.deincrementPly();

    // Beta cutoff
    if (score >= beta) {
      // Add this move as a new killer move and update history if move is quiet
      _orderingInfo.updateKillers(_orderingInfo.getPly(), move);
      if (!(move.getFlags() & Move::CAPTURE)) {
        _orderingInfo.incrementHistory(_board.getActivePlayer(), move.getFrom(), move.getTo(), depth);
      }

      // Add a new tt entry for this node
      TranspTableEntry newTTEntry(score, depth, TranspTableEntry::LOWER_BOUND, move);
      _tt.set(board.getZKey(), newTTEntry);
      return beta;
    }

    // Check if alpha raised (new best move)
    if (score > alpha) {
      alpha = score;
      bestMove = move;
    }
  }

  // Store bestScore in transposition table
  TranspTableEntry::Flag flag;
  if (alpha <= alphaOrig) {
    flag = TranspTableEntry::UPPER_BOUND;
  } else {
    flag = TranspTableEntry::EXACT;
  }
  TranspTableEntry newTTEntry(alpha, depth, flag, bestMove);
  _tt.set(board.getZKey(), newTTEntry);

  return alpha;
}

int Search::_qSearch(const Board& board, int alpha, int beta) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate / stalemate
  if (legalMoves.size() == 0) {
    if (board.colorIsInCheck(board.getActivePlayer())) { // Checkmate
      return -INF;
    } else { // Stalemate
      return 0;
    }
  }

  int standPat = Eval(board, board.getActivePlayer()).getScore();
  _nodes ++;

  CaptureMovePicker movePicker(const_cast<MoveList*>(&legalMoves));

  // If node is quiet, just return eval
  if (!movePicker.hasNext()) {
    return standPat;
  }

  if (standPat >= beta) {
    return beta;
  }
  if (alpha < standPat) {
    alpha = standPat;
  }

  Board movedBoard;
  while (movePicker.hasNext()) {
    Move move = movePicker.getNext();

    movedBoard = board;
    movedBoard.doMove(move);

    int score = -_qSearch(movedBoard, -beta, -alpha);

    if (score >= beta) {
      return beta;
    }
    if (score > alpha) {
      alpha = score;
    }
  }
  return alpha;
}
<commit_msg>Fix always displaying a one move PV on mate<commit_after>#include "defs.h"
#include "search.h"
#include "eval.h"
#include "movegen.h"
#include "transptable.h"
#include "movepicker.h"
#include "generalmovepicker.h"
#include "capturemovepicker.h"
#include <string>
#include <algorithm>
#include <time.h>
#include <iostream>

Search::Search(const Board& board, bool logUci) : _orderingInfo(OrderingInfo(const_cast<TranspTable*>(&_tt))) {
  _logUci = logUci;
  _board = board;
  _bestScore = 0;
  _orderingInfo = OrderingInfo(const_cast<TranspTable*>(&_tt));
}

void Search::perform(int depth) {
  _rootMax(_board, depth);

  if (_logUci) {
    _logUciInfo(_getPv(depth), depth, _bestMove, _bestScore, _nodes);
  }
}

MoveList Search::_getPv(int length) {
  MoveList pv;
  Board currBoard = _board;
  const TranspTableEntry* currEntry;
  int currLength = 0;

  while (currLength++ < length && (currEntry = _tt.getEntry(currBoard.getZKey()))) {
    pv.push_back(currEntry->getBestMove());
    currBoard.doMove(currEntry->getBestMove());
  }

  return pv;
}

void Search::_logUciInfo(const MoveList& pv, int depth, Move bestMove, int bestScore, int nodes) {
  std::string pvString;
  for(auto move : pv) {
    pvString += move.getNotation() + " ";
  }

  std::string scoreString;
  if (bestScore == INF) {
    scoreString = "mate " + std::to_string(pv.size());
  } else if (_bestScore == -INF) {
    scoreString = "mate -" + std::to_string(pv.size());
  } else {
    scoreString = "cp " + std::to_string(bestScore);
  }

  std::cout << "info depth " + std::to_string(depth) + " ";
  std::cout << "nodes " + std::to_string(nodes) + " ";
  std::cout << "score " + scoreString + " ";
  std::cout << "pv " + pvString;
  std::cout << std::endl;
}

Move Search::getBestMove() {
  return _bestMove;
}

int Search::getBestScore() {
  return _bestScore;
}

void Search::_rootMax(const Board& board, int depth) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();
  _nodes = 0;

  // If no legal moves are avaliable, just return, setting bestmove to a null move
  if (legalMoves.size() == 0) {
    _bestMove = Move();
    _bestScore = -INF;
    return;
  }

  GeneralMovePicker movePicker(const_cast<OrderingInfo*>(&_orderingInfo), const_cast<Board*>(&board), const_cast<MoveList*>(&legalMoves));

  int alpha = -INF;
  int beta = INF;

  int currScore;

  Move bestMove;
  Board movedBoard;
  bool first = true;
  while (movePicker.hasNext()) {
    Move move = movePicker.getNext();
    movedBoard = board;
    movedBoard.doMove(move);

    _orderingInfo.incrementPly();
    currScore = -_negaMax(movedBoard, depth-1, -beta, -alpha);
    _orderingInfo.deincrementPly();

    // If the current score is better than alpha, or this is the first move in the loop
    if (currScore > alpha || (first)) {
      bestMove = move;
      alpha = currScore;

      // Break if we've found a checkmate
      if (currScore == INF) {
        break;
      }
    }

    first = false;
  }

  TranspTableEntry ttEntry(alpha, depth, TranspTableEntry::EXACT, bestMove);
  _tt.set(board.getZKey(), ttEntry);

  _bestMove = bestMove;
  _bestScore = alpha;
}

int Search::_negaMax(const Board& board, int depth, int alpha, int beta) {
  int alphaOrig = alpha;

  const TranspTableEntry* ttEntry = _tt.getEntry(board.getZKey());
  // Check transposition table cache
  if (ttEntry && (ttEntry->getDepth() >= depth)) {
    switch(ttEntry->getFlag()) {
      case TranspTable::EXACT:
        return ttEntry->getScore();
      case TranspTable::UPPER_BOUND:
        beta = std::min(beta, ttEntry->getScore());
        break;
      case TranspTable::LOWER_BOUND:
        alpha = std::max(alpha, ttEntry->getScore());
        break;
    }

    if (alpha >= beta) {
      return ttEntry->getScore();
    }
  }

  // Transposition table lookups are inconclusive, generate moves and recurse
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate and stalemate
  if (legalMoves.size() == 0) {
    int score = board.colorIsInCheck(board.getActivePlayer()) ? -INF : 0; // -INF = checkmate, 0 = stalemate (draw)
    return score;
  }

  // Eval if depth is 0
  if (depth == 0) {
    return _qSearch(board, alpha, beta);
  }

  GeneralMovePicker movePicker(const_cast<OrderingInfo*>(&_orderingInfo), const_cast<Board*>(&board), const_cast<MoveList*>(&legalMoves));

  Move bestMove;
  Board movedBoard;
  while (movePicker.hasNext()) {
    Move move = movePicker.getNext();

    movedBoard = board;
    movedBoard.doMove(move);

    _orderingInfo.incrementPly();
    int score = -_negaMax(movedBoard, depth-1, -beta, -alpha);
    _orderingInfo.deincrementPly();

    // Beta cutoff
    if (score >= beta) {
      // Add this move as a new killer move and update history if move is quiet
      _orderingInfo.updateKillers(_orderingInfo.getPly(), move);
      if (!(move.getFlags() & Move::CAPTURE)) {
        _orderingInfo.incrementHistory(_board.getActivePlayer(), move.getFrom(), move.getTo(), depth);
      }

      // Add a new tt entry for this node
      TranspTableEntry newTTEntry(score, depth, TranspTableEntry::LOWER_BOUND, move);
      _tt.set(board.getZKey(), newTTEntry);
      return beta;
    }

    // Check if alpha raised (new best move)
    if (score > alpha) {
      alpha = score;
      bestMove = move;
    }
  }

  // If the best move was not set in the main search loop
  // alpha was not raised at any point, just pick the first move
  // avaliable (arbitrary) to avoid putting a null move in the
  // transposition table
  if (bestMove.getFlags() & Move::NULL_MOVE) {
    bestMove = legalMoves.at(0);
  }

  // Store bestScore in transposition table
  TranspTableEntry::Flag flag;
  if (alpha <= alphaOrig) {
    flag = TranspTableEntry::UPPER_BOUND;
  } else {
    flag = TranspTableEntry::EXACT;
  }
  TranspTableEntry newTTEntry(alpha, depth, flag, bestMove);
  _tt.set(board.getZKey(), newTTEntry);

  return alpha;
}

int Search::_qSearch(const Board& board, int alpha, int beta) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate / stalemate
  if (legalMoves.size() == 0) {
    if (board.colorIsInCheck(board.getActivePlayer())) { // Checkmate
      return -INF;
    } else { // Stalemate
      return 0;
    }
  }

  int standPat = Eval(board, board.getActivePlayer()).getScore();
  _nodes ++;

  CaptureMovePicker movePicker(const_cast<MoveList*>(&legalMoves));

  // If node is quiet, just return eval
  if (!movePicker.hasNext()) {
    return standPat;
  }

  if (standPat >= beta) {
    return beta;
  }
  if (alpha < standPat) {
    alpha = standPat;
  }

  Board movedBoard;
  while (movePicker.hasNext()) {
    Move move = movePicker.getNext();

    movedBoard = board;
    movedBoard.doMove(move);

    int score = -_qSearch(movedBoard, -beta, -alpha);

    if (score >= beta) {
      return beta;
    }
    if (score > alpha) {
      alpha = score;
    }
  }
  return alpha;
}
<|endoftext|>


<commit_before>#include "defs.h"
#include "search.h"
#include "eval.h"
#include "movegen.h"
#include "transptable.h"
#include <string>
#include <algorithm>
#include <time.h>
#include <iostream>
#include <functional>

Search::Search(const Board& board, bool logUci) {
  _logUci = logUci;
  _board = board;
  _bestScore = 0;
}

void Search::perform(int depth) {
  _rootMax(_board, depth);

  MoveList pv = _getPv(_board, depth);
  if (_logUci) {
    _logUciInfo(pv, depth, _bestMove, _bestScore);
  }
}

void Search::_logUciInfo(const MoveList& pv, int depth, Move bestMove, int bestScore) {
  std::string pvString;
  for(auto move : pv) {
    pvString += move.getNotation() + " ";
  }

  std::string scoreString;
  if (bestScore == INF) {
    scoreString = "mate " + std::to_string(pv.size());
  } else if (_bestScore == -INF) {
    scoreString = "mate -" + std::to_string(pv.size());
  } else {
    scoreString = "cp " + std::to_string(bestScore);
  }

  std::cout << "info depth " + std::to_string(depth) + " ";
  std::cout << "score " + scoreString + " ";
  std::cout << "pv " + pvString;
  std::cout << std::endl;
}

MoveList Search::_getPv(const Board& board, int depth) {
  int bestScore = INF;
  Move bestMove;
  Board bestBoard;

  bool foundBest = false;
  Board movedBoard;

  for (auto move : MoveGen(board).getLegalMoves()) {
    movedBoard = board;
    movedBoard.doMove(move);

    ZKey movedZKey = movedBoard.getZKey();

    // Due to the way negamax works, bestScore is the lowest score
    if (_tt.contains(movedZKey) && _tt.getFlag(movedZKey) == TranspTable::EXACT && _tt.getScore(movedZKey) < bestScore) {
      foundBest = true;
      bestScore = _tt.getScore(movedZKey);
      bestMove = move;
      bestBoard = movedBoard;
    }
  }

  if (!foundBest || depth == 0) {
    return MoveList();
  } else {
    MoveList pvList = _getPv(bestBoard, depth-1);
    pvList.insert(pvList.begin(), bestMove);
    return pvList;
  }
}

Move Search::getBestMove() {
  return _bestMove;
}

int Search::getBestScore() {
  return _bestScore;
}

void Search::_rootMax(const Board& board, int depth) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();
  _orderMoves(board, legalMoves);

  int bestScore = -INF;
  int currScore;

  Move bestMove;
  Board movedBoard;
  for (auto move : legalMoves) {
    movedBoard = board;
    movedBoard.doMove(move);

    currScore = -_negaMax(movedBoard, depth-1, -INF, -bestScore);

    if (currScore > bestScore) {
      bestMove = move;
      bestScore = currScore;

      // Break if we've found a checkmate
      if (bestScore == INF) {
        break;
      }
    }
  }

  // If we couldn't find a path other than checkmate, just pick the first legal move
  if (bestMove.getFlags() & Move::NULL_MOVE) {
    bestMove = legalMoves.at(0);
  }

  _tt.set(board.getZKey(), bestScore, depth, TranspTable::EXACT);

  _bestMove = bestMove;
  _bestScore = bestScore;
}

void Search::_orderMoves(const Board& board, MoveList& moveList) {
  // Order moves by tt score
  std::sort(moveList.begin(), moveList.end(), std::bind(&Search::_compareMovesTt, this, board, std::placeholders::_1, std::placeholders::_2));

  // Ending index of sorted section of moveList
  unsigned int i;

  // Order captures by MVV/LVA
  Board movedBoard;
  for (i=0;i<moveList.size();i++) {
    movedBoard = board;
    movedBoard.doMove(moveList.at(i));

    if (!_tt.contains(movedBoard.getZKey())) {
      break;
    }
  }

  std::sort(moveList.begin() + i, moveList.end(), std::bind(&Search::_compareMovesMvvLva, this, std::placeholders::_1, std::placeholders::_2));

  // Order promotions by promotion value
  for(;i<moveList.size();i++) {
    if (!(moveList.at(i).getFlags() & Move::CAPTURE)) {
      break;
    }
  }
  std::sort(moveList.begin() + i, moveList.end(), std::bind(&Search::_compareMovesPromotionValue, this, std::placeholders::_1, std::placeholders::_2));
}

void Search::_orderMovesQSearch(MoveList& moveList) {
  std::sort(moveList.begin(), moveList.end(), std::bind(&Search::_compareMovesMvvLva, this, std::placeholders::_1, std::placeholders::_2));
}

bool Search::_compareMovesTt(Board board, Move a, Move b) {
  Board aBoard = board;
  Board bBoard = board;

  aBoard.doMove(a);
  bBoard.doMove(b);

  int aScore = _tt.contains(aBoard.getZKey()) ? _tt.getScore(aBoard.getZKey()) : -INF;
  int bScore = _tt.contains(bBoard.getZKey()) ? _tt.getScore(bBoard.getZKey()) : -INF;

  return aScore < bScore;
}

bool Search::_compareMovesMvvLva(Move a, Move b) {
  bool aIsCapture = a.getFlags() & Move::CAPTURE;
  bool bIsCapture = b.getFlags() & Move::CAPTURE;

  if (aIsCapture && !bIsCapture) {
    return true;
  } else if (bIsCapture && !aIsCapture) {
    return false;
  } else { // Both captures
    int aCaptureValue = _getPieceValue(a.getCapturedPieceType());
    int bCaptureValue = _getPieceValue(b.getCapturedPieceType());

    if (aCaptureValue != bCaptureValue) {
      return aCaptureValue > bCaptureValue;
    } else { // Captured piece type value is the same, order by attacker value
      int aPieceValue = _getPieceValue(a.getPieceType());
      int bPieceValue = _getPieceValue(b.getPieceType());

      return aPieceValue < bPieceValue;
    }
  }
}

bool Search::_compareMovesPromotionValue(Move a, Move b) {
  bool aIsPromotion = a.getFlags() & Move::PROMOTION;
  bool bIsPromotion = b.getFlags() & Move::PROMOTION;

  if (aIsPromotion && !bIsPromotion) {
    return true;
  } else if (bIsPromotion && !aIsPromotion) {
    return false;
  } else { // Both promotions
    int aPromotionValue = _getPieceValue(a.getPromotionPieceType());
    int bPromotionValue = _getPieceValue(b.getPromotionPieceType());

    return aPromotionValue < bPromotionValue;
  }
}

int Search::_getPieceValue(PieceType pieceType) {
  int score = 0;

  switch(pieceType) {
    case PAWN: score = 1;
      break;
    case KNIGHT: score = 3;
      break;
    case BISHOP: score = 3;
      break;
    case ROOK: score = 5;
      break;
    case QUEEN: score = 9;
      break;
    default: break;
  }

  return score;
}

int Search::_negaMax(const Board& board, int depth, int alpha, int beta) {
  int alphaOrig = alpha;

  ZKey zKey = board.getZKey();
  // Check transposition table cache
  if (_tt.contains(zKey) && (_tt.getDepth(zKey) >= depth)) {
    switch(_tt.getFlag(zKey)) {
      case TranspTable::EXACT:
        return _tt.getScore(zKey);
      case TranspTable::UPPER_BOUND:
        alpha = std::max(alpha, _tt.getScore(zKey));
        break;
      case TranspTable::LOWER_BOUND:
        beta = std::min(beta, _tt.getScore(zKey));
        break;
    }

    if (alpha > beta) {
      return _tt.getScore(zKey);
    }
  }

  // Transposition table lookups are inconclusive, generate moves and recurse
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate and stalemate
  if (legalMoves.size() == 0) {
    int score = board.colorIsInCheck(board.getActivePlayer()) ? -INF : 0; // -INF = checkmate, 0 = stalemate (draw)

    _tt.set(board.getZKey(), score, depth, TranspTable::EXACT);
    return score;
  }

  // Eval if depth is 0
  if (depth == 0) {
    int score = _qSearch(board, alpha, beta);
    _tt.set(board.getZKey(), score, 0, TranspTable::EXACT);
    return score;
  }

  _orderMoves(board, legalMoves);

  int bestScore = -INF;
  Board movedBoard;
  for (auto move : legalMoves) {
    movedBoard = board;
    movedBoard.doMove(move);

    bestScore = std::max(bestScore, -_negaMax(movedBoard, depth-1, -beta, -alpha));

    alpha = std::max(alpha, bestScore);
    if (alpha > beta) {
      break;
    }
  }

  // Store bestScore in transposition table
  TranspTable::Flag flag;
  if (bestScore < alphaOrig) {
    flag = TranspTable::UPPER_BOUND;
  } else if (bestScore >= beta) {
    flag = TranspTable::LOWER_BOUND;
  } else {
    flag = TranspTable::EXACT;
  }
  _tt.set(zKey, bestScore, depth, flag);

  return bestScore;
}

int Search::_qSearch(const Board& board, int alpha, int beta) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate / stalemate
  if (legalMoves.size() == 0) {
    if (board.colorIsInCheck(board.getActivePlayer())) { // Checkmate
      return -INF;
    } else { // Stalemate
      return 0;
    }
  }

  _orderMovesQSearch(legalMoves);
  int standPat = Eval(board, board.getActivePlayer()).getScore();

  // If node is quiet, just return eval
  if (!(legalMoves.at(0).getFlags() & Move::CAPTURE)) {
    return standPat;
  }

  if (standPat >= beta) {
    return beta;
  }
  if (alpha < standPat) {
    alpha = standPat;
  }

  Board movedBoard;
  for (auto move : legalMoves) {
    if ((move.getFlags() & Move::CAPTURE) == 0) {
      break;
    }

    movedBoard = board;
    movedBoard.doMove(move);

    int score = -_qSearch(movedBoard, -beta, -alpha);

    if (score >= beta) {
      return beta;
    }
    if (score > alpha) {
      alpha = score;
    }
  }
  return alpha;
}
<commit_msg>Fix incorrect return values from negamax due to tt lookups<commit_after>#include "defs.h"
#include "search.h"
#include "eval.h"
#include "movegen.h"
#include "transptable.h"
#include <string>
#include <algorithm>
#include <time.h>
#include <iostream>
#include <functional>

Search::Search(const Board& board, bool logUci) {
  _logUci = logUci;
  _board = board;
  _bestScore = 0;
}

void Search::perform(int depth) {
  _rootMax(_board, depth);

  MoveList pv = _getPv(_board, depth);
  if (_logUci) {
    _logUciInfo(pv, depth, _bestMove, _bestScore);
  }
}

void Search::_logUciInfo(const MoveList& pv, int depth, Move bestMove, int bestScore) {
  std::string pvString;
  for(auto move : pv) {
    pvString += move.getNotation() + " ";
  }

  std::string scoreString;
  if (bestScore == INF) {
    scoreString = "mate " + std::to_string(pv.size());
  } else if (_bestScore == -INF) {
    scoreString = "mate -" + std::to_string(pv.size());
  } else {
    scoreString = "cp " + std::to_string(bestScore);
  }

  std::cout << "info depth " + std::to_string(depth) + " ";
  std::cout << "score " + scoreString + " ";
  std::cout << "pv " + pvString;
  std::cout << std::endl;
}

MoveList Search::_getPv(const Board& board, int depth) {
  int bestScore = INF;
  Move bestMove;
  Board bestBoard;

  bool foundBest = false;
  Board movedBoard;

  for (auto move : MoveGen(board).getLegalMoves()) {
    movedBoard = board;
    movedBoard.doMove(move);

    ZKey movedZKey = movedBoard.getZKey();

    // Due to the way negamax works, bestScore is the lowest score
    if (_tt.contains(movedZKey) && _tt.getFlag(movedZKey) == TranspTable::EXACT && _tt.getScore(movedZKey) < bestScore) {
      foundBest = true;
      bestScore = _tt.getScore(movedZKey);
      bestMove = move;
      bestBoard = movedBoard;
    }
  }

  if (!foundBest || depth == 0) {
    return MoveList();
  } else {
    MoveList pvList = _getPv(bestBoard, depth-1);
    pvList.insert(pvList.begin(), bestMove);
    return pvList;
  }
}

Move Search::getBestMove() {
  return _bestMove;
}

int Search::getBestScore() {
  return _bestScore;
}

void Search::_rootMax(const Board& board, int depth) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();
  _orderMoves(board, legalMoves);

  int bestScore = -INF;
  int currScore;

  Move bestMove;
  Board movedBoard;
  for (auto move : legalMoves) {
    movedBoard = board;
    movedBoard.doMove(move);

    currScore = -_negaMax(movedBoard, depth-1, -INF, -bestScore);

    if (currScore > bestScore) {
      bestMove = move;
      bestScore = currScore;

      // Break if we've found a checkmate
      if (bestScore == INF) {
        break;
      }
    }
  }

  // If we couldn't find a path other than checkmate, just pick the first legal move
  if (bestMove.getFlags() & Move::NULL_MOVE) {
    bestMove = legalMoves.at(0);
  }

  _tt.set(board.getZKey(), bestScore, depth, TranspTable::EXACT);

  _bestMove = bestMove;
  _bestScore = bestScore;
}

void Search::_orderMoves(const Board& board, MoveList& moveList) {
  // Order moves by tt score
  std::sort(moveList.begin(), moveList.end(), std::bind(&Search::_compareMovesTt, this, board, std::placeholders::_1, std::placeholders::_2));

  // Ending index of sorted section of moveList
  unsigned int i;

  // Order captures by MVV/LVA
  Board movedBoard;
  for (i=0;i<moveList.size();i++) {
    movedBoard = board;
    movedBoard.doMove(moveList.at(i));

    if (!_tt.contains(movedBoard.getZKey())) {
      break;
    }
  }

  std::sort(moveList.begin() + i, moveList.end(), std::bind(&Search::_compareMovesMvvLva, this, std::placeholders::_1, std::placeholders::_2));

  // Order promotions by promotion value
  for(;i<moveList.size();i++) {
    if (!(moveList.at(i).getFlags() & Move::CAPTURE)) {
      break;
    }
  }
  std::sort(moveList.begin() + i, moveList.end(), std::bind(&Search::_compareMovesPromotionValue, this, std::placeholders::_1, std::placeholders::_2));
}

void Search::_orderMovesQSearch(MoveList& moveList) {
  std::sort(moveList.begin(), moveList.end(), std::bind(&Search::_compareMovesMvvLva, this, std::placeholders::_1, std::placeholders::_2));
}

bool Search::_compareMovesTt(Board board, Move a, Move b) {
  Board aBoard = board;
  Board bBoard = board;

  aBoard.doMove(a);
  bBoard.doMove(b);

  int aScore = _tt.contains(aBoard.getZKey()) ? _tt.getScore(aBoard.getZKey()) : -INF;
  int bScore = _tt.contains(bBoard.getZKey()) ? _tt.getScore(bBoard.getZKey()) : -INF;

  return aScore < bScore;
}

bool Search::_compareMovesMvvLva(Move a, Move b) {
  bool aIsCapture = a.getFlags() & Move::CAPTURE;
  bool bIsCapture = b.getFlags() & Move::CAPTURE;

  if (aIsCapture && !bIsCapture) {
    return true;
  } else if (bIsCapture && !aIsCapture) {
    return false;
  } else { // Both captures
    int aCaptureValue = _getPieceValue(a.getCapturedPieceType());
    int bCaptureValue = _getPieceValue(b.getCapturedPieceType());

    if (aCaptureValue != bCaptureValue) {
      return aCaptureValue > bCaptureValue;
    } else { // Captured piece type value is the same, order by attacker value
      int aPieceValue = _getPieceValue(a.getPieceType());
      int bPieceValue = _getPieceValue(b.getPieceType());

      return aPieceValue < bPieceValue;
    }
  }
}

bool Search::_compareMovesPromotionValue(Move a, Move b) {
  bool aIsPromotion = a.getFlags() & Move::PROMOTION;
  bool bIsPromotion = b.getFlags() & Move::PROMOTION;

  if (aIsPromotion && !bIsPromotion) {
    return true;
  } else if (bIsPromotion && !aIsPromotion) {
    return false;
  } else { // Both promotions
    int aPromotionValue = _getPieceValue(a.getPromotionPieceType());
    int bPromotionValue = _getPieceValue(b.getPromotionPieceType());

    return aPromotionValue < bPromotionValue;
  }
}

int Search::_getPieceValue(PieceType pieceType) {
  int score = 0;

  switch(pieceType) {
    case PAWN: score = 1;
      break;
    case KNIGHT: score = 3;
      break;
    case BISHOP: score = 3;
      break;
    case ROOK: score = 5;
      break;
    case QUEEN: score = 9;
      break;
    default: break;
  }

  return score;
}

int Search::_negaMax(const Board& board, int depth, int alpha, int beta) {
  int alphaOrig = alpha;

  ZKey zKey = board.getZKey();
  // Check transposition table cache
  if (_tt.contains(zKey) && (_tt.getDepth(zKey) >= depth)) {
    switch(_tt.getFlag(zKey)) {
      case TranspTable::EXACT:
        return _tt.getScore(zKey);
      case TranspTable::UPPER_BOUND:
        if (_tt.getScore(zKey) <= alpha) {
          return alpha;
        }
        break;
      case TranspTable::LOWER_BOUND:
        if (_tt.getScore(zKey) >= beta) {
          return beta;
        }
        break;
    }
  }

  // Transposition table lookups are inconclusive, generate moves and recurse
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate and stalemate
  if (legalMoves.size() == 0) {
    int score = board.colorIsInCheck(board.getActivePlayer()) ? -INF : 0; // -INF = checkmate, 0 = stalemate (draw)

    _tt.set(board.getZKey(), score, depth, TranspTable::EXACT);
    return score;
  }

  // Eval if depth is 0
  if (depth == 0) {
    int score = _qSearch(board, alpha, beta);
    _tt.set(board.getZKey(), score, 0, TranspTable::EXACT);
    return score;
  }

  _orderMoves(board, legalMoves);

  int bestScore = -INF;
  Board movedBoard;
  for (auto move : legalMoves) {
    movedBoard = board;
    movedBoard.doMove(move);

    bestScore = std::max(bestScore, -_negaMax(movedBoard, depth-1, -beta, -alpha));

    alpha = std::max(alpha, bestScore);
    if (alpha > beta) {
      break;
    }
  }

  // Store bestScore in transposition table
  TranspTable::Flag flag;
  if (bestScore < alphaOrig) {
    flag = TranspTable::UPPER_BOUND;
  } else if (bestScore >= beta) {
    flag = TranspTable::LOWER_BOUND;
  } else {
    flag = TranspTable::EXACT;
  }
  _tt.set(zKey, bestScore, depth, flag);

  return bestScore;
}

int Search::_qSearch(const Board& board, int alpha, int beta) {
  MoveGen movegen(board);
  MoveList legalMoves = movegen.getLegalMoves();

  // Check for checkmate / stalemate
  if (legalMoves.size() == 0) {
    if (board.colorIsInCheck(board.getActivePlayer())) { // Checkmate
      return -INF;
    } else { // Stalemate
      return 0;
    }
  }

  _orderMovesQSearch(legalMoves);
  int standPat = Eval(board, board.getActivePlayer()).getScore();

  // If node is quiet, just return eval
  if (!(legalMoves.at(0).getFlags() & Move::CAPTURE)) {
    return standPat;
  }

  if (standPat >= beta) {
    return beta;
  }
  if (alpha < standPat) {
    alpha = standPat;
  }

  Board movedBoard;
  for (auto move : legalMoves) {
    if ((move.getFlags() & Move::CAPTURE) == 0) {
      break;
    }

    movedBoard = board;
    movedBoard.doMove(move);

    int score = -_qSearch(movedBoard, -beta, -alpha);

    if (score >= beta) {
      return beta;
    }
    if (score > alpha) {
      alpha = score;
    }
  }
  return alpha;
}
<|endoftext|>


<commit_before>#include <fcntl.h>

#include <netinet/in.h>
#include <sys/socket.h>

#include "server.h"
#include "net/length.h"

#include "xapian.h"

const int MSECS_IDLE_TIMEOUT_DEFAULT = 60000;
const int MSECS_ACTIVE_TIMEOUT_DEFAULT = 15000;

void print_string(const std::string &string) {
	const char *p = string.c_str();
	const char *p_end = p + string.size();
	printf("'");
	while (p != p_end) {
		if (*p >= ' ' && *p <= '~') {
			printf("%c", *p++);
		} else {
			printf("\\x%02x", *p++ & 0xff);
		}
	}
	printf("'\n");
}

class XapianWorker : public Task {
private:
	XapiandClient *client;

public:
	XapianWorker(XapiandClient *client_) : Task(), client(client_) {}

	~XapianWorker() {}

	virtual void run() {
		client->run();
	}
};

//
//   Buffer class - allow for output buffering such that it can be written out
//                                 into async pieces
//
struct Buffer {
	char type;
	char *data;
	ssize_t len;
	ssize_t pos;

	Buffer(char type_, const char *bytes, ssize_t nbytes) {
		pos = 0;
		len = nbytes;
		type = type_;
		data = new char[nbytes];
		memcpy(data, bytes, nbytes);
	}

	virtual ~Buffer() {
		delete [] data;
	}

	char *dpos() {
		return data + pos;
	}

	ssize_t nbytes() {
		return len - pos;
	}
};

void XapiandClient::callback(ev::io &watcher, int revents)
{
	if (EV_ERROR & revents) {
		perror("got invalid event");
		return;
	}

	if (revents & EV_READ)
		read_cb(watcher);

	if (revents & EV_WRITE)
		write_cb(watcher);

	pthread_mutex_lock(&qmtx);
	if (write_queue.empty()) {
		io.set(ev::READ);
	} else {
		io.set(ev::READ|ev::WRITE);
	}
	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::async_cb(ev::async &watcher, int revents)
{
	pthread_mutex_lock(&qmtx);
	if (!write_queue.empty()) {
		io.set(ev::READ|ev::WRITE);
	}
	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::write_cb(ev::io &watcher)
{
	pthread_mutex_lock(&qmtx);

	if (write_queue.empty()) {
		io.set(ev::READ);
	} else {
		Buffer* buffer = write_queue.front();

		// printf("sent:");
		// print_string(std::string(buffer->dpos(), buffer->nbytes()));

		ssize_t written = write(watcher.fd, buffer->dpos(), buffer->nbytes());
		if (written < 0) {
			perror("read error");
		} else {
			buffer->pos += written;
			if (buffer->nbytes() == 0) {
				write_queue.pop();
				delete buffer;
			}
		}
	}

	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::read_cb(ev::io &watcher)
{
	char buf[1024];

	ssize_t received = recv(watcher.fd, buf, sizeof(buf), 0);

	if (received < 0) {
		perror("read error");
		return;
	}

	if (received == 0) {
		// Gack - we're deleting ourself inside of ourself!
		delete this;
	} else {
		buffer.append(buf, received);
		if (buffer.length() >= 2) {
			const char *o = buffer.data();
			const char *p = o;
			const char *p_end = p + buffer.size();

			message_type type = static_cast<message_type>(*p++);
			size_t len;
			try {
				len = decode_length(&p, p_end, true);
			} catch (const Xapian::NetworkError & e) {
				return;
			}
			std::string data = std::string(p, len);
			buffer.erase(0, p - o + len);

			// printf("received:");
			// print_string(data);

			Buffer *msg = new Buffer(type, data.c_str(), data.size());

			messages_queue.push(msg);

			if (type != MSG_GETMSET && type != MSG_SHUTDOWN) {
				thread_pool->addTask(new XapianWorker(this));
			}

		}
	}
}

void XapiandClient::signal_cb(ev::sig &signal, int revents)
{
	delete this;
}

message_type XapiandClient::get_message(double timeout, std::string & result, message_type required_type)
{
	Buffer* msg;
	if (!messages_queue.pop(msg)) {
		throw Xapian::NetworkError("No message available");
	}

	std::string buf(&msg->type, 1);
	buf += encode_length(msg->nbytes());
	buf += std::string(msg->dpos(), msg->nbytes());
	printf("get_message:");
	print_string(buf);

	message_type type = static_cast<message_type>(msg->type);
	result.assign(msg->dpos(), msg->nbytes());

	delete msg;
	return type;
}

void XapiandClient::send_message(reply_type type, const std::string &message) {
	char type_as_char = static_cast<char>(type);
	std::string buf(&type_as_char, 1);
	buf += encode_length(message.size());
	buf += message;

	printf("send_message:");
	print_string(buf);

	pthread_mutex_lock(&qmtx);
	write_queue.push(new Buffer(type, buf.c_str(), buf.size()));
	pthread_mutex_unlock(&qmtx);

	async.send();
}

void XapiandClient::send_message(reply_type type, const std::string &message, double end_time)
{
	send_message(type, message);
}

Xapian::Database * XapiandClient::get_db(bool writable_)
{
	if (endpoints.empty()) {
		return NULL;
	}
	if (!database_pool->checkout(&database, endpoints, writable_)) {
		return NULL;
	}
	return database->db;
}

void XapiandClient::release_db(Xapian::Database *db_)
{
	if (database) {
		database_pool->checkin(&database);
	}
}

void XapiandClient::select_db(const std::vector<std::string> &dbpaths_, bool writable_)
{
	std::vector<std::string>::const_iterator i(dbpaths_.begin());
	for (; i != dbpaths_.end(); i++) {
		Endpoint endpoint = Endpoint(*i, std::string(), 8890);
		endpoints.push_back(endpoint);
	}
	dbpaths = dbpaths_;
}

void XapiandClient::run()
{
	try {
		run_one();
	} catch (const Xapian::NetworkError &e) {
		printf("ERROR: %s\n", e.get_msg().c_str());
	} catch (...) {
		printf("ERROR!\n");
	}
}

XapiandClient::XapiandClient(int sock_, ThreadPool *thread_pool_, DatabasePool *database_pool_, double active_timeout_, double idle_timeout_)
	: RemoteProtocol(std::vector<std::string>(), active_timeout_, idle_timeout_, true),
	  sock(sock_),
	  thread_pool(thread_pool_),
	  database_pool(database_pool_),
	  database(NULL)
{
	pthread_mutex_init(&qmtx, 0);

	fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

	printf("Got connection, %d client(s) connected.\n", ++total_clients);

	io.set<XapiandClient, &XapiandClient::callback>(this);
	io.start(sock, ev::READ);

	sig.set<XapiandClient, &XapiandClient::signal_cb>(this);
	sig.start(SIGINT);

	async.set<XapiandClient, &XapiandClient::async_cb>(this);
	async.start();

	try {
		msg_update(std::string());
	} catch (const Xapian::NetworkError &e) {
		printf("ERROR: %s\n", e.get_msg().c_str());
	} catch (...) {
		printf("ERROR!\n");
	}
}

XapiandClient::~XapiandClient()
{
	shutdown(sock, SHUT_RDWR);

	// Stop and free watcher if client socket is closing
	io.stop();
	sig.stop();
	async.stop();

	close(sock);

	printf("Lost connection, %d client(s) connected.\n", --total_clients);

	pthread_mutex_destroy(&qmtx);
}

void XapiandServer::io_accept(ev::io &watcher, int revents)
{
	if (EV_ERROR & revents) {
		perror("got invalid event");
		return;
	}

	struct sockaddr_in client_addr;
	socklen_t client_len = sizeof(client_addr);

	int client_sock = accept(watcher.fd, (struct sockaddr *)&client_addr, &client_len);

	if (client_sock < 0) {
		perror("accept error");
		return;
	}

	double active_timeout = MSECS_ACTIVE_TIMEOUT_DEFAULT * 1e-3;
	double idle_timeout = MSECS_IDLE_TIMEOUT_DEFAULT * 1e-3;
	new XapiandClient(client_sock, &thread_pool, &database_pool, active_timeout, idle_timeout);
}

void XapiandServer::signal_cb(ev::sig &signal, int revents)
{
	signal.loop.break_loop();
}

XapiandServer::XapiandServer(int port, int thread_pool_size)
	: thread_pool(thread_pool_size)
{
	int optval = 1;
	struct sockaddr_in addr;

	sock = socket(PF_INET, SOCK_STREAM, 0);
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = INADDR_ANY;

	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
		perror("bind");
		close(sock);
		sock = 0;
	} else {
		printf("Listening on port %d\n", port);
		fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

		listen(sock, 5);

		io.set<XapiandServer, &XapiandServer::io_accept>(this);
		io.start(sock, ev::READ);

		sig.set<&XapiandServer::signal_cb>();
		sig.start(SIGINT);
	}
}

XapiandServer::~XapiandServer()
{
	shutdown(sock, SHUT_RDWR);

	io.stop();
	sig.stop();

	close(sock);

	printf("Done with all work!\n");
}

int XapiandClient::total_clients = 0;
<commit_msg>Use waiting queue for write buffer<commit_after>#include <fcntl.h>

#include <netinet/in.h>
#include <sys/socket.h>

#include "server.h"
#include "net/length.h"

#include "xapian.h"

const int WRITE_QUEUE_SIZE = 30;

const int MSECS_IDLE_TIMEOUT_DEFAULT = 60000;
const int MSECS_ACTIVE_TIMEOUT_DEFAULT = 15000;

void print_string(const std::string &string) {
	const char *p = string.c_str();
	const char *p_end = p + string.size();
	printf("'");
	while (p != p_end) {
		if (*p >= ' ' && *p <= '~') {
			printf("%c", *p++);
		} else {
			printf("\\x%02x", *p++ & 0xff);
		}
	}
	printf("'\n");
}

class XapianWorker : public Task {
private:
	XapiandClient *client;

public:
	XapianWorker(XapiandClient *client_) : Task(), client(client_) {}

	~XapianWorker() {}

	virtual void run() {
		client->run();
	}
};

//
//   Buffer class - allow for output buffering such that it can be written out
//                                 into async pieces
//
struct Buffer {
	char type;
	char *data;
	ssize_t len;
	ssize_t pos;

	Buffer(char type_, const char *bytes, ssize_t nbytes) {
		pos = 0;
		len = nbytes;
		type = type_;
		data = new char[nbytes];
		memcpy(data, bytes, nbytes);
	}

	virtual ~Buffer() {
		delete [] data;
	}

	char *dpos() {
		return data + pos;
	}

	ssize_t nbytes() {
		return len - pos;
	}
};

void XapiandClient::callback(ev::io &watcher, int revents)
{
	if (EV_ERROR & revents) {
		perror("got invalid event");
		return;
	}

	if (revents & EV_READ)
		read_cb(watcher);

	if (revents & EV_WRITE)
		write_cb(watcher);

	pthread_mutex_lock(&qmtx);
	if (write_queue.empty()) {
		io.set(ev::READ);
	} else {
		io.set(ev::READ|ev::WRITE);
	}
	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::async_cb(ev::async &watcher, int revents)
{
	pthread_mutex_lock(&qmtx);
	if (!write_queue.empty()) {
		io.set(ev::READ|ev::WRITE);
	}
	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::write_cb(ev::io &watcher)
{
	pthread_mutex_lock(&qmtx);

	if (write_queue.empty()) {
		io.set(ev::READ);
	} else {
		Buffer* buffer = write_queue.front();

		// printf("sent:");
		// print_string(std::string(buffer->dpos(), buffer->nbytes()));

		ssize_t written = write(watcher.fd, buffer->dpos(), buffer->nbytes());
		if (written < 0) {
			perror("read error");
		} else {
			buffer->pos += written;
			if (buffer->nbytes() == 0) {
				write_queue.pop(buffer);
				delete buffer;
			}
		}
	}

	pthread_mutex_unlock(&qmtx);
}

void XapiandClient::read_cb(ev::io &watcher)
{
	char buf[1024];

	ssize_t received = recv(watcher.fd, buf, sizeof(buf), 0);

	if (received < 0) {
		perror("read error");
		return;
	}

	if (received == 0) {
		// Gack - we're deleting ourself inside of ourself!
		delete this;
	} else {
		buffer.append(buf, received);
		if (buffer.length() >= 2) {
			const char *o = buffer.data();
			const char *p = o;
			const char *p_end = p + buffer.size();

			message_type type = static_cast<message_type>(*p++);
			size_t len;
			try {
				len = decode_length(&p, p_end, true);
			} catch (const Xapian::NetworkError & e) {
				return;
			}
			std::string data = std::string(p, len);
			buffer.erase(0, p - o + len);

			// printf("received:");
			// print_string(data);

			Buffer *msg = new Buffer(type, data.c_str(), data.size());

			messages_queue.push(msg);

			if (type != MSG_GETMSET && type != MSG_SHUTDOWN) {
				thread_pool->addTask(new XapianWorker(this));
			}

		}
	}
}

void XapiandClient::signal_cb(ev::sig &signal, int revents)
{
	delete this;
}

message_type XapiandClient::get_message(double timeout, std::string & result, message_type required_type)
{
	Buffer* msg;
	if (!messages_queue.pop(msg)) {
		throw Xapian::NetworkError("No message available");
	}

	std::string buf(&msg->type, 1);
	buf += encode_length(msg->nbytes());
	buf += std::string(msg->dpos(), msg->nbytes());
	printf("get_message:");
	print_string(buf);

	message_type type = static_cast<message_type>(msg->type);
	result.assign(msg->dpos(), msg->nbytes());

	delete msg;
	return type;
}

void XapiandClient::send_message(reply_type type, const std::string &message) {
	char type_as_char = static_cast<char>(type);
	std::string buf(&type_as_char, 1);
	buf += encode_length(message.size());
	buf += message;

	printf("send_message:");
	print_string(buf);

	pthread_mutex_lock(&qmtx);
	Buffer *buffer = new Buffer(type, buf.c_str(), buf.size());
	write_queue.push(buffer);
	pthread_mutex_unlock(&qmtx);

	async.send();
}

void XapiandClient::send_message(reply_type type, const std::string &message, double end_time)
{
	send_message(type, message);
}

Xapian::Database * XapiandClient::get_db(bool writable_)
{
	if (endpoints.empty()) {
		return NULL;
	}
	if (!database_pool->checkout(&database, endpoints, writable_)) {
		return NULL;
	}
	return database->db;
}

void XapiandClient::release_db(Xapian::Database *db_)
{
	if (database) {
		database_pool->checkin(&database);
	}
}

void XapiandClient::select_db(const std::vector<std::string> &dbpaths_, bool writable_)
{
	std::vector<std::string>::const_iterator i(dbpaths_.begin());
	for (; i != dbpaths_.end(); i++) {
		Endpoint endpoint = Endpoint(*i, std::string(), 8890);
		endpoints.push_back(endpoint);
	}
	dbpaths = dbpaths_;
}

void XapiandClient::run()
{
	try {
		run_one();
	} catch (const Xapian::NetworkError &e) {
		printf("ERROR: %s\n", e.get_msg().c_str());
	} catch (...) {
		printf("ERROR!\n");
	}
}

XapiandClient::XapiandClient(int sock_, ThreadPool *thread_pool_, DatabasePool *database_pool_, double active_timeout_, double idle_timeout_)
	: RemoteProtocol(std::vector<std::string>(), active_timeout_, idle_timeout_, true),
	  sock(sock_),
	  thread_pool(thread_pool_),
	  database_pool(database_pool_),
	  write_queue(WRITE_QUEUE_SIZE),
	  database(NULL)
{
	pthread_mutex_init(&qmtx, 0);

	fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

	printf("Got connection, %d client(s) connected.\n", ++total_clients);

	io.set<XapiandClient, &XapiandClient::callback>(this);
	io.start(sock, ev::READ);

	sig.set<XapiandClient, &XapiandClient::signal_cb>(this);
	sig.start(SIGINT);

	async.set<XapiandClient, &XapiandClient::async_cb>(this);
	async.start();

	try {
		msg_update(std::string());
	} catch (const Xapian::NetworkError &e) {
		printf("ERROR: %s\n", e.get_msg().c_str());
	} catch (...) {
		printf("ERROR!\n");
	}
}

XapiandClient::~XapiandClient()
{
	shutdown(sock, SHUT_RDWR);

	// Stop and free watcher if client socket is closing
	io.stop();
	sig.stop();
	async.stop();

	close(sock);

	printf("Lost connection, %d client(s) connected.\n", --total_clients);

	pthread_mutex_destroy(&qmtx);
}

void XapiandServer::io_accept(ev::io &watcher, int revents)
{
	if (EV_ERROR & revents) {
		perror("got invalid event");
		return;
	}

	struct sockaddr_in client_addr;
	socklen_t client_len = sizeof(client_addr);

	int client_sock = accept(watcher.fd, (struct sockaddr *)&client_addr, &client_len);

	if (client_sock < 0) {
		perror("accept error");
		return;
	}

	double active_timeout = MSECS_ACTIVE_TIMEOUT_DEFAULT * 1e-3;
	double idle_timeout = MSECS_IDLE_TIMEOUT_DEFAULT * 1e-3;
	new XapiandClient(client_sock, &thread_pool, &database_pool, active_timeout, idle_timeout);
}

void XapiandServer::signal_cb(ev::sig &signal, int revents)
{
	signal.loop.break_loop();
}

XapiandServer::XapiandServer(int port, int thread_pool_size)
	: thread_pool(thread_pool_size)
{
	int optval = 1;
	struct sockaddr_in addr;

	sock = socket(PF_INET, SOCK_STREAM, 0);
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval));

	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	addr.sin_addr.s_addr = INADDR_ANY;

	if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
		perror("bind");
		close(sock);
		sock = 0;
	} else {
		printf("Listening on port %d\n", port);
		fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);

		listen(sock, 5);

		io.set<XapiandServer, &XapiandServer::io_accept>(this);
		io.start(sock, ev::READ);

		sig.set<&XapiandServer::signal_cb>();
		sig.start(SIGINT);
	}
}

XapiandServer::~XapiandServer()
{
	shutdown(sock, SHUT_RDWR);

	io.stop();
	sig.stop();

	close(sock);

	printf("Done with all work!\n");
}

int XapiandClient::total_clients = 0;
<|endoftext|>