// Piece-geometry study for the Maharajah variant. // For each piece built from movement atoms: empty-board mobility, colour-binding, // board diameter, and a full K+X vs K retrograde solve (win %, longest mate). #include #include #include typedef struct { int dx, dy, ride; } Vec; typedef struct { const char *name; const char *betza; Vec v[64]; int n; } Piece; static void add8(Piece *p, int a, int b, int ride) { int s[8][2] = {{a,b},{b,a},{-a,b},{-b,a},{a,-b},{b,-a},{-a,-b},{-b,-a}}; for (int i = 0; i < 8; i++) { int dup = 0; for (int j = 0; j < p->n; j++) if (p->v[j].dx == s[i][0] && p->v[j].dy == s[i][1]) { dup = 1; if (ride) p->v[j].ride = 1; } if (!dup) { p->v[p->n].dx = s[i][0]; p->v[p->n].dy = s[i][1]; p->v[p->n].ride = ride; p->n++; } } } // atoms: W F N leap, R B riders (R=W ridden, B=F ridden), NN nightrider static Piece make(const char *name, const char *betza, const char *atoms) { Piece p; memset(&p, 0, sizeof p); p.name = name; p.betza = betza; for (const char *c = atoms; *c; c++) { switch (*c) { case 'W': add8(&p, 1, 0, 0); break; case 'F': add8(&p, 1, 1, 0); break; case 'N': add8(&p, 2, 1, 0); break; case 'R': add8(&p, 1, 0, 1); break; case 'B': add8(&p, 1, 1, 1); break; case 'X': add8(&p, 2, 1, 1); break; // nightrider } } return p; } #define SQ(x,y) ((y)*8+(x)) static int adj(int a, int b) { int dx = abs(a%8 - b%8), dy = abs(a/8 - b/8); return dx <= 1 && dy <= 1 && a != b; } // does piece at p attack target t, given blockers b1,b2 (-1 = none)? static int attacks(const Piece *P, int p, int t, int b1, int b2) { int px = p%8, py = p/8; for (int i = 0; i < P->n; i++) { int x = px, y = py; for (;;) { x += P->v[i].dx; y += P->v[i].dy; if (x < 0 || x > 7 || y < 0 || y > 7) break; int s = SQ(x,y); if (s == t) return 1; if (s == b1 || s == b2) break; if (!P->v[i].ride) break; } } return 0; } static int piece_moves(const Piece *P, int p, int b1, int b2, int *out) { int n = 0, px = p%8, py = p/8; for (int i = 0; i < P->n; i++) { int x = px, y = py; for (;;) { x += P->v[i].dx; y += P->v[i].dy; if (x < 0 || x > 7 || y < 0 || y > 7) break; int s = SQ(x,y); if (s == b1 || s == b2) break; out[n++] = s; if (!P->v[i].ride) break; } } return n; } #define IDX(wk,x,bk) (((wk)*64+(x))*64+(bk)) static signed short W[262144], B[262144]; // W: wtm dtm plies to mate (0 unknown,-1 illegal); B: btm lost-in plies (0 unknown, -1 illegal) static void solve(const Piece *P, double *winpct, int *maxdtm) { // legality for (int wk = 0; wk < 64; wk++) for (int x = 0; x < 64; x++) for (int bk = 0; bk < 64; bk++) { int i = IDX(wk,x,bk); int ok = wk != x && wk != bk && x != bk && !adj(wk,bk); W[i] = (ok && !attacks(P, x, bk, wk, -1)) ? 0 : -1; B[i] = ok ? 0 : -1; } // mates (btm, ply 0 encoded as 1) for (int wk = 0; wk < 64; wk++) for (int x = 0; x < 64; x++) for (int bk = 0; bk < 64; bk++) { int i = IDX(wk,x,bk); if (B[i] < 0) continue; if (!attacks(P, x, bk, wk, -1)) continue; int has = 0; for (int d = 0; d < 64 && !has; d++) { if (!adj(bk,d)) continue; if (adj(d,wk) || d == wk) continue; if (d == x) { has = 1; break; } // capture of unprotected piece (wk not adjacent, checked above) if (attacks(P, x, d, wk, -1)) continue; has = 1; } if (!has) B[i] = 1; } int changed = 1, ply = 1; while (changed) { changed = 0; // white to move: win in ply+1 if a move reaches btm lost == ply for (int wk = 0; wk < 64; wk++) for (int x = 0; x < 64; x++) for (int bk = 0; bk < 64; bk++) { int i = IDX(wk,x,bk); if (W[i] != 0) continue; int found = 0; for (int d = 0; d < 64 && !found; d++) { if (!adj(wk,d) || d == x || adj(d,bk) || d == bk) continue; int j = IDX(d,x,bk); if (B[j] == ply) found = 1; } if (!found) { int mv[64]; int n = piece_moves(P, x, wk, bk, mv); for (int k = 0; k < n && !found; k++) { int j = IDX(wk,mv[k],bk); if (B[j] == ply) found = 1; } } if (found) { W[i] = ply + 1; changed = 1; } } // black to move: lost in ply+2 if every move leads to a wtm win already known for (int wk = 0; wk < 64; wk++) for (int x = 0; x < 64; x++) for (int bk = 0; bk < 64; bk++) { int i = IDX(wk,x,bk); if (B[i] != 0) continue; int has = 0, all = 1; for (int d = 0; d < 64 && all; d++) { if (!adj(bk,d) || d == wk || adj(d,wk)) continue; if (d == x) { has = 1; all = 0; break; } if (attacks(P, x, d, wk, -1)) continue; has = 1; int j = IDX(wk,x,d); if (W[j] <= 0) all = 0; } if (has && all) { B[i] = ply + 2; changed = 1; } } ply += 2; if (ply > 400) break; } long legal = 0, won = 0; int mx = 0; for (int i = 0; i < 262144; i++) if (W[i] >= 0) { legal++; if (W[i] > 0) { won++; if (W[i] > mx) mx = W[i]; } } *winpct = 100.0 * won / legal; *maxdtm = mx / 2; } int main(void) { Piece ps[] = { make("Wazir (W)", "W", "W"), make("Ferz (F)", "F", "F"), make("Knight", "N", "N"), make("Commoner (non-royal king)", "K", "WF"), make("Bishop", "B", "B"), make("Rook", "R", "R"), make("Queen", "Q", "RB"), make("Archbishop", "BN", "BN"), make("Chancellor", "RN", "RN"), make("Maharajah", "QN", "RBN"), make("W+N", "WN", "WN"), make("F+N", "FN", "FN"), make("Centaur (K+N)", "KN", "WFN"), make("Nightrider", "NN", "X"), make("Dragon Horse (B+W)", "BW", "BW"), make("Dragon King (R+F)", "RF", "RF"), make("Dragon Horse+N", "BWN", "BWN"), make("Dragon King+N", "RFN", "RFN"), }; int np = sizeof ps / sizeof ps[0]; printf("| Piece | Betza | avg | corner | centre | colour-bound | diameter | KXK win%% | longest mate |\n"); for (int k = 0; k < np; k++) { Piece *P = &ps[k]; int sum = 0, mn = 99, mxm = 0, mv[64]; for (int s = 0; s < 64; s++) { int n = piece_moves(P, s, -1, -1, mv); sum += n; if (n < mn) mn = n; if (n > mxm) mxm = n; } // diameter + colour binding: BFS from every square int diam = 0, bound = 0; for (int s = 0; s < 64; s++) { int dist[64]; for (int t = 0; t < 64; t++) dist[t] = -1; int q[64], h = 0, tl = 0; q[tl++] = s; dist[s] = 0; while (h < tl) { int u = q[h++]; int n = piece_moves(P, u, -1, -1, mv); for (int j = 0; j < n; j++) if (dist[mv[j]] < 0) { dist[mv[j]] = dist[u] + 1; q[tl++] = mv[j]; } } for (int t = 0; t < 64; t++) { if (dist[t] < 0) bound = 1; else if (dist[t] > diam) diam = dist[t]; } } double wp; int md; solve(P, &wp, &md); printf("| %s | %s | %.2f | %d | %d | %s | %d | %.2f | %d |\n", P->name, P->betza, sum / 64.0, mn, mxm, bound ? "yes" : "no", diam, wp, md); fflush(stdout); } return 0; }