<commit_before>/* listener.cc
   Mathieu Stefani, 12 August 2015

*/

#include <pistache/common.h>
#include <pistache/errors.h>
#include <pistache/listener.h>
#include <pistache/os.h>
#include <pistache/peer.h>
#include <pistache/transport.h>

#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/timerfd.h>
#include <sys/types.h>

#include <chrono>
#include <memory>
#include <vector>

#include <cerrno>
#include <signal.h>

#ifdef PISTACHE_USE_SSL

#include <openssl/err.h>
#include <openssl/ssl.h>

#endif /* PISTACHE_USE_SSL */

namespace Pistache {
namespace Tcp {

void setSocketOptions(Fd fd, Flags<Options> options) {
  if (options.hasFlag(Options::ReuseAddr)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)));
  }

  if (options.hasFlag(Options::ReusePort)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)));
  }

  if (options.hasFlag(Options::Linger)) {
    struct linger opt;
    opt.l_onoff = 1;
    opt.l_linger = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_LINGER, &opt, sizeof(opt)));
  }

  if (options.hasFlag(Options::FastOpen)) {
    int hint = 5;
    TRY(::setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &hint, sizeof(hint)));
  }
  if (options.hasFlag(Options::NoDelay)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)));
  }
}

Listener::Listener()
    : addr_(), listen_fd(-1), backlog_(Const::MaxBacklog), shutdownFd(),
      poller(), options_(), workers_(Const::DefaultWorkers), workersName_(),
      reactor_(), transportKey(), useSSL_(false), ssl_ctx_(nullptr) {}

Listener::Listener(const Address &address)
    : addr_(address), listen_fd(-1), backlog_(Const::MaxBacklog), shutdownFd(),
      poller(), options_(), workers_(Const::DefaultWorkers), workersName_(),
      reactor_(), transportKey(), useSSL_(false), ssl_ctx_(nullptr) {}

Listener::~Listener() {
  if (isBound())
    shutdown();
  if (acceptThread.joinable())
    acceptThread.join();

  if (listen_fd >= 0) {
    close(listen_fd);
    listen_fd = -1;
  }
#ifdef PISTACHE_USE_SSL
  if (this->useSSL_) {
    SSL_CTX_free((SSL_CTX *)this->ssl_ctx_);
    EVP_cleanup();
  }
#endif /* PISTACHE_USE_SSL */
}

void Listener::init(size_t workers, Flags<Options> options,
                    const std::string &workersName, int backlog) {
  if (workers > hardware_concurrency()) {
    // Log::warning() << "More workers than available cores"
  }

  options_ = options;
  backlog_ = backlog;
  useSSL_ = false;
  workers_ = workers;
  workersName_ = workersName;
}

void Listener::setHandler(const std::shared_ptr<Handler> &handler) {
  handler_ = handler;
}

void Listener::pinWorker(size_t worker, const CpuSet &set) {
  UNUSED(worker)
  UNUSED(set)
#if 0
    if (ioGroup.empty()) {
        throw std::domain_error("Invalid operation, did you call init() before ?");
    }
    if (worker > ioGroup.size()) {
        throw std::invalid_argument("Trying to pin invalid worker");
    }

    auto &wrk = ioGroup[worker];
    wrk->pin(set);
#endif
}

void Listener::bind() { bind(addr_); }

void Listener::bind(const Address &address) {
  if (!handler_)
    throw std::runtime_error("Call setHandler before calling bind()");
  addr_ = address;

  struct addrinfo hints;
  hints.ai_family = address.family();
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;
  hints.ai_protocol = 0;

  const auto &host = addr_.host();
  const auto &port = addr_.port().toString();
  AddrInfo addr_info;

  TRY(addr_info.invoke(host.c_str(), port.c_str(), &hints));

  int fd = -1;

  const addrinfo *addr = nullptr;
  for (addr = addr_info.get_info_ptr(); addr; addr = addr->ai_next) {
    fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    if (fd < 0)
      continue;

    setSocketOptions(fd, options_);

    if (::bind(fd, addr->ai_addr, addr->ai_addrlen) < 0) {
      close(fd);
      continue;
    }

    TRY(::listen(fd, backlog_));
    break;
  }

  // At this point, it is still possible that we couldn't bind any socket. If it
  // is the case, the previous loop would have exited naturally and addr will be
  // null.
  if (addr == nullptr) {
    throw std::runtime_error(strerror(errno));
  }

  make_non_blocking(fd);
  poller.addFd(fd, Flags<Polling::NotifyOn>(Polling::NotifyOn::Read),
               Polling::Tag(fd));
  listen_fd = fd;

  auto transport = std::make_shared<Transport>(handler_);

  reactor_.init(Aio::AsyncContext(workers_, workersName_));
  transportKey = reactor_.addHandler(transport);
}

bool Listener::isBound() const { return listen_fd != -1; }

// Return actual TCP port Listener is on, or 0 on error / no port.
// Notes:
// 1) Default constructor for 'Port()' sets value to 0.
// 2) Socket is created inside 'Listener::run()', which is called from
//    'Endpoint::serve()' and 'Endpoint::serveThreaded()'.  So getting the
//    port is only useful if you attempt to do so from a _different_ thread
//    than the one running 'Listener::run()'.  So for a traditional single-
//    threaded program this method is of little value.
Port Listener::getPort() const {
  if (listen_fd == -1) {
    return Port();
  }

  struct sockaddr_in sock_addr = {0};
  socklen_t addrlen = sizeof(sock_addr);
  auto sock_addr_alias = reinterpret_cast<struct sockaddr *>(&sock_addr);

  if (-1 == getsockname(listen_fd, sock_addr_alias, &addrlen)) {
    return Port();
  }

  return Port(ntohs(sock_addr.sin_port));
}

void Listener::run() {
  shutdownFd.bind(poller);
  reactor_.run();

  for (;;) {
    std::vector<Polling::Event> events;
    int ready_fds = poller.poll(events);

    if (ready_fds == -1) {
      throw Error::system("Polling");
    }
    for (const auto &event : events) {
      if (event.tag == shutdownFd.tag())
        return;

      if (event.flags.hasFlag(Polling::NotifyOn::Read)) {
        auto fd = event.tag.value();
        if (static_cast<ssize_t>(fd) == listen_fd) {
          try {
            handleNewConnection();
          } catch (SocketError &ex) {
            std::cerr << "Server: " << ex.what() << std::endl;
          } catch (ServerError &ex) {
            std::cerr << "Server: " << ex.what() << std::endl;
            throw;
          }
        }
      }
    }
  }
}

void Listener::runThreaded() {
  acceptThread = std::thread([=]() { this->run(); });
}

void Listener::shutdown() {
  if (shutdownFd.isBound())
    shutdownFd.notify();
  reactor_.shutdown();
}

Async::Promise<Listener::Load>
Listener::requestLoad(const Listener::Load &old) {
  auto handlers = reactor_.handlers(transportKey);

  std::vector<Async::Promise<rusage>> loads;
  for (const auto &handler : handlers) {
    auto transport = std::static_pointer_cast<Transport>(handler);
    loads.push_back(transport->load());
  }

  return Async::whenAll(std::begin(loads), std::end(loads))
      .then(
          [=](const std::vector<rusage> &usages) {
            Load res;
            res.raw = usages;

            if (old.raw.empty()) {
              res.global = 0.0;
              for (size_t i = 0; i < handlers.size(); ++i)
                res.workers.push_back(0.0);
            } else {

              auto totalElapsed = [](rusage usage) {
                return (usage.ru_stime.tv_sec * 1e6 + usage.ru_stime.tv_usec) +
                       (usage.ru_utime.tv_sec * 1e6 + usage.ru_utime.tv_usec);
              };

              auto now = std::chrono::system_clock::now();
              auto diff = now - old.tick;
              auto tick =
                  std::chrono::duration_cast<std::chrono::microseconds>(diff);
              res.tick = now;

              for (size_t i = 0; i < usages.size(); ++i) {
                auto last = old.raw[i];
                const auto &usage = usages[i];

                auto nowElapsed = totalElapsed(usage);
                auto timeElapsed = nowElapsed - totalElapsed(last);

                auto loadPct = (timeElapsed * 100.0) / tick.count();
                res.workers.push_back(loadPct);
                res.global += loadPct;
              }

              res.global /= usages.size();
            }

            return res;
          },
          Async::Throw);
}

Address Listener::address() const { return addr_; }

Options Listener::options() const { return options_; }

void Listener::handleNewConnection() {
  struct sockaddr_in peer_addr;
  int client_fd = acceptConnection(peer_addr);

  void *ssl = nullptr;

#ifdef PISTACHE_USE_SSL
  if (this->useSSL_) {

    SSL* ssl_data = SSL_new((SSL_CTX *)this->ssl_ctx_);
    if (ssl == NULL) {
      close(client_fd);
      throw std::runtime_error("Cannot create SSL connection");
    }

    SSL_set_fd(ssl, client_fd);
    SSL_set_accept_state(ssl);

    if (SSL_accept(ssl) <= 0) {
      ERR_print_errors_fp(stderr);
      SSL_free(ssl);
      close(client_fd);
      return;
    }
    ssl = static_cast<void*>(ssl_data);
  }
#endif /* PISTACHE_USE_SSL */

  make_non_blocking(client_fd);

  std::shared_ptr<Peer> peer;
  if (this->useSSL_) {
    peer = Peer::CreateSSL(client_fd, Address::fromUnix(&peer_addr), ssl);
  } else {
    peer = Peer::Create(client_fd, Address::fromUnix(&peer_addr));
  }

  dispatchPeer(peer);
}

