c++ simulator implementation

This commit is contained in:
Stefan Bühler 2012-07-14 09:26:38 +02:00
parent 7ad209ee40
commit 9378156ac9
6 changed files with 633 additions and 0 deletions

14
Makefile Normal file
View File

@ -0,0 +1,14 @@
CXXFLAGS=-O2 -Wall -g
all: main
mine.o:: mine.h
io.o:: io.h
main.o:: io.h mine.h
main: main.o io.o mine.o
g++ -o $@ $^
clean:
rm -f main main.o io.o mine.o

91
io.cpp Normal file
View File

@ -0,0 +1,91 @@
#include "io.h"
#include <algorithm>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct IOBlock {
IOBlock *next;
ssize_t used, size;
unsigned char data[];
};
IOBlock* newIOBlock() {
const ssize_t amount = 512*1024; // avoid fragmentation: 128kb is the standard M_MMAP_THRESHOLD
IOBlock* b = (IOBlock*) malloc(amount);
if (!b) abort();
b->size = amount - sizeof(*b);
b->used = 0;
b->next = NULL;
return b;
}
// frees block list
static void blocksToVector(IOBlock *blocks, ssize_t total, std::vector<unsigned char> &buf) {
buf.reserve(std::max<ssize_t>(total+1, 512*1024)); // try forcing mmap
buf.resize(total);
(&buf.at(0))[total] = '\0';
ssize_t pos = 0;
IOBlock *prev = 0;
for (IOBlock *cur = blocks; cur; prev = cur, cur = cur->next) {
free(prev);
if (cur->used > 0) {
memcpy(&buf.at(pos), cur->data, cur->used);
pos += cur->used;
}
}
free(prev);
}
void readAll(int fd, std::vector<unsigned char> &buf) {
IOBlock *first = newIOBlock();
IOBlock *cur = first;
ssize_t total = 0;
for (;;) {
ssize_t avail;
for (;;) {
avail = cur->size - cur->used;
if (0 != avail) break;
cur->next = newIOBlock();
cur = cur->next;
}
ssize_t len = read(fd, cur->data + cur->used, avail);
if (0 == len) break; /* eof */
if (0 > len) {
perror("read failed");
abort();
}
cur->used += len;
total += len;
}
blocksToVector(first, total, buf);
}
void readLine(int fd, std::vector<unsigned char> &buf) {
IOBlock *first = newIOBlock();
IOBlock *cur = first;
ssize_t total = 0;
for (;;) {
if (cur->size == cur->used) {
cur->next = newIOBlock();
cur = cur->next;
}
ssize_t len = read(fd, cur->data + cur->used, 1);
if (0 == len) break; /* eof */
if (0 > len) {
perror("read failed");
abort();
}
if (cur->data[cur->used] == '\n' || cur->data[cur->used] == '\r') break;
cur->used += 1;
total += 1;
}
blocksToVector(first, total, buf);
}

9
io.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _MINE_IO_H
#define _MINE_IO_H _MINE_IO_H
#include <vector>
void readAll(int fd, std::vector<unsigned char> &buf);
void readLine(int fd, std::vector<unsigned char> &buf);
#endif

130
main.cpp Normal file
View File

