Skip to content

Commit

Permalink
feat: n-puzzle
Browse files Browse the repository at this point in the history
  • Loading branch information
Tiphereth-A committed Feb 8, 2024
1 parent 4659d7d commit b1e33d7
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 2 deletions.
6 changes: 6 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ notebook:
- smawk: SMAWK 算法
code_ext: hpp
test_ext: cpp
- astar: A* 算法
code_ext: hpp
test_ext: cpp
graph:
- alist: 邻接表
code_ext: hpp
Expand Down Expand Up @@ -1225,6 +1228,9 @@ notebook:
- texas_holdem: 德州扑克
code_ext: hpp
test_ext: cpp
- npuzzle_data: N puzzle 数据类
code_ext: hpp
test_ext: cpp
fast:
- rsort32: 32 位整数基数排序
code_ext: hpp
Expand Down
82 changes: 82 additions & 0 deletions src/code/game/npuzzle_data.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#ifndef TIFALIBS_GAME_NPUZZLE_DATA
#define TIFALIBS_GAME_NPUZZLE_DATA

#include "../util/abs_constexpr.hpp"
#include "../util/util.hpp"

namespace tifa_libs::game {

// clang-format off
enum Dir4 { U, D, L, R };
// clang-format on

// n = k*k-1
class NPuzzleData {
u32 k, pos0;
vec<u32> node_;
vec<Dir4> moves_;
u32 cost_;

public:
constexpr explicit NPuzzleData(u32 k) : k(k) {}

constexpr auto const &cost() const { return cost_; }
constexpr auto &node() { return node_; }
constexpr auto const &node() const { return node_; }
constexpr auto const &moves() const { return moves_; }

constexpr bool solved() {
for (u32 i = 0; i < node_.size(); ++i)
if (node_[i] != i) return 0;
return 1;
}
constexpr vec<NPuzzleData> next() {
auto moves = gen_move();
vec<NPuzzleData> ans(moves.size(), *this);
for (u32 i = 0; i < moves.size(); ++i) ans[i].move(moves[i]);
return ans;
}
constexpr void move(Dir4 dir) {
moves_.push_back(dir);
u32 _ = pos0;
switch (dir) {
case U: pos0 -= k; break;
case D: pos0 += k; break;
case L: --pos0; break;
case R: ++pos0; break;
}
std::swap(node_[_], node_[pos0]);
cost_ = gen_cost();
}

constexpr auto operator<=>(NPuzzleData const &node) const { return node_ <=> node.node_; }

friend std::istream &operator>>(std::istream &is, NPuzzleData &np) {
np.node_.resize(np.k * np.k);
for (auto &i : np.node_) is >> i;
np.pos0 = u32(std::find(np.node_.begin(), np.node_.end(), 0) - np.node_.begin());
np.cost_ = np.gen_cost();
return is;
}

private:
constexpr vec<Dir4> gen_move() const {
vec<Dir4> ans;
if (pos0 / k) ans.push_back(U);
if (pos0 / k != k - 1) ans.push_back(D);
if (pos0 % k) ans.push_back(L);
if (pos0 % k != k - 1) ans.push_back(R);
return ans;
}
constexpr u32 gen_cost() const {
u32 h1 = 0, h2 = 0;
for (u32 i = 0; i < k; ++i)
for (u32 j = 0; j < k; ++j)
if (u32 _ = node_[k * i + j]; _ != k * i + j && _) ++h1, h2 += u32(abs((i32)i - i32(_ / k)) + abs((i32)j - i32(_ % k)));
return std::max(h1, h2) + (u32)moves_.size();
}
};

} // namespace tifa_libs::game

#endif
40 changes: 40 additions & 0 deletions src/code/opt/astar.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef TIFALIBS_OPT_ASTAR
#define TIFALIBS_OPT_ASTAR

#include "../util/traits.hpp"

