高血压小实验

· · 个人记录

国际象棋,但是双方每一步的决策都是从所有合法决策中等概率随机选择。

根据统计大概有 80\% 的棋局是子力不足 / 重复局面 / 50 步和棋(以这些和棋方式结束的棋局大多会进行 240 回合左右),也就是有一定的概率能随出将死或逼和的局面。

以下代码会读取一个数作为两步棋之间的时间间隔,以 ms 为单位(方便观看)。并按照开头所述策略进行一局对局。在棋局结束后会生成一个文件,其中包含该对局的 PGN 代码,可以导入 lichess.org 等国际象棋软件进行分析。已知问题:这个 PGN 在结果为一方将杀另一方获胜的时候,最后一步会显示为 + 而不是 #,待修。

一些短对局(少于 30 步)的分析结果可以达到:

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

// PNBRQK = 123456
// W = +, B = -

const auto SEED = chrono::steady_clock::now().time_since_epoch().count();
//const int64_t SEED = 543877746620000;
mt19937 rnd(SEED);

typedef unsigned long long ull;

const ull BSE = 0xAE3803;

const int Next[8][2] = {{0, 1}, {1, 0}, {1, 1}, {1, -1}, {1, 2}, {2, 1}, {1, -2}, {2, -1}};
const int MaxR[6][8] = {{0, 0, 0, 0, 0, 0, 0, 0},
                        {0, 0, 0, 0, 1, 1, 1, 1},
                        {0, 0, 9, 9, 0, 0, 0, 0},
                        {9, 9, 0, 0, 0, 0, 0, 0},
                        {9, 9, 9, 9, 0, 0, 0, 0},
                        {1, 1, 1, 1, 0, 0, 0, 0}};
const string PIECES = "EPNBRQK";
string pgn = "";

inline void Movepos(int x, int y) {
    COORD position;
    position.X = x; position.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), position);
}