@ -0,0 +1,130 @@
#include "io.h"
#include "mine.h"
#include <iostream>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
void runMoves(Mine &m, const char *moves) {
while (*moves) {
// m.show(std::cout);
// std::cout << *moves << "\n";
switch(*moves++) {
case 'L': m.move(Mine::Left); break;
case 'R': m.move(Mine::Right); break;
case 'U': m.move(Mine::Up); break;
case 'D': m.move(Mine::Down); break;
case 'W': m.move(Mine::Wait); break;
case 'A': m.move(Mine::Abort); break;
default: break;
}
}
}
void simulate(Mine &m, const char *moves) {
runMoves(m, moves);
m.show(std::cout);
}
void run_testcase(std::vector<unsigned char> &buf) {
Mine m(buf);
char *sbuf = (char*) &buf.at(0);
sbuf = strchr(sbuf, '!');
if (!sbuf) {
std::cerr << "Not a testcase, missing '!'\n";
exit(1);
}
char *moves = sbuf+1;
char *score = strchr(moves, '\n');
char *state = 0;
if (score) {
if (score[-1] == '\r') score[-1] = '\0';
*score++ = '\0';
state = strchr(score, '\n');
if (state) {
if (state[-1] == '\r') state[-1] = '\0';
*state++ = '\0';
sbuf = strchr(state, '\n');
if (sbuf) {
// remove trailing newline
if (sbuf[-1] == '\r') sbuf[-1] = '\0';
*sbuf++ = '\0';
}
}
}
runMoves(m, moves);
m.show(std::cout);
if (score) {
int n = atoi(score);
if (n != m.score()) {
std::cerr << "Testcase score missmatch: expected " << n << "\n";
exit(10);
}
}
if (state) {
if (0 == strcasecmp(state, "Win")) {
if (m.state() != Mine::Won) {
std::cerr << "Testcase result missmatch: expected to win\n";
exit(10);
}
} else if (0 == strcasecmp(state, "Alive")) {
if (m.state() != Mine::Alive) {
std::cerr << "Testcase result missmatch: expected to be still mining\n";
exit(10);
}
} else if (0 == strcasecmp(state, "Aborted")) {
if (m.state() != Mine::Alive && m.state() != Mine::Aborted) {
std::cerr << "Testcase result missmatch: expected to abort (alive counts too)\n";
exit(10);
}
} else if (0 == strcasecmp(state, "Broken")) {
if (m.state() != Mine::Lost) {
std::cerr << "Testcase result missmatch: expected to break robot\n";
exit(10);
}
} else {
std::cerr << "Unknown expected testcase result '" << state << "'\n";
exit(1);
}
}
}
int main(int argc, char **argv) {
if (argc == 3) {
// simulate: [file] [moves]
int fd = open(argv[1], O_RDONLY);
if (fd < 0) {
std::cerr << "opening map '" << argv[1] << "' failed\n";
exit(1);
}
std::vector<unsigned char> buf;
readAll(fd, buf);
close(fd);
Mine m(buf);
buf.clear();
simulate(m, argv[2]);
} else if (argc == 2) {
// simulate: [file with !moves on bottom]
int fd = open(argv[1], O_RDONLY);
if (fd < 0) {
std::cerr << "opening map '" << argv[1] << "' failed\n";
exit(1);
}
std::vector<unsigned char> buf;
readAll(fd, buf);
close(fd);
run_testcase(buf);
} else {
// solve puzzle, read map from stdin
std::cerr << "Solving not implemented yet\n";
exit(1);
}
return 0;
}

282
mine.cpp Normal file
View File

