棋盘
设
空格:0,
兵:1,
竖的武将:2,
横的武将:3,
曹操:4
总共有20个格子,可以用字符串或者char数组
编码方式
总共有5种棋子,需要3位来表示
但是如果单独表示曹操,则只需要2位
剩下一个曹操,考虑用位置来编码,因为曹操的左上角只会出现在前4行和前3列中
所以有12种状态,需要4位
所以总共是 11 × 2 + 4 = 26 b i t 11\times 2+ 4=26\ bit 11×2+4=26 bit
这种编码忽略了竖的武将之间的相对顺序,以及兵的相对顺序
另一个好处是,如果
c
o
d
e
&
0
b
1111
=
10
code\&0b1111=10
code&0b1111=10,则曹操出来了
如果你编码考虑了顺序,那去重的时候就麻烦了
unsigned encode(const std::string& board) {
//9 8 ... 0 general (the general is encoded in 0-11)
unsigned result = 0;
bool flag[CHESS_NUM] = {};
int i = -1;
unsigned chess_cnt = 0, general_pos = 0;
for (const char& c : board) {
++i;
if (flag[i]) {
continue;
}
if (4 != c) {
result |= c << (2 * chess_cnt);
if (2 == c) {//V
flag[i + WIDTH] = true;
}
else if (3 == c) {//H
flag[i + 1] = true;
//++i;
}
++chess_cnt;
}
else {
general_pos = i - i / WIDTH;
flag[i + 1] = flag[i + WIDTH] = flag[i + WIDTH + 1] = true;
//flag[i + WIDTH] = flag[i + WIDTH+1] = true;
//++i;
}
}
return result << 4 | general_pos;
}
判断合法
建议写一个用来debug,防止出现关羽拦腰斩等情况
bool is_valid(const std::string& board) {
bool flag[CHESS_NUM] = {};
int i = -1;
int cnt[5] = {};
for (const char& c : board) {
++i;
if (flag[i]) {
continue;
}
++cnt[c];
if (4 != c) {
if (2 == c) {//V
if (i + WIDTH >= CHESS_NUM || board[i + WIDTH] != 2) {
return false;
}
flag[i + WIDTH] = true;
}
else if (3 == c) {//H
if (i + 1 >= CHESS_NUM || board[i + 1] != 3 || i % WIDTH + 1 == WIDTH) {
return false;
}
flag[i + 1] = true;
//++i;
}
}
else {
if (i + WIDTH + 1 >= CHESS_NUM || i % WIDTH + 1 == WIDTH ||
board[i + 1] != 4 || board[i + WIDTH] != 4 || board[i + WIDTH + 1] != 4) {
return false;
}
flag[i + 1] = flag[i + WIDTH] = flag[i + WIDTH + 1] = true;
}
}
return 2 == cnt[0] && 4 == cnt[1] && 4 == cnt[2] && 1 == cnt[3] && 1 == cnt[4];
}
移动
首先要判断能不能移动
注意到棋子都是和周围的空格交换位置
所以逐个判断就行了
注意判断拦腰斩就行,因为上下移动一般不会出事
//find space
unsigned space1 = CHESS_NUM, space2 = CHESS_NUM;
for (unsigned i = 0; i < CHESS_NUM; ++i) {
if (0 == board[i]) {
if (space1 == CHESS_NUM) {
space1 = i;
}
else {
space2 = i;
break;
}
}
}
int space_type = 0;//1:horizontal,2:vertical
if (space1 + WIDTH == space2) {
space_type = 2;
}
else if (space1 + 1 == space2 && space1 % WIDTH + 1 < WIDTH) {
space_type = 1;
}
memset(flag, false, sizeof(flag));
for (unsigned i = 0; i < 20; ++i) {
if (flag[i] || 0 == board[i]) {
continue;
}
unsigned col = i % WIDTH;
if (2 == board[i]) { // V
if ((space1 + 1 == i && col > 0 || i + 1 == space1 && col + 1 < WIDTH) && 2 == space_type) { //left,right
in_queue(q, board, cur_code, i, space1);
}
else {
if (space1 + WIDTH == i || space2 + WIDTH == i) {//up
in_queue(q, board, cur_code, i, i - WIDTH);
if (2 == space_type) {
in_queue(q, board, cur_code, i, i - 2 * WIDTH);
}
}
if (i + 2 * WIDTH == space1 || i + 2 * WIDTH == space2) {//down
in_queue(q, board, cur_code, i, i + WIDTH);
if (2 == space_type) {
in_queue(q, board, cur_code, i, i + 2 * WIDTH);
}
}
}
flag[i + WIDTH] = true;
}
else if (1 == board[i]) {//C
if ((space1 + 1 == i || space2 + 1 == i) && col > 0) {//left
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i - 1);
}
}
if ((i + 1 == space1 || i + 1 == space2) && col + 1 < WIDTH) {//right
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i + 1);
}
}
if (space1 + WIDTH == i || space2 + WIDTH == i) {//up
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i - WIDTH);
}
}
if (i + WIDTH == space1 || i + WIDTH == space2) {//down
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i + WIDTH);
}
}
}
else if (3 == board[i]) {//H
if ((space1 + WIDTH == i || i + WIDTH == space1) && 1 == space_type) {//up,down
in_queue(q, board, cur_code, i, space1);
}
else {
if ((space1 + 1 == i || space2 + 1 == i) && col > 0) {//left
in_queue(q, board, cur_code, i, i - 1);
if (1 == space_type) {
in_queue(q, board, cur_code, i, i - 2);
}
}
if ((i + 2 == space1 || i + 2 == space2) && col + 2 < WIDTH) {//right
in_queue(q, board, cur_code, i, i + 1);
if (1 == space_type) {
in_queue(q, board, cur_code, i, i + 2);
}
}
}
//flag[i+1]=true;
++i;
}
else {//general
unsigned code = 0;
if (space1 + 1 == i && col > 0 && 2 == space_type) {//left
code = in_queue(q, board, cur_code, i, space1);
}
else if (i + 2 == space1 && col + 2 < WIDTH && 2 == space_type) {//right
code = in_queue(q, board, cur_code, i, i + 1);
}
else if (space1 + WIDTH == i && 1 == space_type) {//up
code = in_queue(q, board, cur_code, i, space1);
}
else if (i + 2 * WIDTH == space1 && 1 == space_type) {//down
code = in_queue(q, board, cur_code, i, i + WIDTH);
}
if (10 == (code & 0xf)) {
end_code = code;
return ans;
}
//flag[i+1]=flag[i+WIDTH]=flag[i+WIDTH+1]=true;
flag[i + WIDTH] = flag[i + WIDTH + 1] = true;
++i;
}
}
移动的话,可以只考虑左上角,先把原来棋子占的地方置位空格,然后把对应位置置为棋子
void move_chess(std::string& board, unsigned before, unsigned after) {
char c = board[before];
switch (board[before]) {
case 2:
board[before] = board[before + WIDTH] = 0;
board[after] = board[after + WIDTH] = 2;
if (!is_valid(board)) {
printf("faQ\n");
}
return;
case 1:
board[before] = 0;
board[after] = 1;
return;
case 4:
board[before] = board[before + 1] = board[before + WIDTH] = board[before + WIDTH + 1] = 0;
board[after] = board[after + 1] = board[after + WIDTH] = board[after + WIDTH + 1] = 4;
return;
case 3:
board[before] = board[before + 1] = 0;
board[after] = board[after + 1] = 3;
return;
default:
std::swap(board[before], board[after]);
return;
}
}
bfs
剩下的其实就是熟悉的bfs了
我这里是编码入队,然后出队的时候解码
你也可以写一个直接字符串入队
大概13-14ms能跑出来
完整代码
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <Windows.h>
#include <unordered_map>
#include <algorithm>
const unsigned HEIGHT = 5, WIDTH = 4, CHESS_NUM = HEIGHT * WIDTH;
class State {
public:
unsigned pre_state, before, after;
explicit State(unsigned pre_state = 0, unsigned before = 0, unsigned after = 0) :
pre_state(pre_state), before(before), after(after) {
}
};
std::unordered_map<unsigned, State> table;
unsigned encode(const std::string& board) {
//9 8 ... 0 general (the general is encoded in 0-11)
unsigned result = 0;
bool flag[CHESS_NUM] = {};
int i = -1;
unsigned chess_cnt = 0, general_pos = 0;
for (const char& c : board) {
++i;
if (flag[i]) {
continue;
}
if (4 != c) {
result |= c << (2 * chess_cnt);
if (2 == c) {//V
flag[i + WIDTH] = true;
}
else if (3 == c) {//H
flag[i + 1] = true;
//++i;
}
++chess_cnt;
}
else {
general_pos = i - i / WIDTH;
flag[i + 1] = flag[i + WIDTH] = flag[i + WIDTH + 1] = true;
//flag[i + WIDTH] = flag[i + WIDTH+1] = true;
//++i;
}
}
return result << 4 | general_pos;
}
void decode(unsigned code, std::string& result) {
memset(&result[0], '\0', CHESS_NUM);
unsigned chess_cnt = 0, general_pos = code & 0xf;
general_pos = general_pos + general_pos / (WIDTH - 1);
result[general_pos] = result[general_pos + 1] = result[general_pos + WIDTH] = result[general_pos + WIDTH + 1] = 4;
code >>= 4;
for (unsigned i = 0; i < CHESS_NUM; ++i) {
if (result[i] != '\0')continue;
unsigned cur = code & 0x3;
code >>= 2;
result[i] = cur;
++chess_cnt;
if (2 == cur) {//V
result[i + WIDTH] = cur;
}
else if (3 == cur) {//H
result[i + 1] = cur;
++i;
}
}
}
std::string decode(unsigned code) {
std::string result(20, '\0');
decode(code, result);
return result;
}
void print_board(const std::string& board) {
for (int i = 0; i < HEIGHT; ++i) {
for (int j = 0; j < WIDTH; ++j) {
printf("%d", int(board[i * WIDTH + j]));
}
printf("\n");
}
printf("\n");
}
unsigned mirror_encode(const std::string& board) {
std::string output = board;
for (auto it = output.begin(); it != output.end(); it += WIDTH) {
std::reverse(it, it + WIDTH);
}
return encode(output);
}
bool is_valid(const std::string& board) {
bool flag[CHESS_NUM] = {};
int i = -1;
int cnt[5] = {};
for (const char& c : board) {
++i;
if (flag[i]) {
continue;
}
++cnt[c];
if (4 != c) {
if (2 == c) {//V
if (i + WIDTH >= CHESS_NUM || board[i + WIDTH] != 2) {
return false;
}
flag[i + WIDTH] = true;
}
else if (3 == c) {//H
if (i + 1 >= CHESS_NUM || board[i + 1] != 3 || i % WIDTH + 1 == WIDTH) {
return false;
}
flag[i + 1] = true;
//++i;
}
}
else {
if (i + WIDTH + 1 >= CHESS_NUM || i % WIDTH + 1 == WIDTH ||
board[i + 1] != 4 || board[i + WIDTH] != 4 || board[i + WIDTH + 1] != 4) {
return false;
}
flag[i + 1] = flag[i + WIDTH] = flag[i + WIDTH + 1] = true;
}
}
return 2 == cnt[0] && 4 == cnt[1] && 4 == cnt[2] && 1 == cnt[3] && 1 == cnt[4];
}
void move_chess(std::string& board, unsigned before, unsigned after) {
char c = board[before];
switch (board[before]) {
case 2:
board[before] = board[before + WIDTH] = 0;
board[after] = board[after + WIDTH] = 2;
if (!is_valid(board)) {
printf("faQ\n");
}
return;
case 1:
board[before] = 0;
board[after] = 1;
return;
case 4:
board[before] = board[before + 1] = board[before + WIDTH] = board[before + WIDTH + 1] = 0;
board[after] = board[after + 1] = board[after + WIDTH] = board[after + WIDTH + 1] = 4;
return;
case 3:
board[before] = board[before + 1] = 0;
board[after] = board[after + 1] = 3;
return;
default:
std::swap(board[before], board[after]);
return;
}
}
unsigned in_queue(std::queue<unsigned>& q, std::string& board, unsigned pre_state, unsigned before, unsigned after) {
move_chess(board, before, after);
unsigned code = encode(board);
if (table.find(code) == table.end()) {
q.push(code);
table[code] = State(pre_state, before, after);
//ensure in the table
unsigned mirror_code = mirror_encode(board);
if (code != mirror_code) {
table[mirror_code] = State(pre_state, before, after);
}
}
move_chess(board, after, before);
return code;
}
unsigned end_code = 0;
int bfs(const std::string& init) {
if (4 == init[17] && 4 == init[18]) {
return 0;
}
std::queue<unsigned> q;
unsigned cur_code = encode(init);
q.push(cur_code);
table[cur_code] = State();
int ans = 0;
std::string board(CHESS_NUM, '\0');
bool flag[CHESS_NUM];
while (!q.empty()) {
++ans;
int size = q.size();
while (size--) {
cur_code = q.front();
q.pop();
decode(cur_code, board);
//find space
unsigned space1 = CHESS_NUM, space2 = CHESS_NUM;
for (unsigned i = 0; i < CHESS_NUM; ++i) {
if (0 == board[i]) {
if (space1 == CHESS_NUM) {
space1 = i;
}
else {
space2 = i;
break;
}
}
}
int space_type = 0;//1:horizontal,2:vertical
if (space1 + WIDTH == space2) {
space_type = 2;
}
else if (space1 + 1 == space2 && space1 % WIDTH + 1 < WIDTH) {
space_type = 1;
}
memset(flag, false, sizeof(flag));
for (unsigned i = 0; i < 20; ++i) {
if (flag[i] || 0 == board[i]) {
continue;
}
unsigned col = i % WIDTH;
if (2 == board[i]) { // V
if ((space1 + 1 == i && col > 0 || i + 1 == space1 && col + 1 < WIDTH) && 2 == space_type) { //left,right
in_queue(q, board, cur_code, i, space1);
}
else {
if (space1 + WIDTH == i || space2 + WIDTH == i) {//up
in_queue(q, board, cur_code, i, i - WIDTH);
if (2 == space_type) {
in_queue(q, board, cur_code, i, i - 2 * WIDTH);
}
}
if (i + 2 * WIDTH == space1 || i + 2 * WIDTH == space2) {//down
in_queue(q, board, cur_code, i, i + WIDTH);
if (2 == space_type) {
in_queue(q, board, cur_code, i, i + 2 * WIDTH);
}
}
}
flag[i + WIDTH] = true;
}
else if (1 == board[i]) {//C
if ((space1 + 1 == i || space2 + 1 == i) && col > 0) {//left
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i - 1);
}
}
if ((i + 1 == space1 || i + 1 == space2) && col + 1 < WIDTH) {//right
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i + 1);
}
}
if (space1 + WIDTH == i || space2 + WIDTH == i) {//up
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i - WIDTH);
}
}
if (i + WIDTH == space1 || i + WIDTH == space2) {//down
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i + WIDTH);
}
}
}
else if (3 == board[i]) {//H
if ((space1 + WIDTH == i || i + WIDTH == space1) && 1 == space_type) {//up,down
in_queue(q, board, cur_code, i, space1);
}
else {
if ((space1 + 1 == i || space2 + 1 == i) && col > 0) {//left
in_queue(q, board, cur_code, i, i - 1);
if (1 == space_type) {
in_queue(q, board, cur_code, i, i - 2);
}
}
if ((i + 2 == space1 || i + 2 == space2) && col + 2 < WIDTH) {//right
in_queue(q, board, cur_code, i, i + 1);
if (1 == space_type) {
in_queue(q, board, cur_code, i, i + 2);
}
}
}
//flag[i+1]=true;
++i;
}
else {//general
unsigned code = 0;
if (space1 + 1 == i && col > 0 && 2 == space_type) {//left
code = in_queue(q, board, cur_code, i, space1);
}
else if (i + 2 == space1 && col + 2 < WIDTH && 2 == space_type) {//right
code = in_queue(q, board, cur_code, i, i + 1);
}
else if (space1 + WIDTH == i && 1 == space_type) {//up
code = in_queue(q, board, cur_code, i, space1);
}
else if (i + 2 * WIDTH == space1 && 1 == space_type) {//down
code = in_queue(q, board, cur_code, i, i + WIDTH);
}
if (10 == (code & 0xf)) {
end_code = code;
return ans;
}
//flag[i+1]=flag[i+WIDTH]=flag[i+WIDTH+1]=true;
flag[i + WIDTH] = flag[i + WIDTH + 1] = true;
++i;
}
}
}
}
return -1;
}
std::vector<std::pair<int, int>> get_directions(const std::string& board, unsigned before, unsigned after) {
int x1 = before / WIDTH, y1 = before % WIDTH, x2 = after / WIDTH, y2 = after % WIDTH;
if (x1 - x2 == y1 - y2 || x1 - x2 == y2 - y1) {
if (0 == board[x2 * WIDTH + y1]) {
return { std::make_pair(x2 - x1,0),std::make_pair(0,y2 - y1) };
}
return { std::make_pair(0,y2 - y1),std::make_pair(x2 - x1,0) };
}
return { std::make_pair(x2 - x1,y2 - y1) };
}
void traverse(const std::string& init, const unsigned& end_code) {
std::stack<State> st;
unsigned start_code = encode(init);
unsigned state = end_code;
while (state != start_code) {
State temp = table[state];
state = temp.pre_state;
st.push(temp);
}
std::string board = init;
print_board(board);
while (!st.empty()) {
State temp = st.top();
st.pop();
std::vector<std::pair<int, int>> directions = get_directions(board, temp.before, temp.after);
for (int i = 0; i < directions.size(); ++i) {
if (i > 0) {
printf(",");
}
printf("%d: (%d,%d)", int(board[temp.before]), directions[i].first, directions[i].second);
}
printf("\n");
move_chess(board, temp.before, temp.after);
print_board(board);
}
}
int main() {
//1:C,2:V,3:H,4:G
std::string s = {
2,4,4,2,
2,4,4,2,
2,3,3,2,
2,1,1,2,
1,0,0,1 };
//print_board(s);
//printf("\n");
LARGE_INTEGER m_nFreq;
LARGE_INTEGER m_nBeginTime;
LARGE_INTEGER nEndTime;
QueryPerformanceFrequency(&m_nFreq); // 获取时钟周期
QueryPerformanceCounter(&m_nBeginTime); // 获取时钟计数
int ans = bfs(s);
QueryPerformanceCounter(&nEndTime);
printf("%d\n", ans);
printf("cost time:%lfms\n", (double)(nEndTime.QuadPart - m_nBeginTime.QuadPart) * 1000 / m_nFreq.QuadPart);
traverse(s, end_code);
return 0;
}
类版本
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <stack>
#include <Windows.h>
#include <unordered_map>
#include <algorithm>
#include <cassert>
class State {
public:
unsigned pre_state, before, after;
explicit State(unsigned pre_state = 0, unsigned before = 0, unsigned after = 0) :
pre_state(pre_state), before(before), after(after) {
}
};
class Klotski_AI {
private:
static const unsigned HEIGHT = 5, WIDTH = 4, CHESS_NUM = HEIGHT * WIDTH;
std::unordered_map<unsigned, State> table;
std::string init;
unsigned end_code;
public:
unsigned encode(const std::string& board) {
//9 8 ... 0 general (the general is encoded in 0-11)
unsigned result = 0;
bool flag[CHESS_NUM] = {};
int i = -1;
unsigned chess_cnt = 0, general_pos = 0;
for (const char& c : board) {
++i;
if (flag[i]) {
continue;
}
if (4 != c) {
result |= c << (2 * chess_cnt);
if (2 == c) {//V
flag[i + WIDTH] = true;
}
else if (3 == c) {//H
flag[i + 1] = true;
//++i;
}
++chess_cnt;
}
else {
general_pos = i - i / WIDTH;
flag[i + 1] = flag[i + WIDTH] = flag[i + WIDTH + 1] = true;
//flag[i + WIDTH] = flag[i + WIDTH+1] = true;
//++i;
}
}
return result << 4 | general_pos;
}
private:
void decode(unsigned code, std::string& result) {
memset(&result[0], '\0', CHESS_NUM);
unsigned chess_cnt = 0, general_pos = code & 0xf;
general_pos = general_pos + general_pos / (WIDTH - 1);
result[general_pos] = result[general_pos + 1] = result[general_pos + WIDTH] = result[general_pos + WIDTH + 1] = 4;
code >>= 4;
for (unsigned i = 0; i < CHESS_NUM; ++i) {
if (result[i] != '\0')continue;
unsigned cur = code & 0x3;
code >>= 2;
result[i] = cur;
++chess_cnt;
if (2 == cur) {//V
result[i + WIDTH] = cur;
}
else if (3 == cur) {//H
result[i + 1] = cur;
++i;
}
}
}
std::string decode(unsigned code) {
std::string result(20, '\0');
decode(code, result);
return result;
}
void print_board(const std::string& board) {
for (int i = 0; i < HEIGHT; ++i) {
for (int j = 0; j < WIDTH; ++j) {
printf("%d", int(board[i * WIDTH + j]));
}
printf("\n");
}
printf("\n");
}
unsigned mirror_encode(const std::string& board) {
std::string output = board;
for (auto it = output.begin(); it != output.end(); it += WIDTH) {
std::reverse(it, it + WIDTH);
}
return encode(output);
}
bool is_valid(const std::string& board) {
if (board.size() != CHESS_NUM) {
return false;
}
bool flag[CHESS_NUM] = {};
int i = -1;
int cnt[5] = {};
for (const char& c : board) {
++i;
if (flag[i]) {
continue;
}
++cnt[c];
if (4 != c) {
if (2 == c) {//V
if (i + WIDTH >= CHESS_NUM || board[i + WIDTH] != 2) {
return false;
}
flag[i + WIDTH] = true;
}
else if (3 == c) {//H
if (i + 1 >= CHESS_NUM || board[i + 1] != 3 || i % WIDTH + 1 == WIDTH) {
return false;
}
flag[i + 1] = true;
//++i;
}
}
else {
if (i + WIDTH + 1 >= CHESS_NUM || i % WIDTH + 1 == WIDTH ||
board[i + 1] != 4 || board[i + WIDTH] != 4 || board[i + WIDTH + 1] != 4) {
return false;
}
flag[i + 1] = flag[i + WIDTH] = flag[i + WIDTH + 1] = true;
}
}
return 2 == cnt[0] && 4 == cnt[1] && 4 == cnt[2] && 1 == cnt[3] && 1 == cnt[4];
}
void move_chess(std::string& board, unsigned before, unsigned after) {
char c = board[before];
switch (board[before]) {
case 2:
board[before] = board[before + WIDTH] = 0;
board[after] = board[after + WIDTH] = 2;
if (!is_valid(board)) {
printf("faQ\n");
}
return;
case 1:
board[before] = 0;
board[after] = 1;
return;
case 4:
board[before] = board[before + 1] = board[before + WIDTH] = board[before + WIDTH + 1] = 0;
board[after] = board[after + 1] = board[after + WIDTH] = board[after + WIDTH + 1] = 4;
return;
case 3:
board[before] = board[before + 1] = 0;
board[after] = board[after + 1] = 3;
return;
default:
std::swap(board[before], board[after]);
return;
}
}
unsigned in_queue(std::queue<unsigned>& q, std::string& board, unsigned pre_state, unsigned before, unsigned after) {
move_chess(board, before, after);
unsigned code = encode(board);
if (table.find(code) == table.end()) {
q.push(code);
table[code] = State(pre_state, before, after);
//ensure in the table
unsigned mirror_code = mirror_encode(board);
if (code != mirror_code) {
table[mirror_code] = State(pre_state, before, after);
}
}
move_chess(board, after, before);
return code;
}
int bfs(const std::string& init) {
if (4 == init[17] && 4 == init[18]) {
return 0;
}
std::queue<unsigned> q;
unsigned cur_code = encode(init);
q.push(cur_code);
table[cur_code] = State();
int ans = 0;
std::string board(CHESS_NUM, '\0');
bool flag[CHESS_NUM];
while (!q.empty()) {
++ans;
int size = q.size();
while (size--) {
cur_code = q.front();
q.pop();
decode(cur_code, board);
//find space
unsigned space1 = CHESS_NUM, space2 = CHESS_NUM;
for (unsigned i = 0; i < CHESS_NUM; ++i) {
if (0 == board[i]) {
if (space1 == CHESS_NUM) {
space1 = i;
}
else {
space2 = i;
break;
}
}
}
int space_type = 0;//1:horizontal,2:vertical
if (space1 + WIDTH == space2) {
space_type = 2;
}
else if (space1 + 1 == space2 && space1 % WIDTH + 1 < WIDTH) {
space_type = 1;
}
memset(flag, false, sizeof(flag));
for (unsigned i = 0; i < 20; ++i) {
if (flag[i] || 0 == board[i]) {
continue;
}
unsigned col = i % WIDTH;
if (2 == board[i]) { // V
if ((space1 + 1 == i && col > 0 || i + 1 == space1 && col + 1 < WIDTH) && 2 == space_type) { //left,right
in_queue(q, board, cur_code, i, space1);
}
else {
if (space1 + WIDTH == i || space2 + WIDTH == i) {//up
in_queue(q, board, cur_code, i, i - WIDTH);
if (2 == space_type) {
in_queue(q, board, cur_code, i, i - 2 * WIDTH);
}
}
if (i + 2 * WIDTH == space1 || i + 2 * WIDTH == space2) {//down
in_queue(q, board, cur_code, i, i + WIDTH);
if (2 == space_type) {
in_queue(q, board, cur_code, i, i + 2 * WIDTH);
}
}
}
flag[i + WIDTH] = true;
}
else if (1 == board[i]) {//C
if ((space1 + 1 == i || space2 + 1 == i) && col > 0) {//left
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i - 1);
}
}
if ((i + 1 == space1 || i + 1 == space2) && col + 1 < WIDTH) {//right
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i + 1);
}
}
if (space1 + WIDTH == i || space2 + WIDTH == i) {//up
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i - WIDTH);
}
}
if (i + WIDTH == space1 || i + WIDTH == space2) {//down
if (space_type) {
in_queue(q, board, cur_code, i, space1);
in_queue(q, board, cur_code, i, space2);
}
else {
in_queue(q, board, cur_code, i, i + WIDTH);
}
}
}
else if (3 == board[i]) {//H
if ((space1 + WIDTH == i || i + WIDTH == space1) && 1 == space_type) {//up,down
in_queue(q, board, cur_code, i, space1);
}
else {
if ((space1 + 1 == i || space2 + 1 == i) && col > 0) {//left
in_queue(q, board, cur_code, i, i - 1);
if (1 == space_type) {
in_queue(q, board, cur_code, i, i - 2);
}
}
if ((i + 2 == space1 || i + 2 == space2) && col + 2 < WIDTH) {//right
in_queue(q, board, cur_code, i, i + 1);
if (1 == space_type) {
in_queue(q, board, cur_code, i, i + 2);
}
}
}
//flag[i+1]=true;
++i;
}
else {//general
unsigned code = 0;
if (space1 + 1 == i && col > 0 && 2 == space_type) {//left
code = in_queue(q, board, cur_code, i, space1);
}
else if (i + 2 == space1 && col + 2 < WIDTH && 2 == space_type) {//right
code = in_queue(q, board, cur_code, i, i + 1);
}
else if (space1 + WIDTH == i && 1 == space_type) {//up
code = in_queue(q, board, cur_code, i, space1);
}
else if (i + 2 * WIDTH == space1 && 1 == space_type) {//down
code = in_queue(q, board, cur_code, i, i + WIDTH);
}
if (10 == (code & 0xf)) {
this->end_code = code;
return ans;
}
//flag[i+1]=flag[i+WIDTH]=flag[i+WIDTH+1]=true;
flag[i + WIDTH] = flag[i + WIDTH + 1] = true;
++i;
}
}
}
}
return -1;
}
std::vector<std::pair<int, int>> get_directions(const std::string& board, unsigned before, unsigned after) {
int x1 = before / WIDTH, y1 = before % WIDTH, x2 = after / WIDTH, y2 = after % WIDTH;
if (x1 - x2 == y1 - y2 || x1 - x2 == y2 - y1) {
if (0 == board[x2 * WIDTH + y1]) {
return { std::make_pair(x2 - x1,0),std::make_pair(0,y2 - y1) };
}
return { std::make_pair(0,y2 - y1),std::make_pair(x2 - x1,0) };
}
return { std::make_pair(x2 - x1,y2 - y1) };
}
void traverse(const std::string& init, const unsigned& end_code) {
std::stack<State> st;
unsigned start_code = encode(init);
unsigned state = end_code;
while (state != start_code) {
State temp = table[state];
state = temp.pre_state;
st.push(temp);
}
std::string board = init;
print_board(board);
while (!st.empty()) {
State temp = st.top();
st.pop();
std::vector<std::pair<int, int>> directions = get_directions(board, temp.before, temp.after);
for (int i = 0; i < directions.size(); ++i) {
if (i > 0) {
printf(",");
}
printf("(%d,%d)", directions[i].first, directions[i].second);
}
printf("\n");
move_chess(board, temp.before, temp.after);
print_board(board);
}
}
public:
Klotski_AI() :init(CHESS_NUM,0), end_code(0) {
}
Klotski_AI(const std::string& init) :init(init), end_code(0) {
assert(is_valid(init));
}
void set_board(unsigned int code= 17133217) {
if (17133217 == code) {//横刀立马(81)
decode(code, this->init);
}
else if (2465185 == code) {//指挥若定(70)
/*{
2,4,4,2,
2,4,4,2,
1,3,3,1,
2,1,1,2,
2,0,0,2
} */
decode(code, this->init);
}
else if (24488449 == code) {//将拥曹营(72)
/*{
0,4,4,0,
2,4,4,2,
2,2,2,2,
1,2,2,1,
3,3,1,1
} */
decode(code, this->init);
}
}
void set_board(const std::string& init) {
assert(is_valid(init));
this->init = init;
}
int solve() {
return bfs(this->init);
}
void traverse() {
if (end_code != 0) {
traverse(this->init, this->end_code);
}
}
};
int main() {
//1:C,2:V,3:H,4:G
Klotski_AI solver;
solver.set_board(17133217);
//solver.set_board(2465185);
//solver.set_board(24488449);
printf("\n");
LARGE_INTEGER m_nFreq;
LARGE_INTEGER m_nBeginTime;
LARGE_INTEGER nEndTime;
QueryPerformanceFrequency(&m_nFreq); // 获取时钟周期
QueryPerformanceCounter(&m_nBeginTime); // 获取时钟计数
int ans = solver.solve();
QueryPerformanceCounter(&nEndTime);
printf("%d\n", ans);
printf("cost time:%lfms\n", (double)(nEndTime.QuadPart - m_nBeginTime.QuadPart) * 1000 / m_nFreq.QuadPart);
solver.traverse();
return 0;
}
参考
https://blog.csdn.net/sinat_41657218/article/details/80435135?spm=1001.2014.3001.5502
http://blog.lzh.today/klotski-solver/
https://github.com/DianaVasiliu/Klotski-solver
https://tieba.baidu.com/p/5354792142
https://www.cnblogs.com/zhenyulu/category/14888.html