int Listener::acceptConnection(struct sockaddr_in &peer_addr) const {
  socklen_t peer_addr_len = sizeof(peer_addr);
  int client_fd =
      ::accept(listen_fd, (struct sockaddr *)&peer_addr, &peer_addr_len);
  if (client_fd < 0) {
    if (errno == EBADF || errno == ENOTSOCK)
      throw ServerError(strerror(errno));
    else
      throw SocketError(strerror(errno));
  }
  return client_fd;
}

void Listener::dispatchPeer(const std::shared_ptr<Peer> &peer) {
  auto handlers = reactor_.handlers(transportKey);
  auto idx = peer->fd() % handlers.size();
  auto transport = std::static_pointer_cast<Transport>(handlers[idx]);

  transport->handleNewPeer(peer);
}

#ifdef PISTACHE_USE_SSL

static SSL_CTX *ssl_create_context(const std::string &cert,
                                   const std::string &key,
                                   bool use_compression) {
  const SSL_METHOD *method;
  SSL_CTX *ctx;

  method = SSLv23_server_method();

  ctx = SSL_CTX_new(method);
  if (ctx == NULL) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot setup SSL context");
  }

  if (!use_compression) {
    /* Disable compression to prevent BREACH and CRIME vulnerabilities. */
    if (!SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION)) {
      ERR_print_errors_fp(stderr);
      throw std::runtime_error("Cannot disable compression");
    }
  }

  /* Function introduced in 1.0.2 */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
  SSL_CTX_set_ecdh_auto(ctx, 1);
#endif /* OPENSSL_VERSION_NUMBER */

  if (SSL_CTX_use_certificate_file(ctx, cert.c_str(), SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot load SSL certificate");
  }

  if (SSL_CTX_use_PrivateKey_file(ctx, key.c_str(), SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot load SSL private key");
  }

  if (!SSL_CTX_check_private_key(ctx)) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error(
        "Private key does not match public key in the certificate");
  }

  return ctx;
}

void Listener::setupSSLAuth(const std::string &ca_file,
                            const std::string &ca_path,
                            int (*cb)(int, void *) = NULL) {
  const char *__ca_file = NULL;
  const char *__ca_path = NULL;

  if (this->ssl_ctx_ == NULL)
    throw std::runtime_error("SSL Context is not initialized");

  if (!ca_file.empty())
    __ca_file = ca_file.c_str();
  if (!ca_path.empty())
    __ca_path = ca_path.c_str();

  if (SSL_CTX_load_verify_locations((SSL_CTX *)this->ssl_ctx_, __ca_file,
                                    __ca_path) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot verify SSL locations");
  }

  SSL_CTX_set_verify((SSL_CTX *)this->ssl_ctx_,
                     SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
                         SSL_VERIFY_CLIENT_ONCE,
  /* Callback type did change in 1.0.1 */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
                     (int (*)(int, X509_STORE_CTX *))cb
#else
                     (SSL_verify_cb)cb
#endif /* OPENSSL_VERSION_NUMBER */
  );
}

void Listener::setupSSL(const std::string &cert_path,
                        const std::string &key_path, bool use_compression) {
  SSL_load_error_strings();
  OpenSSL_add_ssl_algorithms();

  this->ssl_ctx_ = ssl_create_context(cert_path, key_path, use_compression);
  this->useSSL_ = true;
}

#endif /* PISTACHE_USE_SSL */

} // namespace Tcp
} // namespace Pistache
<commit_msg>Fix using wrong pointer in ssl<commit_after>/* listener.cc
   Mathieu Stefani, 12 August 2015

*/

#include <pistache/common.h>
#include <pistache/errors.h>
#include <pistache/listener.h>
#include <pistache/os.h>
#include <pistache/peer.h>
#include <pistache/transport.h>

#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/timerfd.h>
#include <sys/types.h>

#include <chrono>
#include <memory>
#include <vector>

#include <cerrno>
#include <signal.h>

#ifdef PISTACHE_USE_SSL

#include <openssl/err.h>
#include <openssl/ssl.h>

#endif /* PISTACHE_USE_SSL */

namespace Pistache {
namespace Tcp {

void setSocketOptions(Fd fd, Flags<Options> options) {
  if (options.hasFlag(Options::ReuseAddr)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)));
  }

  if (options.hasFlag(Options::ReusePort)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one)));
  }

  if (options.hasFlag(Options::Linger)) {
    struct linger opt;
    opt.l_onoff = 1;
    opt.l_linger = 1;
    TRY(::setsockopt(fd, SOL_SOCKET, SO_LINGER, &opt, sizeof(opt)));
  }

  if (options.hasFlag(Options::FastOpen)) {
    int hint = 5;
    TRY(::setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &hint, sizeof(hint)));
  }
  if (options.hasFlag(Options::NoDelay)) {
    int one = 1;
    TRY(::setsockopt(fd, SOL_TCP, TCP_NODELAY, &one, sizeof(one)));
  }
}

Listener::Listener()
    : addr_(), listen_fd(-1), backlog_(Const::MaxBacklog), shutdownFd(),
      poller(), options_(), workers_(Const::DefaultWorkers), workersName_(),
      reactor_(), transportKey(), useSSL_(false), ssl_ctx_(nullptr) {}

Listener::Listener(const Address &address)
    : addr_(address), listen_fd(-1), backlog_(Const::MaxBacklog), shutdownFd(),
      poller(), options_(), workers_(Const::DefaultWorkers), workersName_(),
      reactor_(), transportKey(), useSSL_(false), ssl_ctx_(nullptr) {}

Listener::~Listener() {
  if (isBound())
    shutdown();
  if (acceptThread.joinable())
    acceptThread.join();

  if (listen_fd >= 0) {
    close(listen_fd);
    listen_fd = -1;
  }
#ifdef PISTACHE_USE_SSL
  if (this->useSSL_) {
    SSL_CTX_free((SSL_CTX *)this->ssl_ctx_);
    EVP_cleanup();
  }
#endif /* PISTACHE_USE_SSL */
}

void Listener::init(size_t workers, Flags<Options> options,
                    const std::string &workersName, int backlog) {
  if (workers > hardware_concurrency()) {
    // Log::warning() << "More workers than available cores"
  }

  options_ = options;
  backlog_ = backlog;
  useSSL_ = false;
  workers_ = workers;
  workersName_ = workersName;
}

void Listener::setHandler(const std::shared_ptr<Handler> &handler) {
  handler_ = handler;
}

void Listener::pinWorker(size_t worker, const CpuSet &set) {
  UNUSED(worker)
  UNUSED(set)
#if 0
    if (ioGroup.empty()) {
        throw std::domain_error("Invalid operation, did you call init() before ?");
    }
    if (worker > ioGroup.size()) {
        throw std::invalid_argument("Trying to pin invalid worker");
    }

    auto &wrk = ioGroup[worker];
    wrk->pin(set);
#endif
}

void Listener::bind() { bind(addr_); }

void Listener::bind(const Address &address) {
  if (!handler_)
    throw std::runtime_error("Call setHandler before calling bind()");
  addr_ = address;

  struct addrinfo hints;
  hints.ai_family = address.family();
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;
  hints.ai_protocol = 0;

  const auto &host = addr_.host();
  const auto &port = addr_.port().toString();
  AddrInfo addr_info;

  TRY(addr_info.invoke(host.c_str(), port.c_str(), &hints));

  int fd = -1;

  const addrinfo *addr = nullptr;
  for (addr = addr_info.get_info_ptr(); addr; addr = addr->ai_next) {
    fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
    if (fd < 0)
      continue;

    setSocketOptions(fd, options_);

    if (::bind(fd, addr->ai_addr, addr->ai_addrlen) < 0) {
      close(fd);
      continue;
    }

    TRY(::listen(fd, backlog_));
    break;
  }

  // At this point, it is still possible that we couldn't bind any socket. If it
  // is the case, the previous loop would have exited naturally and addr will be
  // null.
  if (addr == nullptr) {
    throw std::runtime_error(strerror(errno));
  }

  make_non_blocking(fd);
  poller.addFd(fd, Flags<Polling::NotifyOn>(Polling::NotifyOn::Read),
               Polling::Tag(fd));
  listen_fd = fd;

  auto transport = std::make_shared<Transport>(handler_);

  reactor_.init(Aio::AsyncContext(workers_, workersName_));
  transportKey = reactor_.addHandler(transport);
}

bool Listener::isBound() const { return listen_fd != -1; }

// Return actual TCP port Listener is on, or 0 on error / no port.
// Notes:
// 1) Default constructor for 'Port()' sets value to 0.
// 2) Socket is created inside 'Listener::run()', which is called from
//    'Endpoint::serve()' and 'Endpoint::serveThreaded()'.  So getting the
//    port is only useful if you attempt to do so from a _different_ thread
//    than the one running 'Listener::run()'.  So for a traditional single-
//    threaded program this method is of little value.
Port Listener::getPort() const {
  if (listen_fd == -1) {
    return Port();
  }

  struct sockaddr_in sock_addr = {0};
  socklen_t addrlen = sizeof(sock_addr);
  auto sock_addr_alias = reinterpret_cast<struct sockaddr *>(&sock_addr);

  if (-1 == getsockname(listen_fd, sock_addr_alias, &addrlen)) {
    return Port();
  }

  return Port(ntohs(sock_addr.sin_port));
}

void Listener::run() {
  shutdownFd.bind(poller);
  reactor_.run();

  for (;;) {
    std::vector<Polling::Event> events;
    int ready_fds = poller.poll(events);

    if (ready_fds == -1) {
      throw Error::system("Polling");
    }
    for (const auto &event : events) {
      if (event.tag == shutdownFd.tag())
        return;

      if (event.flags.hasFlag(Polling::NotifyOn::Read)) {
        auto fd = event.tag.value();
        if (static_cast<ssize_t>(fd) == listen_fd) {
          try {
            handleNewConnection();
          } catch (SocketError &ex) {
            std::cerr << "Server: " << ex.what() << std::endl;
          } catch (ServerError &ex) {
            std::cerr << "Server: " << ex.what() << std::endl;
            throw;
          }
        }
      }
    }
  }
}