@ -0,0 +1,282 @@
#include "mine.h"
#include <algorithm>
#include <stdlib.h>
#include <string.h>
static const char cellSymbols[] = "#R*\\L. ";
static const char cellOpenSymbols[] = "#R*\\O. ";
static const char moveSymbols[] = "LRUDWA";
static inline char cellSymbol(Mine::Cell c, bool liftOpen) {
return (liftOpen ? cellOpenSymbols : cellSymbols)[(int) c];
}
static inline char moveSymbol(Mine::Move m) { return moveSymbols[(int) m]; }
Mine::Mine(const std::vector<unsigned char>& data)
: m_cells(0), m_newRocks(0), m_width(0), m_height(0)
, m_robot(), m_lifts(), m_lambdas()
, m_nLambdas(0), m_nMoves(0), m_state(Aborted) {
if (0 == data.size()) return;
int linewidth = 0;
size_t i = 0;
if ('!' == data.at(i)) {
// skip solution on first line
for (; i < data.size() && '\n' != data[i]; ++i) ;
}
for (; i < data.size(); ++i) {
unsigned char c = data.at(i);
switch (c) {
case '#':
case 'R':
case '*':
case '\\':
case 'L':
case '.':
case ' ':
// normal cell
++linewidth;
break;
case 'O': abort(); // no open lift allowed
break;
case '\n':
++m_height;
m_width = std::max<int>(m_width, linewidth);
linewidth = 0;
break;
case '\r':
// ignore
break;
case '!': // test case data, skip to enc
if (i > 0 && data[i-1] != '\n') ++m_height;
i = data.size();
break;
}
}
if (i == data.size() && data.back() != '\n') ++m_height;
m_newRocks = new bool[m_width];
m_cells = new iCell[m_width * m_height];
int x = 0, y = m_height-1;
iCell *cell = &m_cells[y*m_width];
for (size_t i = 0; i < data.size(); ++i) {
unsigned char c = data.at(i);
switch (c) {
case '#': *cell++ = Wall; ++x; break;
case 'R':
*cell++ = Robot;
if (m_robot.valid()) abort();
m_robot.set(x, y);
++x;
break;
case '*': *cell++ = Rock; ++x; break;
case '\\':
*cell++ = Lambda;
m_lambdas.push_back(Position(x, y));
++x;
break;
case 'L': *cell++ = Lift;
m_lifts.push_back(Position(x, y));
++x;
break;
case '.': *cell++ = Earth; ++x; break;
case ' ': *cell++ = Space; ++x; break;
case '\n':
for ( ; x < m_width; ++x) *cell++ = Space;
if (0 == y) {
i = data.size();
} else {
x = 0;
--y;
cell = &m_cells[y*m_width];
}
break;
case '\r':
// ignore
break;
case '!': // test case data, skip to enc
i = data.size();
break;
}
}
for ( ; x < m_width; ++x) *cell++ = Space;
if (!m_robot.valid()) abort();
m_state = Alive;
}
Mine::Mine(const Mine& other)
: m_cells(0), m_newRocks(0), m_width(other.m_width), m_height(other.m_height)
, m_robot(other.m_robot), m_lifts(other.m_lifts), m_lambdas(other.m_lambdas)
, m_nLambdas(other.m_nLambdas), m_nMoves(other.m_nMoves), m_state(other.m_state) {
m_newRocks = new bool[m_width];
size_t n = m_width * m_height;
m_cells = new iCell[n];
memcpy(m_cells, other.m_cells, sizeof(iCell) * n);
}
Mine& Mine::operator=(const Mine& other) {
m_width = other.m_width; m_height = other.m_height;
m_robot = other.m_robot; m_lifts = other.m_lifts; m_lambdas = other.m_lambdas;
m_nLambdas = other.m_nLambdas; m_nMoves = other.m_nMoves; m_state = other.m_state;
m_newRocks = new bool[m_width];
size_t n = m_width * m_height;
m_cells = new iCell[n];
memcpy(m_cells, other.m_cells, sizeof(iCell) * n);
return *this;
}
Mine::~Mine() {
delete[] m_cells; m_cells = 0; m_width = 0; m_height = 0; m_state = Aborted;
delete[] m_newRocks; m_newRocks = 0;
}
inline static void delPos(std::vector<Position>& v, const Position& p) {
std::vector<Position>::iterator i = std::find(v.begin(), v.end(), p);
if (i == v.end()) abort();
*i = v.back();
v.pop_back();
}
void Mine::move(Move m) {
if (m_state != Alive) return;
if (m_nMoves >= m_width*m_height) {
m_state = Aborted;
return;
}
int n = 1;
switch (m) {
case Left:
n = -1;
/* fall through */
case Right:
switch (at(m_robot.right(n))) {
case Lambda:
// collect lambda
++m_nLambdas;
delPos(m_lambdas, m_robot.right(n));
/* fall through */
case Earth:
case Space:
set(m_robot, Space);
m_robot = m_robot.right(n);
set(m_robot, Robot);
break;
case Rock:
if (Space == at(m_robot.right(2*n))) {
set(m_robot.right(2*n), Rock);
set(m_robot, Space);
m_robot = m_robot.right(n);
set(m_robot, Robot);
}
break;
case Wall: break;
case Robot: break; // "impossible"
case Lift:
if (isLiftOpen()) {
set(m_robot, Space);
m_robot = m_robot.right(n);
set(m_robot, Robot);
m_state = Won;
}
}
break;
case Down:
n = -1;
/* fall through */
case Up:
switch (at(m_robot.top(n))) {
case Lambda:
// collect lambda
++m_nLambdas;
delPos(m_lambdas, m_robot.top(n));
/* fall through */
case Earth:
case Space:
set(m_robot, Space);
m_robot = m_robot.top(n);
set(m_robot, Robot);
break;
case Rock: break;
case Wall: break;
case Robot: break; // "impossible"
case Lift:
if (isLiftOpen()) {
set(m_robot, Space);
m_robot = m_robot.top(n);
set(m_robot, Robot);
m_state = Won;
}
}
break;
case Wait: break;
case Abort:
m_state = Aborted;
return;
}
++m_nMoves;
/* move rocks */
bool topRow = true;
for (int y = m_height; --y >= 0; ) {
iCell *cell = &m_cells[y*m_width];
if (y == 0) {
if (!topRow) for (int x = 0; x < m_width; ++x, ++cell) {
if (m_newRocks[x]) *cell = Rock;
}
} else {
iCell *cellNext = cell-m_width; // row below
bool nextColRock = false, thisColRock = false, hadRock = false;
for (int x = 0; x < m_width; ++x, ++cell, ++cellNext) {
thisColRock = nextColRock; nextColRock = false;
if (*cell == Rock) {
if (*cellNext == Space) {
thisColRock = true;
*cell = Space;
} else if (*cellNext == Rock) {
if (x+1 < m_width && cellNext[1] == Space && cell[1] == Space) {
*cell = Space;
nextColRock = true;
} else if (x > 0 && cellNext[1] == Space && !hadRock) {
*cell = Space;
if (Alive == m_state && y > 1 && cellNext[-m_width-1] == Robot) m_state = Lost;
m_newRocks[x-1] = true;
}
} else if (*cellNext == Lambda) {
if (x+1 < m_width && cellNext[1] == Space && cell[1] == Space) {
*cell = Space;
nextColRock = true;
}
}
hadRock = true;
} else {
hadRock = false;
}
if (!topRow && m_newRocks[x]) *cell = Rock;
m_newRocks[x] = thisColRock;
if (Alive == m_state && thisColRock && y > 1 && cellNext[-m_width] == Robot) m_state = Lost;
}
}
topRow = false;
}
}
void Mine::show(std::ostream &o) {
bool liftOpen = isLiftOpen();
for (int y = m_height; --y >= 0; ) {
for (int x = 0; x < m_width; ++x) {
o << cellSymbol(at(x, y), liftOpen);
}
o << "\n";
}
o << "Score: " << score() << "\n";
switch (m_state) {
case Alive: o << "Still mining\n"; break;
case Won: o << "Mining complete\n"; break;
case Lost: o << "Robot broken\n"; break;
case Aborted: o << "Mining aborted\n"; break;
}
}

