+
+std::ostream& operator<<(std::ostream& os, const Board& board) {
+ for (int i = 0; i < BOARD_SIZE; i++) {
+ if (i > 0) {
+ os << " ";
+ }
+ os << int(board.board[i]);
+ }
+ return os;
+}
+
+signed char Board::hole() const {
+ for (int i = 0; i < BOARD_SIZE; i++) {
+ if (board[i] == 0) {
+ return i;
+ }
+ }
+ throw std::runtime_error("Board with no hole");
+}
+
+InvertedBoard Board::invert() const {
+ InvertedBoard inv;
+ for (int i = 0; i < BOARD_SIZE; i++) {
+ inv.pos[board[i]] = i;
+ }
+ return inv;
+}
+
+int Board::distance(const Board& o) const {
+ return distance(o.invert());
+}
+
+int Board::distance(const InvertedBoard& invo) const {
+ int dist = 0;
+ for (int i = 0; i < BOARD_SIZE; i++) {
+ dist += std::abs(i % BOARD_DIM - invo.pos[board[i]] % BOARD_DIM) +
+ std::abs(i / BOARD_DIM - invo.pos[board[i]] / BOARD_DIM);
+ }
+ return dist;
+}
+
+Step::Step(Board board, std::shared_ptr<Step> prev) : board(board), prev(prev) {}
+
+std::vector<std::shared_ptr<Step>> Step::successors(std::shared_ptr<Step> shared_this) const {
+ std::vector<std::shared_ptr<Step>> suc;
+ signed char hole_pos = board.hole();
+ for (int i = 0; adjacent[hole_pos][i] >= 0; i++) {
+ suc.emplace_back(std::make_shared<Step>(board, shared_this));
+ std::swap(suc.back()->board.board[hole_pos], suc.back()->board.board[adjacent[hole_pos][i]]);
+ }
+ return suc;
+}
+
+int Step::length() const {
+ if (prev.get() == nullptr) {
+ return 0;
+ }
+ return 1 + prev->length();
+}
+
+int Step::cost(const InvertedBoard& invgoal) const {
+ return length() + board.distance(invgoal) / 2;
+}
+
+std::ostream& operator<<(std::ostream& os, const Step& step) {
+ if (step.prev != nullptr) {
+ os << *step.prev;
+ signed char this_hole = step.board.hole();
+ signed char prev_hole = step.prev->board.hole();
+ os << int(step.board.board[prev_hole]) << " ";
+ switch (this_hole - prev_hole) {
+ case -1: os << "right"; break;
+ case 1: os << "left"; break;
+ case -BOARD_DIM: os << "down"; break;
+ case BOARD_DIM: os << "up"; break;
+ default: os << "somehow!"; break;
+ }
+ os << std::endl;
+ }
+ return os;
+}
+
+std::shared_ptr<Step> find_path(const std::string& start, const std::string& goal) {
+ std::istringstream iss_start{start}, iss_goal{goal};
+ Board board_start, board_goal;
+ iss_start >> board_start;
+ iss_goal >> board_goal;
+ if (iss_start.fail() || !iss_start.eof()) {
+ throw std::runtime_error("Could not parse the start board: " + start);
+ }
+ if (iss_goal.fail() || !iss_goal.eof()) {
+ throw std::runtime_error("Could not parse the goal board: " + goal);
+ }
+ return find_path(board_start, board_goal);
+}
+
+std::shared_ptr<Step> find_path(const Board& start, const Board& goal) {
+ InvertedBoard invgoal = goal.invert();
+ auto heap_greater = [invgoal](const std::shared_ptr<Step>& a, const std::shared_ptr<Step>& b) {
+ return a->cost(invgoal) > b->cost(invgoal);
+ };
+ std::priority_queue<std::shared_ptr<Step>,
+ std::vector<std::shared_ptr<Step>>,
+ decltype(heap_greater)> todo(heap_greater);
+ std::set<Board> seen;
+
+ seen.emplace(start);
+ todo.push(std::make_shared<Step>(start, nullptr));
+ while (!todo.empty()) {
+ if (todo.top()->board == goal) {
+ return todo.top();
+ }
+ std::vector<std::shared_ptr<Step>> successors = todo.top()->successors(todo.top());
+ for (const std::shared_ptr<Step>& s : successors) {
+ if (seen.find(s->board) == seen.end()) {
+ seen.emplace(s->board);
+ todo.push(s);
+ if (seen.size() % 10000 == 0) {
+ std::cerr << "Examined " << seen.size() << " boards. " << todo.size()
+ << " waiting. Considering paths of length "
+ << todo.top()->cost(invgoal) << std::endl;
+ }
+ }
+ }
+ todo.pop();
+ }
+ throw std::runtime_error("No path from start to goal");
+}