]>
git.scottworley.com Git - slidingtile/blob - sliding_tile_lib.cc
1 #include "sliding_tile_lib.h"
17 signed char Step::adjacent
[BOARD_SIZE
][5] = {
36 bool Board::is_valid() const {
37 bool seen
[BOARD_SIZE
];
38 for (int i
= 0; i
< BOARD_SIZE
; i
++) {
42 for (int i
= 0; i
< BOARD_SIZE
; i
++) {
43 if (board
[i
] < 0 || board
[i
] >= BOARD_SIZE
|| seen
[board
[i
]]) {
46 seen
[board
[i
]] = true;
49 // Redundant because pigeon-hole-principle, but check anyway
50 for (int i
= 0; i
< BOARD_SIZE
; i
++) {
59 bool Board::operator==(const Board
& o
) const {
60 for (int i
= 0; i
< BOARD_SIZE
; i
++) {
61 if (board
[i
] != o
.board
[i
]) {
68 bool Board::operator!=(const Board
& o
) const {
69 return !operator==(o
);
72 bool Board::operator<(const Board
& o
) const {
73 for (int i
= 0; i
< BOARD_SIZE
; i
++) {
74 if (board
[i
] < o
.board
[i
]) {
76 } else if (board
[i
] > o
.board
[i
]) {
83 std::istream
& operator>>(std::istream
& is
, Board
& board
) {
84 for (int i
= 0; i
< BOARD_SIZE
; i
++) {
86 is
.setstate(std::istream::failbit
);
89 if (i
> 0 && is
.get() != ',') {
90 is
.setstate(std::istream::failbit
);
95 board
.board
[i
] = numeric
;
97 if (!board
.is_valid()) {
98 is
.setstate(std::istream::failbit
);
103 std::ostream
& operator<<(std::ostream
& os
, const Board
& board
) {
104 for (int i
= 0; i
< BOARD_SIZE
; i
++) {
108 os
<< int(board
.board
[i
]);
113 signed char Board::hole() const {
114 for (int i
= 0; i
< BOARD_SIZE
; i
++) {
119 throw std::runtime_error("Board with no hole");
122 InvertedBoard
Board::invert() const {
124 for (int i
= 0; i
< BOARD_SIZE
; i
++) {
125 inv
.pos
[board
[i
]] = i
;
130 int Board::distance(const Board
& o
) const {
131 return distance(o
.invert());
134 int Board::distance(const InvertedBoard
& invo
) const {
136 for (int i
= 0; i
< BOARD_SIZE
; i
++) {
138 dist
+= std::abs(i
% BOARD_DIM
- invo
.pos
[board
[i
]] % BOARD_DIM
) +
139 std::abs(i
/ BOARD_DIM
- invo
.pos
[board
[i
]] / BOARD_DIM
);
145 Step::Step(Board board
, std::shared_ptr
<Step
> prev
) : board(board
), prev(prev
) {
153 std::vector
<std::shared_ptr
<Step
>> Step::successors(std::shared_ptr
<Step
> shared_this
) const {
154 std::vector
<std::shared_ptr
<Step
>> suc
;
155 signed char hole_pos
= board
.hole();
156 for (int i
= 0; adjacent
[hole_pos
][i
] >= 0; i
++) {
157 suc
.emplace_back(std::make_shared
<Step
>(board
, shared_this
));
158 std::swap(suc
.back()->board
.board
[hole_pos
], suc
.back()->board
.board
[adjacent
[hole_pos
][i
]]);
163 int Step::length() const {
164 if (prev
.get() == nullptr) {
167 return 1 + prev
->length();
170 int Step::cost(const InvertedBoard
& invgoal
) const {
171 return length() + board
.distance(invgoal
) / 2;
174 std::ostream
& operator<<(std::ostream
& os
, const Step
& step
) {
175 if (step
.prev
!= nullptr) {
177 signed char this_hole
= step
.board
.hole();
178 signed char prev_hole
= step
.prev
->board
.hole();
179 os
<< int(step
.board
.board
[prev_hole
]) << " ";
180 switch (this_hole
- prev_hole
) {
181 case -1: os
<< "right"; break;
182 case 1: os
<< "left"; break;
183 case -BOARD_DIM
: os
<< "down"; break;
184 case BOARD_DIM
: os
<< "up"; break;
185 default: os
<< "somehow!"; break;
192 std::shared_ptr
<Step
> find_path(const std::string
& start
, const std::string
& goal
, unsigned max_frontier
) {
193 std::istringstream iss_start
{start
}, iss_goal
{goal
};
194 Board board_start
, board_goal
;
195 iss_start
>> board_start
;
196 iss_goal
>> board_goal
;
197 if (iss_start
.fail() || !iss_start
.eof()) {
198 throw std::runtime_error("Could not parse the start board: " + start
);
200 if (iss_goal
.fail() || !iss_goal
.eof()) {
201 throw std::runtime_error("Could not parse the goal board: " + goal
);
203 return find_path(board_start
, board_goal
, max_frontier
);
206 std::shared_ptr
<Step
> find_path(const Board
& start
, const Board
& goal
, unsigned max_frontier
) {
207 InvertedBoard invgoal
= goal
.invert();
208 std::multimap
<int, std::shared_ptr
<Step
>> todo
;
209 std::set
<Board
> seen
;
212 auto start_step
= std::make_shared
<Step
>(start
, nullptr);
213 todo
.emplace(start_step
->cost(invgoal
), start_step
);
214 while (!todo
.empty()) {
215 auto cur
= todo
.begin()->second
;
216 todo
.erase(todo
.begin());
217 if (cur
->board
== goal
) {
220 std::vector
<std::shared_ptr
<Step
>> successors
= cur
->successors(cur
);
221 for (const std::shared_ptr
<Step
>& s
: successors
) {
222 if (seen
.find(s
->board
) == seen
.end()) {
223 seen
.insert(s
->board
);
224 todo
.emplace(s
->cost(invgoal
), s
);
225 if (seen
.size() % 10000 == 0) {
226 std::cerr
<< "Examined " << seen
.size() << " boards. Tracking "
227 << Step::count
<< " steps. " << todo
.size()
228 << " waiting. Considering paths of length "
229 << cur
->cost(invgoal
) << std::endl
;
233 while (todo
.size() > max_frontier
) {
234 todo
.erase(--todo
.end());
237 throw std::runtime_error("No path from start to goal");