细心细心细心
查看原帖
细心细心细心
552532
MeteorDream楼主2021/9/14 23:39

几点小提醒 (其实只是想发下代码,有没有人看无所谓)

我自己做得挺顺的,虽然写了很久就是了……

因为中途有打印调试,提前排除了几个低级bug(包括棋子放错位置,函数传错参数等),最后提交只有两个问题:

  1. 移动棋子的操作中棋子在棋盘上的位置移动了,但棋子本身的坐标没变,导致出现访问空指针的异常
  2. 检查是否形成将军的时候采用遍历所有棋子的方法,但没有排除掉已经被吃的棋子,导致结果错误

思路还是很清晰的,但很容易犯一些小错误,不过看来题解区大佬的解答感觉自己写得还是复杂了些

#include <bits/stdc++.h>
using namespace std;

// 阵营
#define Camp bool
#define RED true
#define BLUE false

// 棋类
#define PIECE int     // 棋子
#define CAPTAIN 0     // 王
#define GUARD 1       // 士
#define ELEPHANT 2    // 象
#define HORSE 3       // 马
#define CAR 4         // 车
#define DUCK 5        // 鸭
#define SOLDIER 6     // 兵

// 棋子
class piece {
public:
    piece(Camp _color = RED, PIECE _p = CAPTAIN, int _x = 0, int _y = 0);
    int get_x();                // 返回当前横坐标
    int get_y();                // 返回当前纵坐标
    bool isLive();              // 返回是否还在棋盘上,若被吃掉了返回false
    void dead();                // 被吃
    bool eat(piece* p);         // 吃掉对方一个棋子
    Camp camp();                // 返回所属阵营
    PIECE type();               // 返回当前是哪一种棋子
    string print_piece();       // 返回类型对应的单个中文字符
    string tostring();          // 返回 [阵营] [类型] 的字符串
    bool move(int x, int y);    // 移动棋子到指定位置

private:
    Camp color;    // 所属阵营,true 表示红方,false 表示黑方
    PIECE p;       // 棋子类型
    int x, y;      // 当前坐标
    bool save;     // 是否还在棋盘上
};

// 双方阵营基类
class player {
public:
    piece* p[16];    // 指针数组
};

// 红方阵营
class red : public player {
public:
    red() {
        p[0] = new piece(RED, CAPTAIN, 0, 4);
        p[1] = new piece(RED, GUARD, 0, 3);
        p[2] = new piece(RED, GUARD, 0, 5);
        p[3] = new piece(RED, ELEPHANT, 0, 2);
        p[4] = new piece(RED, ELEPHANT, 0, 6);
        p[5] = new piece(RED, HORSE, 0, 1);
        p[6] = new piece(RED, HORSE, 0, 7);
        p[7] = new piece(RED, CAR, 0, 0);
        p[8] = new piece(RED, CAR, 0, 8);
        p[9] = new piece(RED, DUCK, 2, 0);
        p[10] = new piece(RED, DUCK, 2, 8);
        p[11] = new piece(RED, SOLDIER, 3, 0);
        p[12] = new piece(RED, SOLDIER, 3, 2);
        p[13] = new piece(RED, SOLDIER, 3, 4);
        p[14] = new piece(RED, SOLDIER, 3, 6);
        p[15] = new piece(RED, SOLDIER, 3, 8);
    }
    ~red() {
        for (int i = 0; i < 16; ++i) {
            delete p[i];
        }
    }
};

// 蓝方阵营
class blue : public player {
public:
    blue() {
        p[0] = new piece(BLUE, CAPTAIN, 9, 4);
        p[1] = new piece(BLUE, GUARD, 9, 3);
        p[2] = new piece(BLUE, GUARD, 9, 5);
        p[3] = new piece(BLUE, ELEPHANT, 9, 2);
        p[4] = new piece(BLUE, ELEPHANT, 9, 6);
        p[5] = new piece(BLUE, HORSE, 9, 1);
        p[6] = new piece(BLUE, HORSE, 9, 7);
        p[7] = new piece(BLUE, CAR, 9, 0);
        p[8] = new piece(BLUE, CAR, 9, 8);
        p[9] = new piece(BLUE, DUCK, 7, 0);
        p[10] = new piece(BLUE, DUCK, 7, 8);
        p[11] = new piece(BLUE, SOLDIER, 6, 0);
        p[12] = new piece(BLUE, SOLDIER, 6, 2);
        p[13] = new piece(BLUE, SOLDIER, 6, 4);
        p[14] = new piece(BLUE, SOLDIER, 6, 6);
        p[15] = new piece(BLUE, SOLDIER, 6, 8);
    }
    ~blue() {
        for (int i = 0; i < 16; ++i) {
            delete p[i];
        }
    }
};