void Listener::runThreaded() {
  acceptThread = std::thread([=]() { this->run(); });
}

void Listener::shutdown() {
  if (shutdownFd.isBound())
    shutdownFd.notify();
  reactor_.shutdown();
}

Async::Promise<Listener::Load>
Listener::requestLoad(const Listener::Load &old) {
  auto handlers = reactor_.handlers(transportKey);

  std::vector<Async::Promise<rusage>> loads;
  for (const auto &handler : handlers) {
    auto transport = std::static_pointer_cast<Transport>(handler);
    loads.push_back(transport->load());
  }

  return Async::whenAll(std::begin(loads), std::end(loads))
      .then(
          [=](const std::vector<rusage> &usages) {
            Load res;
            res.raw = usages;

            if (old.raw.empty()) {
              res.global = 0.0;
              for (size_t i = 0; i < handlers.size(); ++i)
                res.workers.push_back(0.0);
            } else {

              auto totalElapsed = [](rusage usage) {
                return (usage.ru_stime.tv_sec * 1e6 + usage.ru_stime.tv_usec) +
                       (usage.ru_utime.tv_sec * 1e6 + usage.ru_utime.tv_usec);
              };

              auto now = std::chrono::system_clock::now();
              auto diff = now - old.tick;
              auto tick =
                  std::chrono::duration_cast<std::chrono::microseconds>(diff);
              res.tick = now;

              for (size_t i = 0; i < usages.size(); ++i) {
                auto last = old.raw[i];
                const auto &usage = usages[i];

                auto nowElapsed = totalElapsed(usage);
                auto timeElapsed = nowElapsed - totalElapsed(last);

                auto loadPct = (timeElapsed * 100.0) / tick.count();
                res.workers.push_back(loadPct);
                res.global += loadPct;
              }

              res.global /= usages.size();
            }

            return res;
          },
          Async::Throw);
}

Address Listener::address() const { return addr_; }

Options Listener::options() const { return options_; }

void Listener::handleNewConnection() {
  struct sockaddr_in peer_addr;
  int client_fd = acceptConnection(peer_addr);

  void *ssl = nullptr;

#ifdef PISTACHE_USE_SSL
  if (this->useSSL_) {

    SSL* ssl_data = SSL_new((SSL_CTX *)this->ssl_ctx_);
    if (ssl_data == nullptr) {
      close(client_fd);
      throw std::runtime_error("Cannot create SSL connection");
    }

    SSL_set_fd(ssl_data, client_fd);
    SSL_set_accept_state(ssl_data);

    if (SSL_accept(ssl) <= 0) {
      ERR_print_errors_fp(stderr);
      SSL_free(ssl_data);
      close(client_fd);
      return;
    }
    ssl = static_cast<void*>(ssl_data);
  }
#endif /* PISTACHE_USE_SSL */

  make_non_blocking(client_fd);

  std::shared_ptr<Peer> peer;
  if (this->useSSL_) {
    peer = Peer::CreateSSL(client_fd, Address::fromUnix(&peer_addr), ssl);
  } else {
    peer = Peer::Create(client_fd, Address::fromUnix(&peer_addr));
  }

  dispatchPeer(peer);
}

int Listener::acceptConnection(struct sockaddr_in &peer_addr) const {
  socklen_t peer_addr_len = sizeof(peer_addr);
  int client_fd =
      ::accept(listen_fd, (struct sockaddr *)&peer_addr, &peer_addr_len);
  if (client_fd < 0) {
    if (errno == EBADF || errno == ENOTSOCK)
      throw ServerError(strerror(errno));
    else
      throw SocketError(strerror(errno));
  }
  return client_fd;
}

void Listener::dispatchPeer(const std::shared_ptr<Peer> &peer) {
  auto handlers = reactor_.handlers(transportKey);
  auto idx = peer->fd() % handlers.size();
  auto transport = std::static_pointer_cast<Transport>(handlers[idx]);

  transport->handleNewPeer(peer);
}

#ifdef PISTACHE_USE_SSL

static SSL_CTX *ssl_create_context(const std::string &cert,
                                   const std::string &key,
                                   bool use_compression) {
  const SSL_METHOD *method;
  SSL_CTX *ctx;

  method = SSLv23_server_method();

  ctx = SSL_CTX_new(method);
  if (ctx == NULL) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot setup SSL context");
  }

  if (!use_compression) {
    /* Disable compression to prevent BREACH and CRIME vulnerabilities. */
    if (!SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION)) {
      ERR_print_errors_fp(stderr);
      throw std::runtime_error("Cannot disable compression");
    }
  }

  /* Function introduced in 1.0.2 */
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
  SSL_CTX_set_ecdh_auto(ctx, 1);
#endif /* OPENSSL_VERSION_NUMBER */

  if (SSL_CTX_use_certificate_file(ctx, cert.c_str(), SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot load SSL certificate");
  }

  if (SSL_CTX_use_PrivateKey_file(ctx, key.c_str(), SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot load SSL private key");
  }

  if (!SSL_CTX_check_private_key(ctx)) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error(
        "Private key does not match public key in the certificate");
  }

  return ctx;
}

void Listener::setupSSLAuth(const std::string &ca_file,
                            const std::string &ca_path,
                            int (*cb)(int, void *) = NULL) {
  const char *__ca_file = NULL;
  const char *__ca_path = NULL;

  if (this->ssl_ctx_ == NULL)
    throw std::runtime_error("SSL Context is not initialized");

  if (!ca_file.empty())
    __ca_file = ca_file.c_str();
  if (!ca_path.empty())
    __ca_path = ca_path.c_str();

  if (SSL_CTX_load_verify_locations((SSL_CTX *)this->ssl_ctx_, __ca_file,
                                    __ca_path) <= 0) {
    ERR_print_errors_fp(stderr);
    throw std::runtime_error("Cannot verify SSL locations");
  }

  SSL_CTX_set_verify((SSL_CTX *)this->ssl_ctx_,
                     SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
                         SSL_VERIFY_CLIENT_ONCE,
  /* Callback type did change in 1.0.1 */
#if OPENSSL_VERSION_NUMBER < 0x10100000L
                     (int (*)(int, X509_STORE_CTX *))cb
#else
                     (SSL_verify_cb)cb
#endif /* OPENSSL_VERSION_NUMBER */
  );
}

void Listener::setupSSL(const std::string &cert_path,
                        const std::string &key_path, bool use_compression) {
  SSL_load_error_strings();
  OpenSSL_add_ssl_algorithms();

  this->ssl_ctx_ = ssl_create_context(cert_path, key_path, use_compression);
  this->useSSL_ = true;
}

#endif /* PISTACHE_USE_SSL */

} // namespace Tcp
} // namespace Pistache
<|endoftext|>


<commit_before>#include <stdint.h>
#ifdef __cplusplus
	#include <cstdlib>
#else
	#include <stdlib.h>
#endif
#ifdef __APPLE__
#include <SDL/SDL.h>
#else
#include <SDL.h>
#endif

#include <windows.h>     // required before including mmsystem
#include <mmsystem.h>    // multimedia functions for windows

#define round(x) ((x)>=0?(int16_t)((x)+0.5):(int16_t)((x)-0.5))

#include "constants.h"
#include "RenderingContext.h"
#include "AmsFont.h"
#include "Button.h"
#include "Slider.h"
#include "src/oscillator/Oscillator.h"
#include "src/oscillator/OscRamp.h"
#include "src/oscillator/OscPulse.h"
#include "src/oscillator/OscTri.h"
#include "src/filter/FastFilter.h"
#include "src/filter/Analog4Poles.h"
#include "src/filter/RCFilter.h"
#include "src/sequencer/FreqSeq.h"

bool     filterActive = true;
int16_t  mute = 1;
double   amp = 0.1;

OscPulse     osc1;
OscTri       osc2;
OscTri       osc3;
FastFilter   glide;
RCFilter     fltr;
FreqSeq      seq;

double map(double value, double iMin, double iMax, double oMin, double oMax) {
	return oMin + (oMax - oMin) * (value - iMin) / (iMax - iMin);
}

void audioCallback(void* udata, uint8_t* stream0, int len) {

	int16_t* stream = (int16_t*) stream0;

	for (len >>= 1; len; len--) {
		// update sequencer
		double f = seq.tic();
		f = glide.tic(f);
		osc1.freq = f;
		osc2.freq = f / 3.01;

		// update oscillators
		double o1 = osc1.tic();
		double o2 = osc2.tic();
		double o3 = osc3.tic();

		// o3 = (1 - o3) / 2;
		o3 = map(o3, -1, 1, 0, 0.5);
		osc1.width = o3;
		osc2.width = o3;

		// simple mix + amplification
		double o = (o1 + o2);

		// apply filter
		// if (filterActive) o = o - fltr.tic(o);
		if (filterActive) o = fltr.tic(o);
		o *= amp;

		// trim overload
		if (o < -1) o = -1;
		if (o >  1) o =  1;

		// convert to output format
		o = 0x8000 * o * mute;

		// write in buffer
		*stream++ = round(o);
	}
}

void playStop() {
	mute = mute == 0 ? 1 : 0;
}

void activateFilter() {
	filterActive = !filterActive;
}

void cutoff(double value) {
	// fltr.freq = value;
	fltr.cutoff = value;
}

void resonance(double value) {
	fltr.reso = value;
}

void changeLfo(double value) {
	osc3.freq = value;
}