struct Chessboard {
    int a[8][8];
    bool mk[2], mr[2][2];
    Chessboard() {
        memset(a, 0, sizeof(a));
        for (int i = 0;i < 8;i++) {
            a[1][i] = 1;
            a[6][i] = -1;
        }
        a[0][0] = a[0][7] = 4;
        a[0][1] = a[0][6] = 2;
        a[0][2] = a[0][5] = 3;
        a[0][3] = 5; a[0][4] = 6;
        a[7][0] = a[7][7] = -4;
        a[7][1] = a[7][6] = -2;
        a[7][2] = a[7][5] = -3;
        a[7][3] = -5; a[7][4] = -6;
        memset(mk, 0, sizeof(mk));
        memset(mr, 0, sizeof(mr));
    }
    inline ull getHash() {
        ull v = 0;
        for (int i = 0;i < 8;i++) {
            for (int j = 0;j < 8;j++) v = v * BSE + (a[i][j] + 10);
        }
        return v;
    }
    inline void Print() {
        Movepos(0, 0);
        cout << "\033[0;48;2;127;127;127m+-+-+-+-+-+-+-+-+\033[0m" << endl;
        for (int i = 7;i >= 0;i--) {
            cout << "\033[0;48;2;127;127;127m|";
            for (int j = 0;j < 8;j++) {
                if (a[i][j] == 0) {
                    cout << "\033[0;43m ";
                } else if (a[i][j] > 0) {
                    cout << "\033[0;30;47m" << PIECES[a[i][j]];
                } else {
                    cout << "\033[0;37;40m" << PIECES[-a[i][j]];
                }
                cout << "\033[0;48;2;127;127;127m|\033[0m";
            }
            cout << "\033[0m" << endl;
            cout << "\033[0;48;2;127;127;127m+-+-+-+-+-+-+-+-+\033[0m" << endl;
        }
    }
    inline void DBG() {
        cout << "\033[0m";
        for (int i = 7;i >= 0;i--) {
            for (int j = 0;j <= 7;j++) {
                if (a[i][j] < 0) cout << (char)(PIECES[-a[i][j]] + 32);
                else if (a[i][j] == 0) cout << ".";
                else cout << PIECES[a[i][j]];
            }
            cout << endl;
        }
        cout << endl;
    }
};
struct ChessOpt {
    Chessboard cur, lst;
    unordered_map <unsigned long long, int> mp;
    vector <pair <Chessboard, string> > des;
    int c50;
    inline string Conv(int x, int y) {
        string res = "";
        res += (char)('a' + y);
        res += (char)('1' + x);
        return res;
    }
    inline void MovePwn(int x, int y) {
        int tp = cur.a[x][y];
        assert(tp == 1 || tp == -1);
        Chessboard nxt;
        // move forward 1
        if (cur.a[x + tp][y] == 0) {
            nxt = cur;
            nxt.a[x][y] = 0;
            if (x + tp == 0 || x + tp == 7) {
                for (int k = 2;k <= 5;k++) {
                    nxt.a[x + tp][y] = tp * k;
                    string mv = Conv(x + tp, y);
                    mv += '=';
                    mv += PIECES[k];
                    des.push_back(make_pair(nxt, mv));
                }
            } else {
                nxt.a[x + tp][y] = tp;
                string mv = Conv(x + tp, y);
                des.push_back(make_pair(nxt, mv));
            }
        }
        // move forward 2
        if (x * 2 + 5 * tp == 7) { // check initial position
            if (cur.a[x + tp][y] == 0 && cur.a[x + 2 * tp][y] == 0) {
                nxt = cur;
                nxt.a[x][y] = 0;
                nxt.a[x + 2 * tp][y] = tp;
                string mv = Conv(x + 2 * tp, y);
                des.push_back(make_pair(nxt, mv));
            }
        }
        // capture
        for (int k = -1;k <= 1;k += 2) {
            if (y + k >= 0 && y + k <= 7 && cur.a[x + tp][y + k] * cur.a[x][y] < 0) {
                nxt = cur;
                nxt.a[x][y] = 0;
                if (x + tp == 0 || x + tp == 7) {
                    for (int l = 2;l <= 5;l++) {
                        nxt.a[x + tp][y + k] = tp * l;
                        string mv = "";
                        mv += (char)(y + 'a');
                        mv += 'x';
                        mv += Conv(x + tp, y + k);
                        mv += '=';
                        mv += PIECES[l];
                        des.push_back(make_pair(nxt, mv));
                    }
                } else {
                    nxt.a[x + tp][y + k] = tp;
                    string mv = "";
                    mv += (char)(y + 'a');
                    mv += 'x';
                    mv += Conv(x + tp, y + k);
                    des.push_back(make_pair(nxt, mv));
                }
            }
        }
        // pass'et capture
        if (x * 2 - tp == 7) {
            for (int k = -1;k <= 1;k += 2) {
                if (y + k >= 0 && y + k <= 7 && cur.a[x][y + k] * tp == -1) {
                    Chessboard dlst;
                    dlst = cur;
                    dlst.a[x + 2 * tp][y + k] = dlst.a[x][y + k];
                    dlst.a[x][y + k] = 0;
                    if (dlst.getHash() == lst.getHash()) {
                        nxt = cur;
                        nxt.a[x][y + k] = 0;
                        nxt.a[x + tp][y + k] = tp;
                        nxt.a[x][y] = 0;
                        string mv;
                        mv += (char)(y + 'a');
                        mv += 'x';
                        mv += Conv(x + tp, y + k);
                        des.push_back(make_pair(nxt, mv));
                    }
                }
            }
        }
    }
    inline void MovePce(int x, int y) {
        int tp = cur.a[x][y];
        Chessboard nxt;
        assert(abs(tp) >= 2 && abs(tp) <= 6);
        int idx = abs(tp) - 1;
        for (int k = 0;k < 8;k++) {
            for (int dir = -1;dir <= 1;dir += 2) {
                for (int dis = 1;dis <= MaxR[idx][k];dis++) {
                    int tx = x + dir * dis * Next[k][0], ty = y + dir * dis * Next[k][1];
                    if (tx < 0 || tx > 7 || ty < 0 || ty > 7) break;
                    if (cur.a[tx][ty] * cur.a[x][y] > 0) break;
                    Chessboard nxt = cur;
                    string mv = "";
                    mv += PIECES[abs(tp)];
                    mv += Conv(x, y);
                    if (cur.a[tx][ty]) mv += 'x';
                    mv += Conv(tx, ty);
                    nxt.a[tx][ty] = cur.a[x][y];
                    nxt.a[x][y] = 0;
                    des.push_back(make_pair(nxt, mv));
                    if (cur.a[tx][ty] * cur.a[x][y] < 0) break;
                }
            }
        }
    }
    inline bool chkCheck(int tp) {
        vector <pair <Chessboard, string> > tdes(des);
        des.clear();
        for (int i = 0;i < 8;i++) {
            for (int j = 0;j < 8;j++) {
                if (cur.a[i][j] * tp < 0) {
                    if (abs(cur.a[i][j]) == 1) MovePwn(i, j);
                    else MovePce(i, j);
                }
            }
        }
        bool uck = 0;
        for (auto [x, s] : des) {
            bool flg = 0;
            for (int i = 0;i < 8;i++) {
                for (int j = 0;j < 8;j++) {
                    if (abs(x.a[i][j]) == 6 && x.a[i][j] * tp > 0) {
                        flg = 1;
                        break;
                    }
                }
            }
            if (!flg) {
                uck = 1;
                break;
            }
        }
        des = tdes;
        return uck;
    }
    inline void chkCastle(int tp) {
        if (chkCheck(tp)) return;
        int rid = (7 - 7 * tp) / 2;
        int tpv;
        if (tp == 1) tpv = 0;
        else tpv = 1;
        if (!cur.mk[tpv]) {
            Chessboard tmp = cur;
            // 0-0-0
            if (!cur.mr[tpv][0] && !cur.a[rid][1] && !cur.a[rid][2] && !cur.a[rid][3]) {
                cur.a[rid][4] = 0;
                cur.a[rid][3] = 6 * tp;
                if (!chkCheck(tp)) {
                    cur.a[rid][3] = 0;
                    cur.a[rid][2] = 6 * tp;
                    if (!chkCheck(tp)) {
                        cur.a[rid][0] = 0;
                        cur.a[rid][3] = 4 * tp;
                        des.push_back(make_pair(cur, (string)("O-O-O")));
                    }
                }
            }
            cur = tmp;
            // 0-0
            if (!cur.mr[tpv][1] && !cur.a[rid][5] && !cur.a[rid][6]) {
                cur.a[rid][4] = 0;
                cur.a[rid][5] = 6 * tp;
                if (!chkCheck(tp)) {
                    cur.a[rid][5] = 0;
                    cur.a[rid][6] = 6 * tp;
                    if (!chkCheck(tp)) {
                        cur.a[rid][7] = 0;
                        cur.a[rid][5] = 4 * tp;
                        des.push_back(make_pair(cur, (string)("O-O")));
                    }
                }
            }
            cur = tmp;
        }
    }
    inline void updMFlag() {
        if (!cur.mk[0]) {
            if (cur.a[0][4] != 6) cur.mk[0] = 1;
        }
        if (!cur.mk[1]) {
            if (cur.a[7][4] != 6) cur.mk[1] = 1;
        }
        if (!cur.mr[0][0]) {
            if (cur.a[0][0] != 4) cur.mr[0][0] = 1;
        }
        if (!cur.mr[0][1]) {
            if (cur.a[0][7] != 4) cur.mr[0][1] = 1;
        }
        if (!cur.mr[1][0]) {
            if (cur.a[7][0] != 4) cur.mr[1][0] = 1;
        }
        if (!cur.mr[1][1]) {
            if (cur.a[7][7] != 4) cur.mr[1][1] = 1;
        }
    }
    inline bool makeMove(int tp) {
        //cout << "\033[0mmakeMove " << tp << endl;
        des.clear();
        for (int i = 0;i < 8;i++) {
            for (int j = 0;j < 8;j++) {
                if (cur.a[i][j] * tp > 0) {
                    if (abs(cur.a[i][j]) == 1) MovePwn(i, j);
                    else MovePce(i, j);
                }
            }
        }
        chkCastle(tp);
        vector <pair <Chessboard, string> > rdes, tmp(des);
        for (auto [x, s] : tmp) {
            //x.DBG();
            Chessboard llst = lst;
            lst = cur;
            cur = x;
            if (!chkCheck(tp)) {
                rdes.push_back(make_pair(x, s));
                //cout << "\033[0mvalid" << endl;
            } else {
                //cout << "\033[0minvalid" << endl;
            }
            cur = lst;
            lst = llst;
        }
        if (rdes.empty()) return 0;
        int idx = rnd() % rdes.size();
        lst = cur;
        cur = rdes[idx].first;
        pgn += rdes[idx].second;
        if (chkCheck(-tp)) pgn += '+';
        pgn += ' ';
        bool flg = 1;
        for (int i = 0;i < 8;i++) {
            for (int j = 0;j < 8;j++) {
                if (cur.a[i][j] != lst.a[i][j]) {
                    if (lst.a[i][j] * cur.a[i][j] != 0 || abs(lst.a[i][j]) == 1 || abs(cur.a[i][j]) == 1) flg = 0;
                }
            }
        }
        if (!flg) c50 = -1;
        mp[cur.getHash()]++;
        return 1;
    }
    inline bool chkInsufficient(int tp) {
        int cnt = 0;
        for (int i = 0;i < 8;i++) {
            for (int j = 0;j < 8;j++) {
                if (cur.a[i][j] * tp > 0) {
                    if (abs(cur.a[i][j]) == 1 || abs(cur.a[i][j]) == 4 || abs(cur.a[i][j]) == 5) return 0;
                    if (abs(cur.a[i][j]) == 2 || abs(cur.a[i][j]) == 3) {
                        cnt++;
                        if (cnt > 1) return 0;
                    }
                }
            }
        }
        return 1;
    }
    inline void Play(int slt) {
        cur.Print();
        cout << endl;
        int stp = 0;
        for (;;) {
            string tmp = pgn;
            stp++;
            string num = "";
            int tstp = stp;
            while (tstp) {
                num += (char)('0' + tstp % 10);
                tstp /= 10;
            }
            reverse(num.begin(), num.end());
            pgn += num;
            pgn += ". ";
            if (!makeMove(1)) {
                pgn = tmp;
                if (chkCheck(1)) {
                    cout << "\033[0m0-1 Checkmate" << endl;
                    return;
                } else {
                    cout << "\033[0m1/2-1/2 Stalemate" << endl;
                    return;
                }
            }
            updMFlag();
            cur.Print();
            if (mp[cur.getHash()] >= 3) {
                cout << "\033[0m1/2-1/2 Repetition" << endl;
                return;
            }
            if (chkInsufficient(1) && chkInsufficient(-1)) {
                cout << "\033[0m1/2-1/2 Insufficient material" << endl;
                return;
            }
            //getchar();
            Sleep(slt);
            if (!makeMove(-1)) {
                if (chkCheck(-1)) {
                    cout << "\033[0m1-0 Checkmate" << endl;
                    return;
                } else {
                    cout << "\033[0m1/2-1/2 Stalemate" << endl;
                    return;
                }
            }
            updMFlag();
            cur.Print();
            c50++;
            if (c50 >= 50) {
                cout << "\033[0m1/2-1/2 50 moves rule" << endl;
                return;
            }
            if (mp[cur.getHash()] >= 3) {
                cout << "\033[0m1/2-1/2 Repetition" << endl;
                return;
            }
            if (chkInsufficient(1) && chkInsufficient(-1)) {
                cout << "\033[0m1/2-1/2 Insufficient material" << endl;
                return;
            }
            //getchar();
            Sleep(slt);
        }
    }
};
ChessOpt obj;

int main() {
    int slt; cin >> slt;
    system("cls");
    obj.Play(slt);
    ofstream fout("result.out");
    fout << pgn << endl;
    cerr << "SEED = " << SEED << endl;
    return 0;
}

一些奇怪的记录: