c++ simulator implementation
This commit is contained in:
parent
7ad209ee40
commit
9378156ac9
14
Makefile
Normal file
14
Makefile
Normal 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
91
io.cpp
Normal 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
9
io.h
Normal 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
130
main.cpp
Normal 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
282
mine.cpp
Normal 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
107
mine.h
Normal 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
|
Loading…
Reference in New Issue
Block a user