#include <istream>
#include <memory>
#include <ostream>
+#include <queue>
+#include <set>
+#include <sstream>
#include <stdexcept>
#include <vector>
+#include <iostream>
+
signed char Step::adjacent[BOARD_SIZE][5] = {
1, 4, -1, -1, -1,
0, 2, 5, -1, -1,
return dist;
}
-std::vector<Step*> Step::successors(std::shared_ptr<Step> shared_this) const {
- std::vector<Step*> suc;
+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(new Step{board, shared_this});
+ 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;
}
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");
+}
#include <sstream>
using testing::Field;
+using testing::Pointee;
TEST(Step, Adjacency) {
const signed char LEFT = -1;
TEST(Step, TwoSuccessors) {
auto s = std::make_shared<Step>(Step{{{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0}}, nullptr});
- std::vector<Step*> suc = s->successors(s);
+ std::vector<std::shared_ptr<Step>> suc = s->successors(s);
EXPECT_THAT(suc, testing::UnorderedElementsAre(
- Field(&Step::board, Board{{1,2,3,4,5,6,7,8,9,10,11,12,13,14,0,15}}),
- Field(&Step::board, Board{{1,2,3,4,5,6,7,8,9,10,11,0,13,14,15,12}})));
+ Pointee(Field(&Step::board, Board{{1,2,3,4,5,6,7,8,9,10,11,12,13,14,0,15}})),
+ Pointee(Field(&Step::board, Board{{1,2,3,4,5,6,7,8,9,10,11,0,13,14,15,12}}))));
}
TEST(Step, FourSuccessors) {
auto s = std::make_shared<Step>(Step{{{1,2,3,4,5,0,6,7,8,9,10,11,12,13,14,15}}, nullptr});
- std::vector<Step*> suc = s->successors(s);
+ std::vector<std::shared_ptr<Step>> suc = s->successors(s);
EXPECT_THAT(suc, testing::UnorderedElementsAre(
- Field(&Step::board, Board{{1,2,3,4,0,5,6,7,8,9,10,11,12,13,14,15}}),
- Field(&Step::board, Board{{1,2,3,4,5,6,0,7,8,9,10,11,12,13,14,15}}),
- Field(&Step::board, Board{{1,0,3,4,5,2,6,7,8,9,10,11,12,13,14,15}}),
- Field(&Step::board, Board{{1,2,3,4,5,9,6,7,8,0,10,11,12,13,14,15}})));
+ Pointee(Field(&Step::board, Board{{1,2,3,4,0,5,6,7,8,9,10,11,12,13,14,15}})),
+ Pointee(Field(&Step::board, Board{{1,2,3,4,5,6,0,7,8,9,10,11,12,13,14,15}})),
+ Pointee(Field(&Step::board, Board{{1,0,3,4,5,2,6,7,8,9,10,11,12,13,14,15}})),
+ Pointee(Field(&Step::board, Board{{1,2,3,4,5,9,6,7,8,0,10,11,12,13,14,15}}))));
}
TEST(Step, Output) {