#include "mine.h" #include #include #include 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& 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(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& v, const Position& p) { std::vector::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; } }