// 棋盘
class board {
public:
    board();                                                // 初始化棋盘上的棋子
    bool canMove(int x1, int y1, int x2, int y2);           // 能否将棋子移动到对应位置
    bool Move(int x1, int y1, int x2, int y2, Camp cap);    // 移动棋子
    bool isEnd();                                           // 本局游戏是否结束
    bool isDanger();                                        // 当前棋局是否处于将军局面
    piece* Lasteat();                                       // 返回最后一步被吃掉的棋子
    piece* get_piece(int x, int y);                         // 获取某个位置的棋子
    void printBoard();                                      // 打印棋盘

private:
    piece* b[10][9];       // 10x9 的网格棋盘
    red player_red;        // 红方玩家
    blue player_blue;      // 蓝方玩家
    piece* d = nullptr;    // 最后一步被吃的棋子

    // 玩家 p1 是否被玩家 p2 将军
    bool check(player* p1, player* p2);

    // 能否将对应棋子移动到对应位置
    bool canMoveCaptain(int x1, int y1, int x2, int y2);
    bool canMoveGuard(int x1, int y1, int x2, int y2);
    bool canMoveElephant(int x1, int y1, int x2, int y2);
    bool canMoveHorse(int x1, int y1, int x2, int y2);
    bool canMoveCar(int x1, int y1, int x2, int y2);
    bool canMoveDuck(int x1, int y1, int x2, int y2);
    bool canMoveSoldier(int x1, int y1, int x2, int y2);
};

// -------------------  棋盘的方法成员  -----------------------

// 初始化棋盘
board::board()
    : d(nullptr) {
    // 棋盘初始化
    for (int i = 0; i < 10; ++i) {
        for (int j = 0; j < 9; ++j) {
            b[i][j] = nullptr;
        }
    }
    // 红方棋子摆上棋盘
    for (int i = 0; i < 16; ++i) {
        piece* cur = player_red.p[i];
        int x = cur->get_x(), y = cur->get_y();
        b[x][y] = cur;
    }
    // 蓝方棋子摆上棋盘
    for (int i = 0; i < 16; ++i) {
        piece* cur = player_blue.p[i];
        int x = cur->get_x(), y = cur->get_y();
        b[x][y] = cur;
    }
}

// 能否将棋子移动到对应位置(假设起始位置合法且有一枚棋子)
bool board::canMove(int x1, int y1, int x2, int y2) {
    // 目标位置不合法
    if (x2 < 0 || x2 > 9 || y2 < 0 || y2 > 8) return false;
    // 目标位置有己方棋子
    if (b[x2][y2] != nullptr && b[x1][y1]->camp() == b[x2][y2]->camp())
        return false;

    // 根据当前位置棋子类型调用判断函数
    switch (b[x1][y1]->type()) {
        case CAPTAIN: return canMoveCaptain(x1, y1, x2, y2);
        case GUARD: return canMoveGuard(x1, y1, x2, y2);
        case ELEPHANT: return canMoveElephant(x1, y1, x2, y2);
        case HORSE: return canMoveHorse(x1, y1, x2, y2);
        case CAR: return canMoveCar(x1, y1, x2, y2);
        case DUCK: return canMoveDuck(x1, y1, x2, y2);
        case SOLDIER: return canMoveSoldier(x1, y1, x2, y2);
        default: return false;
    }
}

// 移动棋子,移动成功返回 true,否则返回 false
bool board::Move(int x1, int y1, int x2, int y2, Camp cap) {
    // 游戏已经结束不可移动
    if (isEnd()) return false;
    // 检查起始位置是否合法,是否有一个棋子
    if (x1 < 0 || x1 > 9 || y1 < 0 || y1 > 8 || b[x1][y1] == nullptr)
        return false;
    // 起始位置的棋子是否是所给的阵营
    if (b[x1][y1]->camp() != cap) return false;
    // 检查目的地是否可达
    if (canMove(x1, y1, x2, y2)) {
        // 目的地可达那就移动
        if (b[x2][y2] != nullptr) {
            // 吃掉目的地的棋子
            b[x2][y2]->dead();
            d = b[x2][y2];
        }
        else
            d = nullptr;
        // 移动棋子
        b[x1][y1]->move(x2, y2);
        b[x2][y2] = b[x1][y1];
        b[x1][y1] = nullptr;
        return true;
    }
    else
        return false;
}

