华容道AI

棋盘


空格: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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Nightmare004

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值