283 lines
6.8 KiB
C++
283 lines
6.8 KiB
C++
#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;
|
|
}
|
|
}
|