// 游戏是否结束
inline bool board::isEnd() {
    // 判断双方王是否存活即可
    if (player_red.p[0]->isLive() && player_blue.p[0]->isLive())
        return false;
    else
        return true;
}

// 玩家 p1 是否被玩家 p2 将军
bool board::check(player* p1, player* p2) {
    // p1 王的位置
    int x = p1->p[0]->get_x(), y = p1->p[0]->get_y();
    // 逐个棋子检查能否攻击到王
    for (int i = 0; i < 16; ++i) {
        if (p2->p[i]->isLive() && canMove(p2->p[i]->get_x(), p2->p[i]->get_y(), x, y))
            return true;
    }
    return false;
}

inline bool board::isDanger() {
    // 结束局面不可能是将军局面
    if (isEnd()) return false;
    return check(&player_red, &player_blue) || check(&player_blue, &player_red);
}

inline piece* board::Lasteat() {
    return d;
}

inline piece* board::get_piece(int x, int y) {
    return b[x][y];
}

// 能否将 王 移动到对应位置(假设当前位置有王且目标位置合法)
inline bool board::canMoveCaptain(int x1, int y1, int x2, int y2) {
    // 王只能上下左右移动
    if (x1 == x2 && (y1 - y2 == 1 || y2 - y1 == 1)) return true;
    if (y1 == y2 && (x1 - x2 == 1 || x2 - x1 == 1)) return true;
    return false;
}

// 能否将 士 移动到对应位置(假设当前位置有士且目标位置合法)
inline bool board::canMoveGuard(int x1, int y1, int x2, int y2) {
    // 士只能斜着移动
    if (x1 + 1 == x2 && y1 + 1 == y2)
        return true;
    else if (x1 - 1 == x2 && y1 - 1 == y2)
        return true;
    else if (x1 + 1 == x2 && y1 - 1 == y2)
        return true;
    else if (x1 - 1 == x2 && y1 + 1 == y2)
        return true;
    return false;
}

// 能否将 象 移动到对应位置(假设当前位置有象且目标位置合法)
inline bool board::canMoveElephant(int x1, int y1, int x2, int y2) {
    // 象只能走田字且中间不能有棋子
    if (x1 + 2 == x2 && y1 + 2 == y2 && b[x1 + 1][y1 + 1] == nullptr)
        return true;
    else if (x1 - 2 == x2 && y1 + 2 == y2 && b[x1 - 1][y1 + 1] == nullptr)
        return true;
    else if (x1 + 2 == x2 && y1 - 2 == y2 && b[x1 + 1][y1 - 1] == nullptr)
        return true;
    else if (x1 - 2 == x2 && y1 - 2 == y2 && b[x1 - 1][y1 - 1] == nullptr)
        return true;
    return false;
}

// 能否将 马 移动到对应位置(假设当前位置有马且目标位置合法)
inline bool board::canMoveHorse(int x1, int y1, int x2, int y2) {
    // 马走日字且中间不能有其他棋子
    for (int i = -1; i <= 1; i += 2) {
        for (int j = -1; j <= 1; j += 2) {
            if (x1 + i * 2 == x2 && y1 + j == y2 && b[x1 + i][y1] == nullptr)
                return true;
            if (x1 + i == x2 && y1 + j * 2 == y2 && b[x1][y1 + j] == nullptr)
                return true;
        }
    }
    return false;
}

// 能否将 车 移动到对应位置(假设当前位置有车且目标位置合法)
inline bool board::canMoveCar(int x1, int y1, int x2, int y2) {
    // 车可以在不跨越其他棋子的情况下到达同行或同列的任意位置
    if (x1 == x2) {
        int dir = y2 - y1 > 0 ? 1 : -1;    // 向左还是向右
        bool tmp = true;
        for (int i = y1 + dir; i != y2; i += dir) {
            if (b[x1][i] != nullptr) {
                tmp = false;
                break;
            }
        }
        if (tmp) return true;
    }
    if (y1 == y2) {
        int dir = x2 - x1 > 0 ? 1 : -1;
        bool tmp = true;
        for (int i = x1 + dir; i != x2; i += dir) {
            if (b[i][y1] != nullptr) {
                tmp = false;
                break;
            }
        }
        if (tmp) return true;
    }
    return false;
}

