Commit | Line | Data |
---|---|---|
3de62e0a BA |
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 | ||
4313762d | 46 | static GenRandInitFen(options) { |
3de62e0a | 47 | return ( |
4313762d | 48 | ChessRules.GenRandInitFen(options) |
3de62e0a BA |
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( | |
4313762d | 65 | sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1); |
3de62e0a BA |
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( | |
4313762d | 85 | sq, color, V.KING, V.steps[V.KNIGHT], 1); |
3de62e0a BA |
86 | } |
87 | ||
88 | isAttackedByCommoner(sq, color) { | |
89 | return this.isAttackedBySlideNJump( | |
4313762d | 90 | sq, color, V.COMMONER, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), 1); |
3de62e0a BA |
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 && | |
7cf07315 | 121 | this.isAttacked([kings[i].x, kings[i].y], oppCol) |
3de62e0a BA |
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] | |
7cf07315 | 144 | if (this.isAttacked(curKingPos, oppCol)) attacks++; |
3de62e0a BA |
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 ( | |
7cf07315 BA |
162 | this.isAttacked([kings[0].x, kings[0].y], oppCol) || |
163 | (kings.length == 2 && this.isAttacked([kings[1].x, kings[1].y], oppCol)) | |
3de62e0a BA |
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 | }; |