int main(int argc, char* argv[]) {

	// initialize MIDI
	HMIDIOUT device;
	int deviceId = 4; // TODO: hardcoded device Id: launchpad
	int midiOutNumDevices = midiOutGetNumDevs();
	int midiInNumDevices  = midiInGetNumDevs();
	if (midiOutOpen(&device, deviceId, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) return 1;
	union {
		uint32_t word;
		uint8_t  data[4];
	} midiMsg;

	midiMsg.data[0] = 0x90; // note-on
	midiMsg.data[1] = 1;    // key number
	midiMsg.data[2] = 78;   // velocity
	midiMsg.data[3] = 0;    // unused
	midiOutShortMsg(device, midiMsg.word);

	// initialize SDL video and audio
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) return 1;

	// make sure SDL cleans up before exit
	atexit(SDL_Quit);

	// init audio
	{
		SDL_AudioSpec audioSpec;
		audioSpec.freq     = SAMPLE_RATE;
		audioSpec.format   = AUDIO_S16;
		audioSpec.channels = 1;
		audioSpec.samples  = 512;
		audioSpec.callback = audioCallback;
		SDL_OpenAudio(&audioSpec, NULL);
	}

	osc3.freq  = 0.03;
	osc3.width = 0.9;
	glide.freq = 0.005;

	SDL_PauseAudio(0); // start audio

	SDL_Surface* screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 16, SDL_HWSURFACE | SDL_DOUBLEBUF);
	SDL_WM_SetCaption("oodio", NULL);
	RenderingContext ctx(screen);
	ctx.backgroundColor(0, 0, 127);

	// load images
	AmsFont font("amstradFont.bmp");
	Button  btn(5, 23, "start/stop");
	Button  btnf(5, 25, "filter");
	Slider  sld(5, 27, 10, 0.1, 2);
	Slider  cut(12, 25, 30, 0.0, 1.0);
	Slider  res(16, 23, 26, 0.0, 0.7);
	btn.onClic(&playStop);
	btnf.onClic(&activateFilter);
	sld.onChange(&changeLfo);
	cut.onChange(&cutoff);
	res.onChange(&resonance);

	font.paper(24); font.pen(1);
	font.print("-------- CONNECTED MIDI OUT DEVICES ---------\n");
	font.paper(1); font.pen(24);
	for (int i = 0; i < midiOutNumDevices; i++) {
		font.print(" ");
		font.printNumber(i);
		font.print(": ");
		MIDIOUTCAPS deviceInfo;
		midiOutGetDevCaps(i, &deviceInfo, sizeof deviceInfo);
		font.print(deviceInfo.szPname);
		font.print("\n");
	}
	font.print("\n");
	font.paper(24); font.pen(1);
	font.print("-------- CONNECTED MIDI IN DEVICES ----------\n");
	font.paper(1); font.pen(24);
	// FIXME: `<=` test because otherwise launchpad does not show up in the list
	for (int i = 0; i <= midiInNumDevices; i++) {
		font.print(" ");
		font.printNumber(i);
		font.print(": ");
		MIDIOUTCAPS deviceInfo;
		midiOutGetDevCaps(i, &deviceInfo, sizeof deviceInfo);
		font.print(deviceInfo.szPname);
		font.print("\n");
	}

	btn.draw(&font);
	btnf.draw(&font);
	font.paper(1);
	font.pen(24);

	// program main loop
	bool run = true;
	while (run) {

		//-------------------------
		// message processing loop
		SDL_Event event;
		while (SDL_PollEvent(&event)) {
			// check for messages
			switch (event.type) {
			case SDL_QUIT:
				// exit if the window is closed
				run = false;
				break;

			// check for keypresses
			case SDL_KEYDOWN:
				// exit if ESCAPE is pressed
				if (event.key.keysym.sym == SDLK_ESCAPE) run = false;
				// start / stop sound with F1 key
				else if (event.key.keysym.sym == SDLK_F1) playStop();
				else if (event.key.keysym.sym == SDLK_F2) osc1.freq = 110;
				else if (event.key.keysym.sym == SDLK_F3) osc1.freq = 440;
				// keyboard
				else if (event.key.keysym.sym <= 256 && event.key.keysym.sym >= 0 ) {
					font.print(event.key.keysym.sym);
				}
				break;

			case SDL_MOUSEMOTION:
				// font.locate(event.motion.x / (8 * PIXEL), event.motion.y / (8 * PIXEL));
				// font.paper(-1);
				// font.print('*');
				sld.move(event.motion.x, event.motion.y);
				cut.move(event.motion.x, event.motion.y);
				res.move(event.motion.x, event.motion.y);
				break;
			case SDL_MOUSEBUTTONDOWN:
				btn.clic(event.button.x, event.button.y);
				btnf.clic(event.button.x, event.button.y);
				sld.clic(event.button.x, event.button.y);
				cut.clic(event.button.x, event.button.y);
				res.clic(event.button.x, event.button.y);
				break;
			case SDL_MOUSEBUTTONUP:
				sld.unclic(event.button.x, event.button.y);
				cut.unclic(event.button.x, event.button.y);
				res.unclic(event.button.x, event.button.y);
				break;
			}
		}

		ctx.clear();
		sld.draw(&font);
		cut.draw(&font);
		res.draw(&font);
		ctx.drawImage(font.getImage(), 0, 0);
		ctx.update();

		SDL_Delay(40); // 25 FPS
	}

	// close audio
	SDL_CloseAudio();

	// close midi
	midiOutReset(device);
	midiOutClose(device);

	return 0;
}
<commit_msg>fix midi-in list<commit_after>#include <stdint.h>
#ifdef __cplusplus
	#include <cstdlib>
#else
	#include <stdlib.h>
#endif
#ifdef __APPLE__
#include <SDL/SDL.h>
#else
#include <SDL.h>
#endif

#include <windows.h>     // required before including mmsystem
#include <mmsystem.h>    // multimedia functions for windows

#define round(x) ((x)>=0?(int16_t)((x)+0.5):(int16_t)((x)-0.5))

#include "constants.h"
#include "RenderingContext.h"
#include "AmsFont.h"
#include "Button.h"
#include "Slider.h"
#include "src/oscillator/Oscillator.h"
#include "src/oscillator/OscRamp.h"
#include "src/oscillator/OscPulse.h"
#include "src/oscillator/OscTri.h"
#include "src/filter/FastFilter.h"
#include "src/filter/Analog4Poles.h"
#include "src/filter/RCFilter.h"
#include "src/sequencer/FreqSeq.h"

bool     filterActive = true;
int16_t  mute = 1;
double   amp = 0.1;

OscPulse     osc1;
OscTri       osc2;
OscTri       osc3;
FastFilter   glide;
RCFilter     fltr;
FreqSeq      seq;

double map(double value, double iMin, double iMax, double oMin, double oMax) {
	return oMin + (oMax - oMin) * (value - iMin) / (iMax - iMin);
}

void audioCallback(void* udata, uint8_t* stream0, int len) {

	int16_t* stream = (int16_t*) stream0;

	for (len >>= 1; len; len--) {
		// update sequencer
		double f = seq.tic();
		f = glide.tic(f);
		osc1.freq = f;
		osc2.freq = f / 3.01;

		// update oscillators
		double o1 = osc1.tic();
		double o2 = osc2.tic();
		double o3 = osc3.tic();

		// o3 = (1 - o3) / 2;
		o3 = map(o3, -1, 1, 0, 0.5);
		osc1.width = o3;
		osc2.width = o3;

		// simple mix + amplification
		double o = (o1 + o2);

		// apply filter
		// if (filterActive) o = o - fltr.tic(o);
		if (filterActive) o = fltr.tic(o);
		o *= amp;

		// trim overload
		if (o < -1) o = -1;
		if (o >  1) o =  1;

		// convert to output format
		o = 0x8000 * o * mute;

		// write in buffer
		*stream++ = round(o);
	}
}

void playStop() {
	mute = mute == 0 ? 1 : 0;
}

void activateFilter() {
	filterActive = !filterActive;
}

void cutoff(double value) {
	// fltr.freq = value;
	fltr.cutoff = value;
}

void resonance(double value) {
	fltr.reso = value;
}

void changeLfo(double value) {
	osc3.freq = value;
}