// 能否将 鸭 移动到对应位置(假设当前位置有鸭且目标位置合法)
inline bool board::canMoveDuck(int x1, int y1, int x2, int y2) {
    // 不懂,照本宣科
    for (int i = -1; i <= 1; i += 2) {
        for (int j = -1; j <= 1; j += 2) {
            if (x1 + i * 3 == x2 && y1 + j * 2 == y2 && b[x1 + i * 2][y1 + j] == nullptr && b[x1 + i][y1] == nullptr)
                return true;
            if (x1 + i * 2 == x2 && y1 + j * 3 == y2 && b[x1 + i][y1 + j * 2] == nullptr && b[x1][y1 + j] == nullptr)
                return true;
        }
    }
    return false;
}

// 能否将 兵 移动到对应位置(假设当前位置有兵且目标位置合法)
inline bool board::canMoveSoldier(int x1, int y1, int x2, int y2) {
    // 周围八个位置
    for (int i = -1; i <= 1; ++i) {
        for (int j = -1; j <= 1; ++j) {
            if (x1 + i == x2 && y1 + j == y2)
                return true;
        }
    }
    return false;
}

// 格式化输出棋盘
inline void board::printBoard() {
    for (int i = 9; i >= 0; --i) {
        for (int j = 8; j >= 0; --j) {
            if (b[i][j] == nullptr)
                cout << "十 ";
            else {
                cout << b[i][j]->print_piece() << ' ';
            }
        }
        putchar('\n');
    }
}

// -------------------  END - 棋盘的方法成员  -----------------------

// -------------------  棋子的方法成员  -----------------------

// 构造函数,传入阵营,类型,坐标
piece::piece(Camp _color, PIECE _p, int _x, int _y)
    : color(_color), p(_p), x(_x), y(_y) {
    save = true;
}

// 返回当前横坐标
inline int piece::get_x() {
    return this->x;
}

//  返回当前纵坐标
inline int piece::get_y() {
    return this->y;
}

// 是否还在棋盘上
inline bool piece::isLive() {
    return this->save;
}

// 被吃
inline void piece::dead() {
    this->save = false;
}

// 吃掉棋子
inline bool piece::eat(piece* p) {
    p->dead();
    return true;
}

// 移动棋子到指定位置
inline bool piece::move(int x, int y) {
    this->x = x;
    this->y = y;
    return true;
}

// 返回所属阵营
inline Camp piece::camp() {
    return this->color;
}

// 返回棋子类型
inline PIECE piece::type() {
    return this->p;
}

inline string piece::print_piece() {
    switch (p) {
        case CAPTAIN: return "王";
        case GUARD: return "士";
        case ELEPHANT: return "象";
        case HORSE: return "马";
        case CAR: return "车";
        case DUCK: return "鸭";
        case SOLDIER: return "兵";
        default: return "无";
    }
}

// 返回 [阵营] [类型] 的字符串
inline string piece::tostring() {
    string c = color == RED ? "red " : "blue ";
    string t;
    switch (p) {
        case CAPTAIN: t = "captain"; break;
        case GUARD: t = "guard"; break;
        case ELEPHANT: t = "elephant"; break;
        case HORSE: t = "horse"; break;
        case CAR: t = "car"; break;
        case DUCK: t = "duck"; break;
        case SOLDIER: t = "soldier"; break;
        default: t = "err";
    }
    return c + t;
}

// -------------------  END - 棋子的方法成员  -----------------------

int Q;    // 操作序列的长度
int x_s, y_s, x_t, y_t;
Camp c;

int main() {
    // freopen("luogu.in", "r", stdin);      // 输入流重定向
    // freopen("luogu.out", "w", stdout);    // 输出流重定向

    board b;
    c = RED;    // 红方先动

    // b.printBoard();

    scanf("%d", &Q);

    for (int i = 0; i < Q; ++i) {
        scanf("%d%d%d%d", &x_s, &y_s, &x_t, &y_t);
        if (b.Move(x_s, y_s, x_t, y_t, c)) {
            // 这步操作移动了哪个棋子
            cout << b.get_piece(x_t, y_t)->tostring() << ';';
            // 这步操作后,是否存在棋子被移出游戏,如有则还需求出被移出游戏的棋子
            cout << (b.Lasteat() == nullptr ? "NA" : b.Lasteat()->tostring()) << ';';
            // 这步操作后,是否形成将军局面
            cout << (b.isDanger() ? "yes" : "no") << ';';
            // 这步操作后,游戏是否结束
            cout << (b.isEnd() ? "yes" : "no");
            cout << '\n';
            c = c == RED ? BLUE : RED;
        }
        else {
            cout << "Invalid command\n";
        }

        // cout << '\n';
        // b.printBoard();
        // cout << '\n';
    }

    return 0;
}
2021/9/14 23:39
加载中...