Several small improvements + integrate options + first working draft of Cwda
[vchess.git] / client / src / variants / Knightmate2.js
1 import { ChessRules } from "@/base_rules";
2
3 export class Knightmate2Rules extends ChessRules {
4
5 static get HasFlags() {
6 return false;
7 }
8
9 static get COMMONER() {
10 return "c";
11 }
12
13 static get PIECES() {
14 return ChessRules.PIECES.concat([V.COMMONER]);
15 }
16
17 getPpath(b) {
18 return ([V.KING, V.COMMONER].includes(b[1]) ? "Knightmate/" : "") + b;
19 }
20
21 static IsGoodPosition(position) {
22 if (position.length == 0) return false;
23 const rows = position.split("/");
24 if (rows.length != V.size.x) return false;
25 let kings = { "k": 0, "K": 0 };
26 for (let row of rows) {
27 let sumElts = 0;
28 for (let i = 0; i < row.length; i++) {
29 if (['K','k'].includes(row[i])) kings[row[i]]++;
30 if (V.PIECES.includes(row[i].toLowerCase())) sumElts++;
31 else {
32 const num = parseInt(row[i], 10);
33 if (isNaN(num) || num <= 0) return false;
34 sumElts += num;
35 }
36 }
37 if (sumElts != V.size.y) return false;
38 }
39 // 1 or 2 kings should be on board.
40 if (Object.values(kings).some(k => ![1, 2].includes(k))) return false;
41 return true;
42 }
43
44 scanKings() {}
45
46 static GenRandInitFen(options) {
47 return (
48 ChessRules.GenRandInitFen(options)
49 .replace(/k/g, 'c').replace(/K/g, 'C')
50 .replace(/n/g, 'k').replace(/N/g, 'K')
51 );
52 }
53
54 getPotentialMovesFrom([x, y]) {
55 switch (this.getPiece(x, y)) {
56 case V.COMMONER:
57 return this.getPotentialCommonerMoves([x, y]);
58 default:
59 return super.getPotentialMovesFrom([x, y]);
60 }
61 }
62
63 getPotentialCommonerMoves(sq) {
64 return this.getSlideNJumpMoves(
65 sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1);
66 }
67
68 getPotentialKingMoves(sq) {
69 return super.getPotentialKnightMoves(sq);
70 }
71
72 isAttacked(sq, color) {
73 return (
74 this.isAttackedByCommoner(sq, color) ||
75 this.isAttackedByPawn(sq, color) ||
76 this.isAttackedByRook(sq, color) ||
77 this.isAttackedByBishop(sq, color) ||
78 this.isAttackedByQueen(sq, color) ||
79 this.isAttackedByKing(sq, color)
80 );
81 }
82
83 isAttackedByKing(sq, color) {
84 return this.isAttackedBySlideNJump(
85 sq, color, V.KING, V.steps[V.KNIGHT], 1);
86 }
87
88 isAttackedByCommoner(sq, color) {
89 return this.isAttackedBySlideNJump(
90 sq, color, V.COMMONER, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1);
91 }
92
93 postPlay() {}
94 postUndo() {}
95
96 // NOTE: 4 next functions (almost) copy-paste from Spartan Chess
97 getKingsPos(color) {
98 let kings = [];
99 for (let i=0; i<8; i++) {
100 for (let j=0; j<8; j++) {
101 if (
102 this.board[i][j] != V.EMPTY &&
103 this.getColor(i, j) == color &&
104 this.getPiece(i, j) == V.KING
105 ) {
106 kings.push({ x: i, y: j });
107 }
108 }
109 }
110 return kings;
111 }
112
113 getCheckSquares() {
114 const color = this.turn;
115 const oppCol = V.GetOppCol(color);
116 const kings = this.getKingsPos(color);
117 let res = [];
118 for (let i of [0, 1]) {
119 if (
120 kings.length >= i+1 &&
121 this.isAttacked([kings[i].x, kings[i].y], oppCol)
122 ) {
123 res.push([kings[i].x, kings[i].y]);
124 }
125 }
126 return res;
127 }
128
129 filterValid(moves) {
130 if (moves.length == 0) return [];
131 const color = moves[0].vanish[0].c;
132 const oppCol = V.GetOppCol(color);
133 // Check if both kings under attack.
134 // If yes, moves must remove at least one attack.
135 const kings = this.getKingsPos(color);
136 return moves.filter(m => {
137 this.play(m);
138 let attacks = 0;
139 for (let k of kings) {
140 const curKingPos =
141 this.board[k.x][k.y] == V.EMPTY
142 ? [m.appear[0].x, m.appear[0].y] //king moved
143 : [k.x, k.y]
144 if (this.isAttacked(curKingPos, oppCol)) attacks++;
145 else break; //no need to check further
146 }
147 this.undo(m);
148 return (
149 (kings.length == 2 && attacks <= 1) ||
150 (kings.length == 1 && attacks == 0)
151 );
152 });
153 }
154
155 getCurrentScore() {
156 if (super.atLeastOneMove()) return "*";
157 // Count kings on board
158 const color = this.turn;
159 const oppCol = V.GetOppCol(color);
160 const kings = this.getKingsPos(color);
161 if (
162 this.isAttacked([kings[0].x, kings[0].y], oppCol) ||
163 (kings.length == 2 && this.isAttacked([kings[1].x, kings[1].y], oppCol))
164 ) {
165 return (color == 'w' ? "0-1" : "1-0");
166 }
167 return "1/2"; //stalemate
168 }
169
170 static get VALUES() {
171 return {
172 p: 1,
173 r: 5,
174 c: 5, //the commoner is valuable
175 b: 3,
176 q: 9,
177 k: 1000
178 };
179 }
180
181 };