int main(int argc, char* argv[]) {

	// initialize MIDI
	HMIDIOUT device;
	int deviceId = 4; // TODO: hardcoded device Id: launchpad
	int midiOutNumDevices = midiOutGetNumDevs();
	int midiInNumDevices  = midiInGetNumDevs();
	if (midiOutOpen(&device, deviceId, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) return 1;
	union {
		uint32_t word;
		uint8_t  data[4];
	} midiMsg;

	midiMsg.data[0] = 0x90; // note-on
	midiMsg.data[1] = 1;    // key number
	midiMsg.data[2] = 78;   // velocity
	midiMsg.data[3] = 0;    // unused
	midiOutShortMsg(device, midiMsg.word);

	// initialize SDL video and audio
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) return 1;

	// make sure SDL cleans up before exit
	atexit(SDL_Quit);

	// init audio
	{
		SDL_AudioSpec audioSpec;
		audioSpec.freq     = SAMPLE_RATE;
		audioSpec.format   = AUDIO_S16;
		audioSpec.channels = 1;
		audioSpec.samples  = 512;
		audioSpec.callback = audioCallback;
		SDL_OpenAudio(&audioSpec, NULL);
	}

	osc3.freq  = 0.03;
	osc3.width = 0.9;
	glide.freq = 0.005;

	SDL_PauseAudio(0); // start audio

	SDL_Surface* screen = SDL_SetVideoMode(SCREEN_W, SCREEN_H, 16, SDL_HWSURFACE | SDL_DOUBLEBUF);
	SDL_WM_SetCaption("oodio", NULL);
	RenderingContext ctx(screen);
	ctx.backgroundColor(0, 0, 127);

	// load images
	AmsFont font("amstradFont.bmp");
	Button  btn(5, 23, "start/stop");
	Button  btnf(5, 25, "filter");
	Slider  sld(5, 27, 10, 0.1, 2);
	Slider  cut(12, 25, 30, 0.0, 1.0);
	Slider  res(16, 23, 26, 0.0, 0.7);
	btn.onClic(&playStop);
	btnf.onClic(&activateFilter);
	sld.onChange(&changeLfo);
	cut.onChange(&cutoff);
	res.onChange(&resonance);

	font.paper(24); font.pen(1);
	font.print("-------- CONNECTED MIDI OUT DEVICES ---------\n");
	font.paper(1); font.pen(24);
	for (int i = 0; i < midiOutNumDevices; i++) {
		font.print(" ");
		font.printNumber(i);
		font.print(": ");
		MIDIOUTCAPS deviceInfo;
		midiOutGetDevCaps(i, &deviceInfo, sizeof deviceInfo);
		font.print(deviceInfo.szPname);
		font.print("\n");
	}
	font.print("\n");

	font.paper(24); font.pen(1);
	font.print("-------- CONNECTED MIDI IN DEVICES ----------\n");
	font.paper(1); font.pen(24);
	for (int i = 0; i < midiInNumDevices; i++) {
		font.print(" ");
		font.printNumber(i);
		font.print(": ");
		MIDIINCAPS deviceInfo;
		midiInGetDevCaps(i, &deviceInfo, sizeof deviceInfo);
		font.print(deviceInfo.szPname);
		font.print("\n");
	}

	btn.draw(&font);
	btnf.draw(&font);
	font.paper(1);
	font.pen(24);

	// program main loop
	bool run = true;
	while (run) {

		//-------------------------
		// message processing loop
		SDL_Event event;
		while (SDL_PollEvent(&event)) {
			// check for messages
			switch (event.type) {
			case SDL_QUIT:
				// exit if the window is closed
				run = false;
				break;

			// check for keypresses
			case SDL_KEYDOWN:
				// exit if ESCAPE is pressed
				if (event.key.keysym.sym == SDLK_ESCAPE) run = false;
				// start / stop sound with F1 key
				else if (event.key.keysym.sym == SDLK_F1) playStop();
				else if (event.key.keysym.sym == SDLK_F2) osc1.freq = 110;
				else if (event.key.keysym.sym == SDLK_F3) osc1.freq = 440;
				// keyboard
				else if (event.key.keysym.sym <= 256 && event.key.keysym.sym >= 0 ) {
					font.print(event.key.keysym.sym);
				}
				break;

			case SDL_MOUSEMOTION:
				// font.locate(event.motion.x / (8 * PIXEL), event.motion.y / (8 * PIXEL));
				// font.paper(-1);
				// font.print('*');
				sld.move(event.motion.x, event.motion.y);
				cut.move(event.motion.x, event.motion.y);
				res.move(event.motion.x, event.motion.y);
				break;
			case SDL_MOUSEBUTTONDOWN:
				btn.clic(event.button.x, event.button.y);
				btnf.clic(event.button.x, event.button.y);
				sld.clic(event.button.x, event.button.y);
				cut.clic(event.button.x, event.button.y);
				res.clic(event.button.x, event.button.y);
				break;
			case SDL_MOUSEBUTTONUP:
				sld.unclic(event.button.x, event.button.y);
				cut.unclic(event.button.x, event.button.y);
				res.unclic(event.button.x, event.button.y);
				break;
			}
		}

		ctx.clear();
		sld.draw(&font);
		cut.draw(&font);
		res.draw(&font);
		ctx.drawImage(font.getImage(), 0, 0);
		ctx.update();

		SDL_Delay(40); // 25 FPS
	}

	// close audio
	SDL_CloseAudio();

	// close midi
	midiOutReset(device);
	midiOutClose(device);

	return 0;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <stdio.h>
#include "colourConverter.h"

#define TEST_VALS 4
using namespace std;

bool sameColour(float *, float *);
void diag(float*);

int main()
{
    bool pass = true; // Used to detect failures and print diagnostics
    float h = 0.0, c = 0.75, l = 0.5;
    float rgb[3] = {0, 0, 0}; // Result var
    float expected[3] = {0, 0, 0};

    float testHues[TEST_VALS] = {0.0, 30.0, 75.0, 120.0};
    float testChromas[TEST_VALS] = {0.0, 1.0, 1.0, 1.0};
    float testSats[TEST_VALS] = {0.0, 1.0, 1.0, 1.0};
    float testLumas[TEST_VALS] = {0.0, 0.25, 0.75, 1.0};
    float testHPrimes[TEST_VALS] = {0.0, 0.5, 1.0, 1.5};

    float expectedReds[TEST_VALS] = {1.0, 0.0, 0.0, 0.0};
    float expectedGreens[TEST_VALS] = {0.0, 0.0, 0.0, 0.0};
    float expectedBlues[TEST_VALS] = {0.0, 0.0, 0.0, 0.0};
    float expectedMinorComp[TEST_VALS] = {0.0, 0.5, 1.0, 0.5};

    colourConverter cc;

    colourConverter::hcl_to_rgb(h, c, l, rgb);

    // smoke test, ensure rgb is modified.
    if (rgb[0] == 0) {
        printf("FAIL - values not modified.\n");
    }

    for (int i = 0; i < TEST_VALS; i++) {
        // Test minor component
        float actualResult;
        actualResult = cc.minorComponent(testHPrimes[i], 1.0);
        if (expectedMinorComp[i] != actualResult) {
            printf("FAIL - Expected MC: %f, actual MC: %f\n",
                expectedMinorComp[i], actualResult);
            pass = false;
            break;
        }

        // Test conversion from HCL
        cc.hcl_to_rgb(testHues[i], testChromas[i], testLumas[i], rgb);
        expected[0] = expectedReds[i];
        expected[1] = expectedGreens[i];
        expected[2] = expectedBlues[i];
        if (!sameColour(expected, rgb)) {
            printf("FAIL - HCL to RGB conversion. Expected:\n");
            diag(expected);
            printf("Actual:\n");
            pass = false;
            break;
        }

        // Ensure values are in correct range
        for (int i = 0; i < 3; i++) {
            if (rgb[i] < 0 || rgb[i] > 1) {
                printf("FAIL - Comp '%d', outside range [0, 1.0] at %f.\n",
                    i, rgb[i]);
                pass = false;
                break;
            }
        }
        if (!pass) {
            break;
        }
    }

    if (!pass) {
        diag(rgb);
    }

    if (pass) {
        printf("SUCCESS!");
        return 0;
    }

    return 1;
}

/**
 * @brief Compares two float triplets
 * @param expected First float triplet (colour)
 * @param actual Secondi float triplet
 * @return true if triplets are the same, false otherwise
 */
bool sameColour(float *expected, float *actual) {
    float result = true;
    for (int i = 0; i < 3; i++) {
        if (expected[i] != actual[i]) {
            result = false;
            break;
        }
    }
    return result;
}

void diag(float *rgb) {
    printf("Red: %d\n", int(rgb[0]*255));
    printf("Green: %d\n", int(rgb[1]*255));
    printf("Blue: %d\n", int(rgb[2]*255));
}
<commit_msg>Diagnostics output converts to Ints with math rounding.<commit_after>#include <iostream>
#include <string>
#include <stdio.h>
#include "colourConverter.h"

#define TEST_VALS 4
using namespace std;

bool sameColour(float *, float *);
void diag(float*);

int main()
{
    bool pass = true; // Used to detect failures and print diagnostics
    float h = 0.0, c = 0.75, l = 0.5;
    float rgb[3] = {0, 0, 0}; // Result var
    float expected[3] = {0, 0, 0};

    float testHues[TEST_VALS] = {0.0, 30.0, 75.0, 120.0};
    float testChromas[TEST_VALS] = {1.0, 1.0, 1.0, 1.0};
    float testSats[TEST_VALS] = {0.0, 1.0, 1.0, 1.0};
    float testLumas[TEST_VALS] = {0.3, 0.25, 0.75, 1.0};
    float testHPrimes[TEST_VALS] = {0.0, 0.5, 1.0, 1.5};

    float expectedReds[TEST_VALS] = {1.0, 0.0, 0.0, 0.0};
    float expectedGreens[TEST_VALS] = {0.0, 0.0, 0.0, 0.0};
    float expectedBlues[TEST_VALS] = {0.0, 0.0, 0.0, 0.0};
    float expectedMinorComp[TEST_VALS] = {0.0, 0.5, 1.0, 0.5};

    colourConverter cc;

    colourConverter::hcl_to_rgb(h, c, l, rgb);

    // smoke test, ensure rgb is modified.
    if (rgb[0] == 0) {
        printf("FAIL - values not modified.\n");
    }

    for (int i = 0; i < TEST_VALS; i++) {
        // Test minor component
        float actualResult;
        actualResult = cc.minorComponent(testHPrimes[i], 1.0);
        if (expectedMinorComp[i] != actualResult) {
            printf("FAIL - Expected MC: %f, actual MC: %f\n",
                expectedMinorComp[i], actualResult);
            pass = false;
            break;
        }

        // Test conversion from HCL
        cc.hcl_to_rgb(testHues[i], testChromas[i], testLumas[i], rgb);
        expected[0] = expectedReds[i];
        expected[1] = expectedGreens[i];
        expected[2] = expectedBlues[i];
        if (!sameColour(expected, rgb)) {
            printf("FAIL - HCL to RGB conversion. Expected:\n");
            diag(expected);
            printf("Actual:\n");
            pass = false;
            break;
        }

        // Ensure values are in correct range
        for (int i = 0; i < 3; i++) {
            if (rgb[i] < 0 || rgb[i] > 1) {
                printf("FAIL - Comp '%d', outside range [0, 1.0] at %f.\n",
                    i, rgb[i]);
                pass = false;
                break;
            }
        }
        if (!pass) {
            break;
        }
    }

    if (!pass) {
        diag(rgb);
    }

    if (pass) {
        printf("SUCCESS!");
        return 0;
    }

    return 1;
}

/**
 * @brief Compares two float triplets
 * @param expected First float triplet (colour)
 * @param actual Secondi float triplet
 * @return true if triplets are the same, false otherwise
 */
bool sameColour(float *expected, float *actual) {
    float result = true;
    for (int i = 0; i < 3; i++) {
        if (expected[i] != actual[i]) {
            result = false;
            break;
        }
    }
    return result;
}

void diag(float *rgb) {
    // Transform to ints with appropriate rounding.
    int r, g, b;
    r = int(rgb[0]*255);
    g = int(rgb[1]*255);
    b = int(rgb[2]*255);

    if (rgb[0] - r >= 0.5)
        r++;
    if (rgb[1] - g >= 0.5)
        g++;
    if (rgb[2] - b >= 0.5)
        b++;

    printf("Red: %d\n", r);
    printf("Green: %d\n", g);
    printf("Blue: %d\n", b);
}
<|endoftext|>


<commit_before>#include "mainwindow.h"
#include "Factory.h"
#include "Product.h"
#include <QApplication>
#include "DatabaseManipulator.h"

#include <QCoreApplication>
#include <QApplication>
#include <QFile>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QFile styleFile("darkorange.stylesheet.txt");
    styleFile.open(QFile::ReadOnly);
    QByteArray bytes = styleFile.readAll();
    QApplication *app = (QApplication*)QApplication::instance();
    app->setStyleSheet(bytes);

    MainWindow w;
    w.setFixedSize(w.width(),w.height());
    w.show();

    /*DatabaseManipulator *db = new DatabaseManipulator();
    //
    db->getProduct("Uniform");
    db->getProductNames();
    db->upgradeProduct("Uniform");
    */

    return a.exec();

 }

