#include #include #include #include #include #include #include #include #include typedef enum { Empty = 0, Floor = 1, Wall = 2 } TileType; typedef enum { Forward = 0, TurnRight = 1, TurnLeft = 2 } Command; typedef enum { FacingUp = 0, FacingRight = 90, FacingDown = 180, FacingLeft = 270 } Facing; typedef enum { Face1, Face2, Face3, Face4, Face5, Face6 } CubeFace; // how many tiles per face const long FACE_SIZE = 50 class Tile { public: Tile(int p_x, int p_y, TileType t) { x = p_x; y = p_y; m_type = t; } Tile() { x = -1; y = -1; m_type = TileType::Empty; } void print(); void printFace(); // Coordinates in the 2D map int x; int y; // Coordinates within the face surface (0...FACE_SIZE) int face_x; int face_y; // ID of the cube face this tile sits on CubeFace m_face; // type of data in this tile TileType m_type; }; void Tile::printFace() { if (m_type == TileType::Empty) { std::cout << " "; return; } switch (m_face) { case Face1: std::cout << "1"; break; case Face2: std::cout << "2"; break; case Face3: std::cout << "3"; break; case Face4: std::cout << "4"; break; case Face5: std::cout << "5"; break; case Face6: std::cout << "6"; break; default: throw "no face"; } } class Player { public: Tile* m_loc; int m_facing; void turn(Command c); void walk(Tile* map, int map_w, int map_h); }; void Player::turn(Command c) { switch (c) { case TurnLeft: m_facing -= 90; if (m_facing < 0) { m_facing = 270; } break; case TurnRight: m_facing += 90; if (m_facing > 270) { m_facing = 0; } break; default: throw "Invalid turn direction"; } switch (m_facing) { case FacingLeft: std::cout << "facing left" << std::endl; break; case FacingRight: std::cout << "facing right" << std::endl; break; case FacingUp: std::cout << "facing up" << std::endl; break; case FacingDown: std::cout << "facing down" << std::endl; break; default: throw "Invalid facing direction"; } } /** * When crossing the edge of one face, returns the ID of the * face onto which we move */ CubeFace teleportEdge(CubeFace src, Facing dir) { switch (src) { case Face1: switch (dir) { case FacingUp: return Face6; case FacingRight: return Face2; case FacingDown: return Face3; case FacingLeft: return Face4; } case Face2: switch (dir) { case FacingUp: return Face6; case FacingRight: return Face5; case FacingDown: return Face3; case FacingLeft: return Face1; } break; case Face3: switch (dir) { case FacingUp: return Face1; case FacingRight: return Face2; case FacingDown: return Face5; case FacingLeft: return Face4; } break; case Face4: switch (dir) { case FacingUp: return Face3; case FacingRight: return Face5; case FacingDown: return Face6; case FacingLeft: return Face1; } break; case Face5: switch (dir) { case FacingUp: return Face3; case FacingRight: return Face2; case FacingDown: return Face6; case FacingLeft: return Face4; } break; case Face6: switch (dir) { case FacingUp: return Face4; case FacingRight: return Face5; case FacingDown: return Face2; case FacingLeft: return Face1; } break; default: throw "invalid src face"; } } void handleFaceTransition(CubeFace src, CubeFace dest) { switch (src) { case Face1: switch (dest) { case Face2: break; // new facing right (same), x becomes face start, y does not change case Face3: break; // new facing down (same), x does not change, y becomes face start height case Face4: break; // new facing right, x becomes face start, y is flipped case Face5: break; // not possible case Face6: break; // new facing right, flip x and y } break; case Face2: switch (dest) { case Face1: break; // facing does not change, x becomes face max, y does not change case Face3: break; // new facing left, flip x and y case Face4: break; // not possible, case Face5: break; // new facing right, flip and y case Face6: break; // new facing up, x does not changed, y becomes face max } break; case Face3: switch (dest) { case Face1: break; // facing does not change, x unchanged, y becomes face height stop case Face2: break; // facing becomes up, flip x and y coordinates within face case Face4: break; // facing becomes down, flip x and y coordinates within face case Face5: break; // facing becomes down, x unchanged, y becomes face height start case Face6: break; // not possible } break; case Face4: switch (dest) { case Face1: break; // new facing right, flip x and y case Face2: break; // not possible case Face3: break; // new facing right, flip x and y case Face5: break; // new facing right, x becomes face min, y does not change case Face6: break; // new facing down, x does not change, y becomes face min } break; case Face5: switch (dest) { case Face1: break; // not possible case Face2: break; // new facing right, x becomes face min, y does not change case Face3: break; // new facing up, x does not change, y becomes face max case Face4: break; // new facing left, x becomes face max, y does not change case Face6: break; // new facing left, flip x and y } break; case Face6: switch (dest) { case Face1: break; // new facing down, flip x and y case Face2: break; // new facing down, x does not change, y becomes face min case Face3: break; // not possible case Face4: break; // new facing up, x does change, y becomes face max case Face5: break; // new facing up, flip x and y } break; default: throw "invalid src face"; } } void Player::walk(Tile* map, int map_w, int map_h) { int offset_x = 0; int offset_y = 0; switch (m_facing) { case FacingLeft: offset_x = -1; break; case FacingRight: offset_x = 1; break; case FacingUp: offset_y = -1; break; case FacingDown: offset_y = 1; break; default: throw "Invalid facing direction when moving"; } Tile search = *m_loc; if (offset_x != 0) { std::cout << "walking x: " << offset_x << std::endl; bool found = false; while (!found) { search.x += offset_x; if (search.x < 0) { search = map[search.y * map_w + map_w-1]; } else if (search.x >= map_w) { search = map[search.y * map_w]; } else { search = map[search.y * map_w + search.x]; } // TODO: instead of checking if the space is empty, check if // we are within the bounds, and we don't need to check the // other face diff from normal walk if (search.m_type == Floor) { found = true; continue; } else if (search.m_type == Wall) { found = true; continue; } else if (search.m_type == Empty) { // ?? } if (search.m_face != m_loc->face) { // magic here, hardcode transitions switch (m_loc->face) { case Face1: break; case Face2: break; case Face3: break; case Face4: break; case Face5: break; case Face6: break; } } } } else if (offset_y != 0) { bool found = false; std::cout << "walking y: " << offset_y << std::endl; while (!found) { search.y += offset_y; if (search.y < 0) { search = map[(map_h-1) * map_w + search.x]; } else if (search.y >= map_h) { search = map[search.x]; } else { search = map[search.y * map_w + search.x]; } if (search.m_type == Floor) { found = true; continue; } else if (search.m_type == Wall) { found = true; continue; } else if (search.m_type == Empty) { // magic here } } } if (search.m_type == Floor) { m_loc = &map[search.y * map_w + search.x]; } else { std::cout << "not moving to invalid tile: "; search.print(); std::cout << std::endl; } } void Tile::print() { switch (m_type) { case TileType::Empty: std::cout << " "; return; case TileType::Wall: std::cout << "#"; return; case TileType::Floor: std::cout << "."; return; default: std::cout << "!"; return; } } std::vector split(std::string s, std::string delimiter) { size_t pos_start = 0, pos_end, delim_len = delimiter.length(); std::string token; std::vector res; while ((pos_end = s.find (delimiter, pos_start)) != std::string::npos) { token = s.substr (pos_start, pos_end - pos_start); pos_start = pos_end + delim_len; res.push_back (token); } res.push_back (s.substr (pos_start)); return res; } /** * Print the map, optionally showing the palyer on it */ void printMap(Tile* map, int w, int h, Player* p, bool printFaces) { for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { if (p != NULL && p->m_loc->x == x && p->m_loc->y == y) { std::cout << "@"; } else { if (printFaces) { map[y * w + x].printFace(); } else { map[y * w + x].print(); } } } std::cout << " | " << y << std::endl; } } void printCommands(std::vector& commands) { int moves = 0; for (int i = 0; i < commands.size(); ++i) { Command& c = commands[i]; switch (c) { case Forward: moves++; if (i == commands.size()-1) { std::cout << "move forward: " << moves << std::endl; } break; case TurnLeft: std::cout << "move forward: " << moves << std::endl; std::cout << "turn left" << std::endl; moves = 0; break; case TurnRight: std::cout << "move forward: " << moves << std::endl; std::cout << "turn right" << std::endl; moves = 0; break; } } } /** * Go through the map from top to bottom, left to right, and find first * tile that is open floor */ Tile* findFirst(Tile* map, int w, int h) { for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { Tile* t = &map[y * w + x]; if (t->m_type == TileType::Floor) { return t; } } } std::cout << "did not find starting tile" << std::endl; return NULL; } int main(int argc, char** argv) { if (argc < 2) { std::cout << "invalid input" << std::endl; return 1; } const std::string fpath = argv[1]; struct stat st; int ret = stat(fpath.c_str(), &st); if (ret != 0) { std::cout << "failed statting: " << fpath << ", " << strerror(errno) << std::endl; return 1; } FILE* fin = fopen(fpath.c_str(), "r"); if (!fin) { std::cout << "Failed opening: " << argv[1] << std::endl; return 1; } char* buf = new char[st.st_size]; memset(buf, 0, st.st_size); size_t r = fread(buf, 1, st.st_size, fin); fclose(fin); if (r != st.st_size) { std::cout << "read: " << r << "/" << st.st_size << " bytes" << std::endl; delete[] buf; return 1; } std::vector lines; std::string l; for (size_t t = 0; t < st.st_size; ++t) { if (buf[t] == '\n') { lines.push_back(l); l = std::string(); continue; } l += buf[t]; } std::cout << "there are: " << lines.size() << " lines" << std::endl; delete[] buf; std::vector maplines; std::vector walklines; for (auto& line : lines) { line.erase(std::remove(line.begin(), line.end(), '\n'), line.cend()); if (line.size() == 0) { continue; } if (line[0] == '.' || line[0] == '#' || line[0] == ' ') { maplines.push_back(line); } else { walklines.push_back(line); } } if (walklines.size() > 1) { std::cout << "walk lines size: " << walklines.size() << std::endl; return 1; } std::string directions = walklines[0]; int map_width = 0; int map_height = maplines.size(); for (auto& w : maplines) { if (w.size() > map_width) { map_width = w.size(); } } // construct w*h map Tile* map = new Tile[map_width * map_height]; for (int y = 0; y < map_height; ++y) { for (int x = 0; x < map_width; ++x) { Tile t = Tile(x, y, TileType::Empty); // hard code cube faces because fuck if I know how to re-wrap // the UV map from arbitrary input if (y < 50) { if (x < 100) { t.m_face = Face1; } else { t.m_face = Face2; } } else if (y < 100) { t.m_face = Face3; } else if (y < 150) { if (x < 50) { t.m_face = Face4; } else { t.m_face = Face5; } } else { t.m_face = Face6; } map[y * map_width + x] = t; } } // Assign each tile a type int row = 0; for (auto& l : maplines) { int column = 0; for (auto& c : l) { Tile* t = &map[row * map_width + column]; if (c == ' ') { t->m_type = TileType::Empty; } else if (c == '#') { t->m_type = TileType::Wall; } else { t->m_type = TileType::Floor; } column++; } row++; } // construct command list from input std::vector commands; std::stringstream ss; for (int i = 0; i < directions.size(); ++i) { char c = directions[i]; if (std::isdigit(c)) { ss << c; // handle last instruction being a move without turning if (i == directions.size()-1) { int count = 0; ss >> count; for (int j = 0; j < count; ++j) { commands.push_back(Forward); } } } else { bool isleft = false; if (c == 'L') { isleft = true; } else if (c == 'R') { isleft = false; } else { std::cout << "invalid direction: " << c << std::endl; return 1; } int count = 0; ss >> count; for (int j = 0; j < count; ++j) { commands.push_back(Forward); } commands.push_back(isleft ? TurnLeft : TurnRight); ss = std::stringstream(); } } printMap(map, map_width, map_height, NULL, true); return 1; printCommands(commands); Tile* start = findFirst(map, map_width, map_height); if (start == NULL || start->m_type != TileType::Floor || start->x < 0 || start->x >= map_width || start->y < 0 || start->y >= map_height) { std::cout << "invalid start tile" << std::endl; return 1; } Player p; p.m_loc = start; p.m_facing = FacingRight; printMap(map, map_width, map_height, &p, false); std::cout << "starting tile x: " << start->x << ", y: " << start->y << std::endl; for (auto& c : commands) { switch (c) { case Forward: p.walk(map, map_width, map_height); break; case TurnLeft: case TurnRight: p.turn(c); break; } } printMap(map, map_width, map_height, &p, false); std::cout << "player is at: x: " << p.m_loc->x+1 << ", y: " << p.m_loc->y+1 << std::endl; int facing = 0; switch (p.m_facing) { case FacingLeft: std::cout << "Facing left (2)" << std::endl; facing = 2; break; case FacingRight: std::cout << "Facing right (0)" << std::endl; facing = 0; break; case FacingUp: std::cout << "Facing up (3)" << std::endl; facing = 3; break; case FacingDown: std::cout << "Facing down (1)" << std::endl; facing = 1; break; } long long password = (1000 * (p.m_loc->y+1)) + (4 * (p.m_loc->x+1)) + facing; std::cout << "password: " << password << std::endl; delete[] map; return 0; }