namespace tifa_libs::opt {

template <class T>
requires requires(T x, T y) {
{ x.solved() } -> std::same_as<bool>;
{ x.next() } -> iterable_c;
x.cost() < y.cost();
x < y;
}
std::optional<T> astar(T const &s) {
struct C {
constexpr bool operator()(T const &a, T const &b) const { return b.cost() < a.cost(); }
};
pq<T, C> pq;
std::set<T> vis;

pq.push(s);
vis.insert(s);
while (!pq.empty()) {
T now = pq.top();
pq.pop();
if (now.solved()) return now;
auto nxt = now.next();
for (auto i : nxt)
if (!vis.count(i)) {
pq.push(i);
vis.insert(i);
}
}
return std::nullopt;
}

} // namespace tifa_libs::opt

#endif
7 changes: 5 additions & 2 deletions src/code/util/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@ using vecp = vec<std::pair<U, T>>;
template <class U, class T>
using vvecp = vvec<std::pair<U, T>>;

template <class T>
using pq = std::priority_queue<T>;
template <class T, class C = std::less<T>>
using pq = std::priority_queue<T, vec<T>, C>;
template <class T>
using pqg = std::priority_queue<T, vec<T>, std::greater<T>>;

using strn = std::string;
using strnv = std::string_view;

#ifdef ONLINE_JUDGE
#undef assert
#define assert(x) 42
Expand Down
7 changes: 7 additions & 0 deletions src/doc_tex/game/npuzzle_data.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
用于求解 \(N\)-puzzle \(N=k^2-1,~k\in\mathbf{N}_{>2}\) 的数据类.

\(N\)-puzzle 是一个滑块游戏. 滑块方盘的长宽均为 \(k\times k\) 个方块, 其中 \(N\) 个位置放序号打乱的方块, 剩下一个为空位. 与空位同行或同列的方块可以通过水平或垂直滑动来移动. 拼图的目标是按编号顺序排列方块

求解时需使用 \fullref{sec:a*-算法}

\inputminted{cpp}{src/src/npuzzle_data_usage.txt}
9 changes: 9 additions & 0 deletions src/doc_tex/opt/astar.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
\paragraph{要求}

数据类 \verb|T| 需可比较且需具有如下成员函数:

\verb|bool solved()|: 返回当前状态是否满足终止条件

\verb|U next()|: 返回当前状态的所有后继, 返回类型 \verb|U| 需可迭代

\verb|W cost()|: 返回当前状态的估价, 返回类型 \verb|W| 需可比较
9 changes: 9 additions & 0 deletions src/src/npuzzle_data_usage.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
int k = 4; // 15-puzzle

NPuzzleData start(k);
// 读入局面
std::cin >> start;
// 输出当前局面
std::cout << start.node();
// 输出操作步骤
std::cout << astar(start)->moves();
16 changes: 16 additions & 0 deletions src/test_cpverifier/aizu/alds1_13_c.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#define PROBLEM "https://onlinejudge.u-aizu.ac.jp/courses/lesson/1/ALDS1/all/ALDS1_13_C"

#include "../../code/game/npuzzle_data.hpp"
#include "../../code/opt/astar.hpp"

int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
tifa_libs::game::NPuzzleData start(4);
std::cin >> start;
for (auto& i : start.node())
if (i) --i;
else i = 15;
std::cout << tifa_libs::opt::astar(start)->moves().size() << '\n';
return 0;
}
22 changes: 22 additions & 0 deletions src/test_cpverifier/hackerrank/n-puzzle.test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#define PROBLEM "https://www.hackerrank.com/challenges/n-puzzle/problem"

#include "../../code/game/npuzzle_data.hpp"
#include "../../code/opt/astar.hpp"

int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
u32 n;
std::cin >> n;
tifa_libs::game::NPuzzleData start(n);
std::cin >> start;
auto v = tifa_libs::opt::astar(start)->moves();
std::cout << v.size() << '\n';
for (auto i : v) switch (i) {
case tifa_libs::game::U: std::cout << "UP\n"; break;
case tifa_libs::game::D: std::cout << "DOWN\n"; break;
case tifa_libs::game::L: std::cout << "LEFT\n"; break;
case tifa_libs::game::R: std::cout << "RIGHT\n"; break;
}
return 0;
}
Empty file.
Empty file added src/test_tinplate/opt/astar.cpp
Empty file.

0 comments on commit b1e33d7

Please sign in to comment.