<commit_msg>main change<commit_after>#include "mainwindow.h"
#include "Factory.h"
#include "Product.h"
#include <QApplication>
#include "DatabaseManipulator.h"

#include <QCoreApplication>
#include <QApplication>
#include <QFile>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QFile styleFile("../WithoutThem/darkorange.stylesheet.txt");
    styleFile.open(QFile::ReadOnly);
    QByteArray bytes = styleFile.readAll();
    QApplication *app = (QApplication*)QApplication::instance();
    app->setStyleSheet(bytes);

    MainWindow w;
    w.setFixedSize(w.width(),w.height());
    w.show();

    /*DatabaseManipulator *db = new DatabaseManipulator();
    //
    db->getProduct("Uniform");
    db->getProductNames();
    db->upgradeProduct("Uniform");
    */

    return a.exec();

 }

<|endoftext|>


<commit_before>/*
VROOM (Vehicle Routing Open-source Optimization Machine)
Copyright (C) 2015, Julien Coupey

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "tsp.h"

tsp::tsp(const cl_args_t& cl_args):
  _matrix(0){

  // Computing matrix with the right tool.
  assert((!cl_args.use_osrm) or (!cl_args.use_tsplib));

  // Exactly one of the two following is true.
  if(cl_args.use_osrm){
    _loader
      = std::make_unique<osrm_wrapper>(cl_args.osrm_address,
                                       cl_args.osrm_port,
                                       cl_args.input);
  }
  if(cl_args.use_tsplib){
    _loader = std::make_unique<tsplib_loader>(cl_args.input);
  }

  _matrix = _loader->get_matrix();
  _is_symmetric = _matrix.is_symmetric();
}

tsp::tsp(const matrix<distance_t>& m):
  _matrix(m),
  _is_symmetric(_matrix.is_symmetric()) {}

const matrix<distance_t>& tsp::get_matrix() const{
  return _matrix;
}

const matrix<distance_t> tsp::get_symmetrized_matrix() const{
  if(_is_symmetric){
    return _matrix;
  }
  else{
    matrix<distance_t> m {_matrix.size()};
    for(index_t i = 0; i < m.size(); ++i){
      m[i][i] = _matrix[i][i];
      for(index_t j = i + 1; j < m.size(); ++j){
        distance_t max = std::max(_matrix[i][j], _matrix[j][i]);
        m[i][j] = max;
        m[j][i] = max;
      }
    }
    return m;
  }
}

const bool tsp::is_symmetric() const{
  return _is_symmetric;
}

std::size_t tsp::size(){
  return _matrix.size();
}

distance_t tsp::cost(const std::list<index_t>& tour) const{
  distance_t cost = 0;
  index_t init_step = 0;        // Initialization actually never used.

  auto step = tour.cbegin();
  if(tour.size() > 0){
    init_step = *step;
  }

  index_t previous_step = init_step;
  ++step;
  for(; step != tour.cend(); ++step){
    cost += _matrix[previous_step][*step];
    previous_step = *step;
  }
  if(tour.size() > 0){
    cost += _matrix[previous_step][init_step];
  }
  return cost;
}

std::string tsp::get_route(const std::list<index_t>& tour) const{
  return _loader->get_route(tour);
}

std::string tsp::get_route_geometry(const std::list<index_t>& tour) const{
  return _loader->get_route_geometry(tour);
}
<commit_msg>Change problem symmetrization. #8<commit_after>/*
VROOM (Vehicle Routing Open-source Optimization Machine)
Copyright (C) 2015, Julien Coupey

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "tsp.h"

tsp::tsp(const cl_args_t& cl_args):
  _matrix(0){

  // Computing matrix with the right tool.
  assert((!cl_args.use_osrm) or (!cl_args.use_tsplib));

  // Exactly one of the two following is true.
  if(cl_args.use_osrm){
    _loader
      = std::make_unique<osrm_wrapper>(cl_args.osrm_address,
                                       cl_args.osrm_port,
                                       cl_args.input);
  }
  if(cl_args.use_tsplib){
    _loader = std::make_unique<tsplib_loader>(cl_args.input);
  }

  _matrix = _loader->get_matrix();
  _is_symmetric = _matrix.is_symmetric();
}

tsp::tsp(const matrix<distance_t>& m):
  _matrix(m),
  _is_symmetric(_matrix.is_symmetric()) {}

const matrix<distance_t>& tsp::get_matrix() const{
  return _matrix;
}

const matrix<distance_t> tsp::get_symmetrized_matrix() const{
  if(_is_symmetric){
    return _matrix;
  }
  else{
    matrix<distance_t> m {_matrix.size()};
    for(index_t i = 0; i < m.size(); ++i){
      m[i][i] = _matrix[i][i];
      for(index_t j = i + 1; j < m.size(); ++j){
        distance_t min = std::min(_matrix[i][j], _matrix[j][i]);
        m[i][j] = min;
        m[j][i] = min;
      }
    }
    return m;
  }
}

const bool tsp::is_symmetric() const{
  return _is_symmetric;
}

std::size_t tsp::size(){
  return _matrix.size();
}

distance_t tsp::cost(const std::list<index_t>& tour) const{
  distance_t cost = 0;
  index_t init_step = 0;        // Initialization actually never used.

  auto step = tour.cbegin();
  if(tour.size() > 0){
    init_step = *step;
  }

  index_t previous_step = init_step;
  ++step;
  for(; step != tour.cend(); ++step){
    cost += _matrix[previous_step][*step];
    previous_step = *step;
  }
  if(tour.size() > 0){
    cost += _matrix[previous_step][init_step];
  }
  return cost;
}

std::string tsp::get_route(const std::list<index_t>& tour) const{
  return _loader->get_route(tour);
}

std::string tsp::get_route_geometry(const std::list<index_t>& tour) const{
  return _loader->get_route_geometry(tour);
}
<|endoftext|>


<commit_before>#include <iostream>
#include <memory>
#include <fstream>
#include <cctype>

#include "Game/Game.h"
#include "Game/Board.h"
#include "Game/Game_Result.h"

#include "Players/Genetic_AI.h"
#include "Players/Human_Player.h"
#include "Players/Random_AI.h"
#include "Players/Outside_Player.h"

#include "Genes/Gene_Pool.h"
#include "Stalemate_Search.h"

#include "Exceptions/Illegal_Move_Exception.h"

#include "Utility.h"
#include "Testing.h"

void print_help();
void replay_game(const std::string& file_name, int game_number);
int find_last_id(const std::string& filename);

int main(int argc, char *argv[])
{
    try
    {
        run_tests();

        if(argc > 1)
        {
            if(std::string(argv[1]) == "-genepool")
            {
                std::string gene_pool_config_file_name;
                if(argc > 2)
                {
                    gene_pool_config_file_name = argv[2];
                }

                gene_pool(gene_pool_config_file_name);
            }
            else if(std::string(argv[1]) == "-replay")
            {
                if(argc > 2)
                {
                    std::string file_name = argv[2];
                    int game_number = -1;
                    if(argc > 3)
                    {
                        game_number = std::stoi(argv[3]);
                    }
                    replay_game(file_name, game_number);
                }
                else
                {
                    std::cout << "Provide a file containing a game to replay." << std::endl;
                    return 1;
                }
            }
            else if(std::string(argv[1]) == "-stalemate")
            {
                stalemate_search_start();
            }
            else
            {
                // Use pointers since each player could be Human, Genetic, Random, etc.
                std::unique_ptr<Player> white;
                std::unique_ptr<Player> black;
                std::unique_ptr<Player> latest;

                double game_time = 0;
                int moves_per_reset = 0;
                double increment_time = 0;

                for(int i = 1; i < argc; ++i)
                {
                    std::string opt = argv[i];
                    if(opt == "-human")
                    {
                        latest = std::make_unique<Human_Player>();
                    }
                    else if(opt == "-random")
                    {
                        latest = std::make_unique<Random_AI>();
                    }
                    else if(opt == "-genetic")
                    {
                        std::string filename;
                        int id = -1;
                        if(i + 1 < argc)
                        {
                            filename = argv[i+1];
                            if(filename.front() == '-')
                            {
                                filename.clear();
                            }
                        }

                        if(i + 2 < argc)
                        {
                            try
                            {
                                id = std::stoi(argv[i+2]);
                            }
                            catch(const std::exception&)
                            {
                            }
                        }

                        if(filename.empty())
                        {
                            latest = std::make_unique<Genetic_AI>();
                            for(int j = 0; j < 100; ++j)
                            {
                                static_cast<Genetic_AI*>(latest.get())->mutate();
                            }
                            static_cast<Genetic_AI*>(latest.get())->print_genome("single_game_player.txt");
                        }
                        else
                        {
                            if(id < 0)
                            {
                                latest = std::make_unique<Genetic_AI>(filename, find_last_id(filename));
                                i += 1;
                            }
                            else
                            {
                                latest = std::make_unique<Genetic_AI>(filename, id);
                                i += 2;
                            }
                        }
                    }
                    else if(opt == "-time")
                    {
                        game_time = std::stod(argv[++i]);
                    }
                    else if(opt == "-reset_moves")
                    {
                        moves_per_reset = std::stoi(argv[++i]);
                    }
                    else if(opt == "-increment_time")
                    {
                        increment_time = std::stod(argv[++i]);
                    }
                    else
                    {
                        throw std::runtime_error("Invalid option: " + opt);
                    }

                    if(latest)
                    {
                        if( ! white)
                        {
                            white = std::move(latest);
                        }
                        else if( ! black)
                        {
                            black = std::move(latest);
                        }
                        else
                        {
                            throw std::runtime_error("More than two players specified.");
                        }
                    }
                }

                std::string game_file_name = "game.pgn";

                if(black)
                {
                    play_game(*white, *black, game_time, moves_per_reset, increment_time, game_file_name);
                }
                else
                {
                    auto outside = connect_to_outside(*white);
                    game_time = outside->get_game_time();
                    moves_per_reset = outside->get_reset_moves();
                    increment_time = outside->get_increment();

                    if(outside->get_ai_color() == WHITE)
                    {
                        play_game(*white, *outside, game_time, moves_per_reset, increment_time, game_file_name);
                    }
                    else
                    {
                        play_game(*outside, *white, game_time, moves_per_reset, increment_time, game_file_name);
                    }
                }
            }
        }
        else
        {
            print_help();
        }
    }
    catch(const std::exception& e)
    {
        std::cerr << "\n\nERROR: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

void print_help()
{
    std::cout << "\n\nGenetic Chess" << std::endl
              << "=============" << std::endl << std::endl
              << "Options:" << std::endl
              << "\t-genepool [file name]" << std::endl
              << "\t\tStart a run of a gene pool with parameters set in the given\n\t\tfile name." << std::endl << std::endl
              << "\t-replay [filename]" << std::endl
              << "\t\tStep through a PGN game file, drawing the board after each\n\t\tmove with an option to begin playing at any time." << std::endl << std::endl
              << "\t-stalemate" << std::endl
              << "\t\tBegin a search for the shortest possible stalemate--i.e., where\n\t\tthe player to move has no legal moves. Estimated time to\n\t\tcompletion: months." << std::endl << std::endl
              << "The following options start a game with various players. If two players are\nspecified, the first plays white and the second black. If only one player is\nspecified, the program will wait for a CECP command from outside to start\nplaying." << std::endl << std::endl
              << "\t-human" << std::endl
              << "\t\tSpecify a human player for a game." << std::endl << std::endl
              << "\t-genetic [filename [number]]" << std::endl
              << "\t\tSpecify a genetic AI player for a game. Optional file name and\n\t\tID number to load an AI from a file." << std::endl << std::endl
              << "\t-random" << std::endl
              << "\t\tSpecify a player that makes random moves for a game." << std::endl << std::endl
              << "Other game options:" << std::endl << std::endl
              << "\t-time [number]" << std::endl
              << "\t\tSpecify the time (in seconds) each player has to play the game\n\t\tor to make a set number of moves (see -reset_moves option)." << std::endl << std::endl
              << "\t-reset_moves [number]" << std::endl
              << "\t\tSpecify the number of moves a player must make within the time\n\t\tlimit. The clock resets to the initial time every time this\n\t\tnumber of moves is made." << std::endl << std::endl
              << "\t-increment_time [number]" << std::endl
              << "\t\tSpecify seconds to add to time after each move." << std::endl << std::endl;
}

int find_last_id(const std::string& players_file_name)
{
    auto pools = load_gene_pool_file(players_file_name);
    int smallest_id = -1;
    if( ! pools.empty())
    {
        smallest_id = pools.front().front().get_id();
        for(const auto& pool : pools)
        {
            for(const auto& player : pool)
            {
                if(player.get_id() < smallest_id)
                {
                    smallest_id = player.get_id();
                }
            }
        }
    }

    if(smallest_id != -1)
    {
        return smallest_id;
    }

    std::ifstream player_input(players_file_name);
    std::string line;
    int last_player = -1;
    while(std::getline(player_input, line))
    {
        if(String::starts_with(line, "ID:"))
        {
            last_player = std::stoi(String::split(line).back());
        }
    }

    // Filter out players with zero wins
    auto games_file = players_file_name + "_games.txt";
    std::ifstream games_input(games_file);
    std::map<int, int> win_count;
    int best_id = -1;
    while(std::getline(games_input, line))
    {
        int white_id = -1;
        int black_id = -1;
        if(String::starts_with(line, "[White"))
        {
            white_id = std::stoi(String::split(String::split(line, "\"")[1])[2]);
        }
        else if(String::starts_with(line, "[Black"))
        {
            black_id = std::stoi(String::split(String::split(line, "\"")[1])[2]);
        }
        else if(String::starts_with(line, "[Result"))
        {
            auto result_string = String::split(line, "\"")[1];
            auto winning_id = -1;
            if(result_string == "1-0")
            {
                winning_id = white_id;
            }
            else if(result_string == "0-1")
            {
                winning_id = black_id;
            }

            int wins_so_far = ++win_count[winning_id];
            if(wins_so_far >= 3 && winning_id > best_id)
            {
                best_id = winning_id;
            }
        }
    }

    if(best_id == -1)
    {
        return last_player;
    }
    else
    {
        return best_id;
    }
}

void replay_game(const std::string& file_name, int game_number)
{
    std::ifstream ifs(file_name);
    std::vector<std::string> game_headers;
    std::string line;
    if(game_number >= 0)
    {
        // fast forward to indicated game
        while(std::getline(ifs, line))
        {
            line = String::trim_outer_whitespace(line);
            if(String::starts_with(line, '['))
            {
                game_headers.push_back(line);
            }
            else
            {
                game_headers.clear();
            }

            if(String::starts_with(line, "[Round"))
            {
                auto number = std::stoi(String::split(line, "\"")[1]);
                if(number == game_number)
                {
                    break;
                }
            }
        }

        if( ! ifs)
        {
            std::cout << "No game with ID number " << game_number << " found." << std::endl;
        }

        for(const auto& header : game_headers)
        {
            std::cout << header << std::endl;
        }
    }

    Board board;
    Game_Result result;
    bool game_started = false;
    while( ! result.game_has_ended() && std::getline(ifs, line))
    {
        line = String::strip_block_comment(line, '{', '}');
        line = String::strip_comments(line, ';');
        if(line.empty())
        {
            if(game_started)
            {
                break;
            }
            else
            {
                continue;
            }
        }
        if(line[0] == '[') // header lines
        {
            std::cout << line << std::endl;
            continue;
        }

        for(const auto& s : String::split(line))
        {
            try
            {
                result = board.submit_move(board.get_move(s));

                board.ascii_draw(WHITE);
                game_started = true;
                std::cout << "Last move: ";
                std::cout << (board.get_game_record().size() + 1)/2 << ". ";
                std::cout << (board.whose_turn() == WHITE ? "... " : "");
                std::cout << board.get_game_record().back()->coordinate_move() << std::endl;
                if(result.game_has_ended())
                {
                    std::cout << result.get_ending_reason() << std::endl;
                    break;
                }

                std::cout << "Enter \"y\" to play game from here: " << std::endl;
                char response = std::cin.get();
                if(std::tolower(response) == 'y')
                {
                    play_game_with_board(Human_Player(),
                                         Human_Player(),
                                         0,
                                         0,
                                         0,
                                         file_name + "_continued.pgn",
                                         board);
                     break;
                }
            }
            catch(const Illegal_Move_Exception&)
            {
                std::cout << "Ignoring: " << s << std::endl;
                continue;
            }
        }
    }
}
<commit_msg>Correct help text<commit_after>#include <iostream>
#include <memory>
#include <fstream>
#include <cctype>

#include "Game/Game.h"
#include "Game/Board.h"
#include "Game/Game_Result.h"

#include "Players/Genetic_AI.h"
#include "Players/Human_Player.h"
#include "Players/Random_AI.h"
#include "Players/Outside_Player.h"

#include "Genes/Gene_Pool.h"
#include "Stalemate_Search.h"

#include "Exceptions/Illegal_Move_Exception.h"

#include "Utility.h"
#include "Testing.h"

void print_help();
void replay_game(const std::string& file_name, int game_number);
int find_last_id(const std::string& filename);

int main(int argc, char *argv[])
{
    try
    {
        run_tests();

        if(argc > 1)
        {
            if(std::string(argv[1]) == "-genepool")
            {
                std::string gene_pool_config_file_name;
                if(argc > 2)
                {
                    gene_pool_config_file_name = argv[2];
                }

                gene_pool(gene_pool_config_file_name);
            }
            else if(std::string(argv[1]) == "-replay")
            {
                if(argc > 2)
                {
                    std::string file_name = argv[2];
                    int game_number = -1;
                    if(argc > 3)
                    {
                        game_number = std::stoi(argv[3]);
                    }
                    replay_game(file_name, game_number);
                }
                else
                {
                    std::cout << "Provide a file containing a game to replay." << std::endl;
                    return 1;
                }
            }
            else if(std::string(argv[1]) == "-stalemate")
            {
                stalemate_search_start();
            }
            else
            {
                // Use pointers since each player could be Human, Genetic, Random, etc.
                std::unique_ptr<Player> white;
                std::unique_ptr<Player> black;
                std::unique_ptr<Player> latest;

                double game_time = 0;
                int moves_per_reset = 0;
                double increment_time = 0;

                for(int i = 1; i < argc; ++i)
                {
                    std::string opt = argv[i];
                    if(opt == "-human")
                    {
                        latest = std::make_unique<Human_Player>();
                    }
                    else if(opt == "-random")
                    {
                        latest = std::make_unique<Random_AI>();
                    }
                    else if(opt == "-genetic")
                    {
                        std::string filename;
                        int id = -1;
                        if(i + 1 < argc)
                        {
                            filename = argv[i+1];
                            if(filename.front() == '-')
                            {
                                filename.clear();
                            }
                        }

                        if(i + 2 < argc)
                        {
                            try
                            {
                                id = std::stoi(argv[i+2]);
                            }
                            catch(const std::exception&)
                            {
                            }
                        }

                        if(filename.empty())
                        {
                            latest = std::make_unique<Genetic_AI>();
                            for(int j = 0; j < 100; ++j)
                            {
                                static_cast<Genetic_AI*>(latest.get())->mutate();
                            }
                            static_cast<Genetic_AI*>(latest.get())->print_genome("single_game_player.txt");
                        }
                        else
                        {
                            if(id < 0)
                            {
                                latest = std::make_unique<Genetic_AI>(filename, find_last_id(filename));
                                i += 1;
                            }
                            else
                            {
                                latest = std::make_unique<Genetic_AI>(filename, id);
                                i += 2;
                            }
                        }
                    }
                    else if(opt == "-time")
                    {
                        game_time = std::stod(argv[++i]);
                    }
                    else if(opt == "-reset_moves")
                    {
                        moves_per_reset = std::stoi(argv[++i]);
                    }
                    else if(opt == "-increment_time")
                    {
                        increment_time = std::stod(argv[++i]);
                    }
                    else
                    {
                        throw std::runtime_error("Invalid option: " + opt);
                    }

                    if(latest)
                    {
                        if( ! white)
                        {
                            white = std::move(latest);
                        }
                        else if( ! black)
                        {
                            black = std::move(latest);
                        }
                        else
                        {
                            throw std::runtime_error("More than two players specified.");
                        }
                    }
                }

                std::string game_file_name = "game.pgn";

                if(black)
                {
                    play_game(*white, *black, game_time, moves_per_reset, increment_time, game_file_name);
                }
                else
                {
                    auto outside = connect_to_outside(*white);
                    game_time = outside->get_game_time();
                    moves_per_reset = outside->get_reset_moves();
                    increment_time = outside->get_increment();

                    if(outside->get_ai_color() == WHITE)
                    {
                        play_game(*white, *outside, game_time, moves_per_reset, increment_time, game_file_name);
                    }
                    else
                    {
                        play_game(*outside, *white, game_time, moves_per_reset, increment_time, game_file_name);
                    }
                }
            }
        }
        else
        {
            print_help();
        }
    }
    catch(const std::exception& e)
    {
        std::cerr << "\n\nERROR: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

void print_help()
{
    std::cout << "\n\nGenetic Chess" << std::endl
              << "=============" << std::endl << std::endl
              << "Options:" << std::endl
              << "\t-genepool [file name]" << std::endl
              << "\t\tStart a run of a gene pool with parameters set in the given\n\t\tfile name." << std::endl << std::endl
              << "\t-replay [filename]" << std::endl
              << "\t\tStep through a PGN game file, drawing the board after each\n\t\tmove with an option to begin playing at any time." << std::endl << std::endl
              << "\t-stalemate" << std::endl
              << "\t\tBegin a search for the shortest possible stalemate--i.e., where\n\t\tthe player to move has no legal moves. Estimated time to\n\t\tcompletion: months." << std::endl << std::endl
              << "The following options start a game with various players. If two players are\nspecified, the first plays white and the second black. If only one player is\nspecified, the program will wait for a CECP command from outside to start\nplaying." << std::endl << std::endl
              << "\t-human" << std::endl
              << "\t\tSpecify a human player for a game." << std::endl << std::endl
              << "\t-genetic [filename [number]]" << std::endl
              << "\t\tSpecify a genetic AI player for a game. Optional file name and\n\t\tID number to load an AI from a file." << std::endl << std::endl
              << "\t-random" << std::endl
              << "\t\tSpecify a player that makes random moves for a game." << std::endl << std::endl
              << "Other game options:" << std::endl << std::endl
              << "\t-time [number]" << std::endl
              << "\t\tSpecify the time (in seconds) each player has to play the game\n\t\tor to make a set number of moves (see -reset_moves option)." << std::endl << std::endl
              << "\t-reset_moves [number]" << std::endl
              << "\t\tSpecify the number of moves a player must make within the time\n\t\tlimit. The clock adds the initial time every time this\n\t\tnumber of moves is made." << std::endl << std::endl
              << "\t-increment_time [number]" << std::endl
              << "\t\tSpecify seconds to add to time after each move." << std::endl << std::endl;
}

int find_last_id(const std::string& players_file_name)
{
    auto pools = load_gene_pool_file(players_file_name);
    int smallest_id = -1;
    if( ! pools.empty())
    {
        smallest_id = pools.front().front().get_id();
        for(const auto& pool : pools)
        {
            for(const auto& player : pool)
            {
                if(player.get_id() < smallest_id)
                {
                    smallest_id = player.get_id();
                }
            }
        }
    }

    if(smallest_id != -1)
    {
        return smallest_id;
    }

    std::ifstream player_input(players_file_name);
    std::string line;
    int last_player = -1;
    while(std::getline(player_input, line))
    {
        if(String::starts_with(line, "ID:"))
        {
            last_player = std::stoi(String::split(line).back());
        }
    }

    // Filter out players with zero wins
    auto games_file = players_file_name + "_games.txt";
    std::ifstream games_input(games_file);
    std::map<int, int> win_count;
    int best_id = -1;
    while(std::getline(games_input, line))
    {
        int white_id = -1;
        int black_id = -1;
        if(String::starts_with(line, "[White"))
        {
            white_id = std::stoi(String::split(String::split(line, "\"")[1])[2]);
        }
        else if(String::starts_with(line, "[Black"))
        {
            black_id = std::stoi(String::split(String::split(line, "\"")[1])[2]);
        }
        else if(String::starts_with(line, "[Result"))
        {
            auto result_string = String::split(line, "\"")[1];
            auto winning_id = -1;
            if(result_string == "1-0")
            {
                winning_id = white_id;
            }
            else if(result_string == "0-1")
            {
                winning_id = black_id;
            }

            int wins_so_far = ++win_count[winning_id];
            if(wins_so_far >= 3 && winning_id > best_id)
            {
                best_id = winning_id;
            }
        }
    }

    if(best_id == -1)
    {
        return last_player;
    }
    else
    {
        return best_id;
    }
}

void replay_game(const std::string& file_name, int game_number)
{
    std::ifstream ifs(file_name);
    std::vector<std::string> game_headers;
    std::string line;
    if(game_number >= 0)
    {
        // fast forward to indicated game
        while(std::getline(ifs, line))
        {
            line = String::trim_outer_whitespace(line);
            if(String::starts_with(line, '['))
            {
                game_headers.push_back(line);
            }
            else
            {
                game_headers.clear();
            }

            if(String::starts_with(line, "[Round"))
            {
                auto number = std::stoi(String::split(line, "\"")[1]);
                if(number == game_number)
                {
                    break;
                }
            }
        }

        if( ! ifs)
        {
            std::cout << "No game with ID number " << game_number << " found." << std::endl;
        }

        for(const auto& header : game_headers)
        {
            std::cout << header << std::endl;
        }
    }

    Board board;
    Game_Result result;
    bool game_started = false;
    while( ! result.game_has_ended() && std::getline(ifs, line))
    {
        line = String::strip_block_comment(line, '{', '}');
        line = String::strip_comments(line, ';');
        if(line.empty())
        {
            if(game_started)
            {
                break;
            }
            else
            {
                continue;
            }
        }
        if(line[0] == '[') // header lines
        {
            std::cout << line << std::endl;
            continue;
        }

        for(const auto& s : String::split(line))
        {
            try
            {
                result = board.submit_move(board.get_move(s));

                board.ascii_draw(WHITE);
                game_started = true;
                std::cout << "Last move: ";
                std::cout << (board.get_game_record().size() + 1)/2 << ". ";
                std::cout << (board.whose_turn() == WHITE ? "... " : "");
                std::cout << board.get_game_record().back()->coordinate_move() << std::endl;
                if(result.game_has_ended())
                {
                    std::cout << result.get_ending_reason() << std::endl;
                    break;
                }

                std::cout << "Enter \"y\" to play game from here: " << std::endl;
                char response = std::cin.get();
                if(std::tolower(response) == 'y')
                {
                    play_game_with_board(Human_Player(),
                                         Human_Player(),
                                         0,
                                         0,
                                         0,
                                         file_name + "_continued.pgn",
                                         board);
                     break;
                }
            }
            catch(const Illegal_Move_Exception&)
            {
                std::cout << "Ignoring: " << s << std::endl;
                continue;
            }
        }
    }
}
<|endoftext|>