You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

282 lines
6.8 KiB

#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;
}
}