#include "fifo_controller.hpp"
#include <cassert>
#include <cstdio>
#include "../common/Log.hpp"
namespace ale {
#define MAX_RUN_LENGTH (0xFF)
static const char hexval[] = {'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
inline void appendByte(char* buf, uInt8 v) {
*buf = hexval[(v >> 4)];
*(buf + 1) = hexval[v & 0xF];
}
FIFOController::FIFOController(OSystem* _osystem, bool named_pipes)
: ALEController(_osystem), m_named_pipes(named_pipes) {
m_max_num_frames = m_osystem->settings().getInt("max_num_frames");
m_run_length_encoding = m_osystem->settings().getBool("run_length_encoding");
}
FIFOController::~FIFOController() {
if (m_fout != NULL)
fclose(m_fout);
if (m_fin != NULL)
fclose(m_fin);
}
void FIFOController::run() {
Action action_a, action_b;
handshake();
while (!isDone()) {
sendData();
readAction(action_a, action_b);
latest_reward = applyActions(action_a, action_b);
display();
}
if (!feof(m_fout))
fprintf(m_fout, "DIE\n");
}
bool FIFOController::isDone() {
return ((m_max_num_frames > 0 &&
m_environment.getFrameNumber() >= m_max_num_frames) ||
feof(m_fin) || feof(m_fout) || ferror(m_fout));
}
void FIFOController::handshake() {
if (m_named_pipes) {
openNamedPipes();
} else { m_fout = stdout;
m_fin = stdin;
assert(m_fin != NULL && m_fout != NULL);
}
char out_buffer[1024];
snprintf(out_buffer, sizeof(out_buffer), "%d-%d\n",
(int)m_environment.getScreen().width(),
(int)m_environment.getScreen().height());
fputs(out_buffer, m_fout);
fflush(m_fout);
char in_buffer[1024];
if (fgets(in_buffer, sizeof(in_buffer), m_fin) == NULL) {
return;
}
char* token = strtok(in_buffer, ",\n");
m_send_screen = atoi(token);
token = strtok(NULL, ",\n");
m_send_ram = atoi(token);
token = strtok(NULL, ",\n");
token = strtok(NULL, ",\n");
m_send_rl = atoi(token);
}
void FIFOController::openNamedPipes() {
m_fout = fopen("ale_fifo_out", "w");
if (m_fout == NULL) {
ale::Logger::Error << "Missing output pipe: ale_fifo_out" << std::endl;
exit(1);
}
m_fin = fopen("ale_fifo_in", "r");
if (m_fin == NULL) {
ale::Logger::Error << "Missing output pipe: ale_fifo_out" << std::endl;
exit(1);
}
}
void FIFOController::sendData() {
if (m_send_ram)
sendRAM();
if (m_send_screen)
sendScreen();
if (m_send_rl)
sendRL();
fputc('\n', m_fout);
fflush(m_fout);
}
void FIFOController::sendScreen() {
const ALEScreen& screen = m_environment.getScreen();
char buffer[204800];
int sn;
if (m_run_length_encoding)
sn = stringScreenRLE(screen, buffer);
else
sn = stringScreenFull(screen, buffer);
buffer[sn] = ':';
buffer[sn + 1] = 0;
fputs(buffer, m_fout);
}
int FIFOController::stringScreenRLE(const ALEScreen& screen, char* buffer) {
int currentColor = -1;
int runLength = 0;
int sn = 0;
for (size_t i = 0; i < screen.arraySize(); i++) {
pixel_t col = screen.getArray()[i];
if (col == currentColor && runLength < MAX_RUN_LENGTH)
runLength++;
else {
if (currentColor != -1) {
appendByte(buffer + sn, currentColor);
appendByte(buffer + sn + 2, runLength);
sn += 4;
}
currentColor = col;
runLength = 1;
}
}
appendByte(buffer + sn, currentColor);
appendByte(buffer + sn + 2, runLength);
sn += 4;
return sn;
}
int FIFOController::stringScreenFull(const ALEScreen& screen, char* buffer) {
int sn = 0;
for (size_t i = 0; i < screen.arraySize(); i++) {
pixel_t col = screen.getArray()[i];
appendByte(buffer + sn, col);
sn += 2;
}
return sn;
}
void FIFOController::sendRAM() {
const ALERAM& ram = m_environment.getRAM();
char buffer[204800];
int sn = 0;
for (size_t i = 0; i < ram.size(); i++) {
byte_t b = ram.get(i);
appendByte(buffer + sn, b);
sn += 2;
}
buffer[sn] = ':';
buffer[sn + 1] = 0;
fputs(buffer, m_fout);
}
void FIFOController::sendRL() {
int r = (int)latest_reward;
bool is_terminal = m_environment.isTerminal();
fprintf(m_fout, "%d,%d:", is_terminal, r);
}
void FIFOController::readAction(Action& action_a, Action& action_b) {
char in_buffer[2048];
if (fgets(in_buffer, sizeof(in_buffer), m_fin) == NULL) {
action_a = PLAYER_A_NOOP;
action_b = PLAYER_B_NOOP;
return;
}
char* token = strtok(in_buffer, ",\n");
action_a = (Action)atoi(token);
token = strtok(NULL, ",\n");
action_b = (Action)atoi(token);
}
}