107
mine.h Normal file
View File

@ -0,0 +1,107 @@
#ifndef _MINE_MINE_H
#define _MINE_MINE_H _MINE_MINE_H
#include <vector>
#include <list>
#include <ostream>
class Position {
public:
int x, y;
inline Position(int x, int y) : x(x), y(y) { }
inline Position() : x(-1), y(-1) { }
inline bool operator==(const Position &other) { return x == other.x && y == other.y; }
inline void set(int x, int y) { this->x = x; this->y = y; }
inline Position left(int n = 1) const { return Position(x-n, y); }
inline Position right(int n = 1) const { return Position(x+n, y); }
inline Position top(int n = 1) const { return Position(x, y+n); }
inline Position bottom(int n = 1) const { return Position(x, y-n); }
inline Position topleft() const { return Position(x-1, y+1); }
inline Position topright() const { return Position(x+1, y+1); }
inline Position bottomleft() const { return Position(x-1, y-1); }
inline Position bottomright() const { return Position(x+1, y-1); }
inline bool valid() const { return x >= 0 && y >= 0; }
};
class Mine {
public:
enum Cell { Wall=0, Robot, Rock, Lambda, Lift, Earth, Space };
enum Move { Left=0, Right, Up, Down, Wait, Abort };
enum State { Alive=0, Won, Lost, Aborted };
Mine(const std::vector<unsigned char>& data);
Mine(const Mine& other);
Mine& operator=(const Mine& other);
~Mine();
inline int width() const { return m_width; }
inline int height() const { return m_height; }
inline Cell at(int x, int y) const {
if (x < 0 || y < 0 || x >= m_width || y >= m_height) return Wall;
return (Cell)m_cells[y*m_width+x];
}
inline Cell at(const Position &pos) const {
return at(pos.x, pos.y);
}
inline Cell operator()(int x, int y) const {
return at(x, y);
}
inline Cell operator()(const Position &pos) const {
return at(pos.x, pos.y);
}
inline const Position& robot() const { return m_robot; }
inline const std::vector<Position>& lifts() const { return m_lifts; }
inline bool isLiftOpen() const { return m_lambdas.size() == 0; }
inline const std::vector<Position>& lambdas() const { return m_lambdas; }
int score() const {
switch (m_state) {
case Lost:
return 25*m_nLambdas - m_nMoves;
case Won: return 75*m_nLambdas - m_nMoves;
case Alive:
case Aborted: return 50*m_nLambdas - m_nMoves;
}
return 0;
}
inline int foundLambdas() const { return m_nLambdas; }
inline int moves() const { return m_nMoves; }
inline State state() const { return m_state; }
void move(Move m);
void show(std::ostream &o);
private:
// no bound checks, writes must be valid.
inline void set(int x, int y, Cell c) {
m_cells[y*m_width+x] = c;
}
inline void set(Position pos, Cell c) {
set(pos.x, pos.y, c);
}
typedef unsigned char iCell;
iCell *m_cells;
bool *m_newRocks; // tmp row in map update
int m_width, m_height;
Position m_robot;
std::vector<Position> m_lifts, m_lambdas;
int m_nLambdas, m_nMoves;
State m_state;
};
#endif