#include "engine_worker.hpp"
#include "protocol.hpp"
#include "window_adapter.hpp"
#include <cstdio>
#include <cstdlib>
#include <filesystem>
#include <stdexcept>
#include <string>
#include <unistd.h>
#include <vector>
extern "C"
{
int nhmain(int argc, char* argv[]);
}
namespace nethack_mcp
{
namespace
{
int findIntegerArgument(int argc, char* argv[], const char* name)
{
for(int index = 0; index + 1 < argc; ++index)
{
if(std::string(argv[index]) == name)
{
return std::stoi(argv[index + 1]);
}
}
return -1;
}
} // namespace
int runEngineWorker(int argc, char* argv[])
{
const int descriptor = findIntegerArgument(argc, argv, "--ipc-fd");
if(descriptor < 0)
{
std::fputs("--engine requires --ipc-fd\n", stderr);
return 2;
}
try
{
FramedChannel channel(descriptor);
Json hello = {
{"type", "hello"},
{"ipc_version", IPC_VERSION},
{"game_id", nullptr},
{"nethack_commit",
"c94fd5225beef48143244bfb7bc42682aad58741"},
{"lua_version", "5.4.9"},
};
std::string error;
if(!channel.send(hello, error))
{
throw std::runtime_error(error);
}
Json start;
if(!channel.receive(start, error)
|| start.value("type", "") != "start")
{
throw std::runtime_error(
error.empty() ? "worker did not receive start" : error);
}
const std::string game_id = start.at("game_id").get<std::string>();
const std::filesystem::path run_directory =
start.at("run_dir").get<std::string>();
const std::string player_name =
start.value("name", std::string("Agent"));
std::filesystem::current_path(run_directory);
setenv("HOME", run_directory.c_str(), 1);
setenv("HACKDIR", run_directory.c_str(), 1);
setenv("NETHACKDIR", run_directory.c_str(), 1);
const Json character = start.value("character", Json::object());
std::string options = "showexp,showscore,time";
for(const char* key : {"role", "race", "gender", "alignment"})
{
if(character.contains(key))
{
options += ",";
options += key;
options += ":";
options += character.at(key).get<std::string>();
}
}
setenv("NETHACKOPTIONS", options.c_str(), 1);
WindowAdapter adapter(channel, game_id);
adapter.install();
char program[] = "nethack_mcp";
char name_option[] = "-u";
std::vector<char> name(player_name.begin(), player_name.end());
name.push_back('\0');
char* nethack_argv[] = {program, name_option, name.data(), nullptr};
return nhmain(3, nethack_argv);
}
catch(const std::exception& exception)
{
std::fprintf(stderr, "NetHack worker failed: %s\n",
exception.what());
return 2;
}
}
} // namespace nethack_mcp