高血压小实验
国际象棋,但是双方每一步的决策都是从所有合法决策中等概率随机选择。
根据统计大概有
以下代码会读取一个数作为两步棋之间的时间间隔,以 ms 为单位(方便观看)。并按照开头所述策略进行一局对局。在棋局结束后会生成一个文件,其中包含该对局的 PGN 代码,可以导入 lichess.org 等国际象棋软件进行分析。已知问题:这个 PGN 在结果为一方将杀另一方获胜的时候,最后一步会显示为 + 而不是 #,待修。
一些短对局(少于 30 步)的分析结果可以达到:
- chess.com 上双方精度都过低无法显示
- lichess.org 上,单方 Inaccuracy 数 + Mistake 数 + Blunder 数 = 总步数
#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;
}
一些奇怪的记录:
- SEED 1570354099107600 随出白方 3 步杀