430cfb0aec2eadd73a589d29dd157c3bb0e4b1d4
1 import { ChessRules
, PiPo
, Move
} from "@/base_rules";
2 import { randInt
} from "@/utils/alea";
4 export class PacosakoRules
extends ChessRules
{
6 static get IMAGE_EXTENSION() {
10 // Unions (left = white if upperCase, black otherwise)
36 static IsGoodPosition(position
) {
37 if (position
.length
== 0) return false;
38 const rows
= position
.split("/");
39 if (rows
.length
!= V
.size
.x
) return false;
40 let kingSymb
= ['k', 'g', 'm', 'u', 'x'];
41 let kings
= { 'k': 0, 'K': 0 };
42 for (let row
of rows
) {
44 for (let i
= 0; i
< row
.length
; i
++) {
45 const lowR
= row
[i
].toLowerCase
46 if (!!(row
[i
].toLowerCase().match(/[a-z]/))) {
48 if (kingSymb
.includes(row
[i
])) kings
['k']++;
49 else if (kingSymb
.some(s
=> row
[i
] == s
.toUpperCase())) kings
['K']++;
52 const num
= parseInt(row
[i
], 10);
53 if (isNaN(num
) || num
<= 0) return false;
57 if (sumElts
!= V
.size
.y
) return false;
59 // Both kings should be on board. Exactly one per color.
60 if (Object
.values(kings
).some(v
=> v
!= 1)) return false;
65 return "Pacosako/" + b
;
69 if (ChessRules
.PIECES
.includes(m
.appear
[0].p
)) return super.getPPpath(m
);
70 // For an union, show only relevant piece:
71 // The color must be deduced from the move: reaching final rank of who?
72 const color
= (m
.appear
[0].x
== 0 ? 'b' : 'w');
73 const up
= this.getUnionPieces(color
, m
.appear
[0].p
);
74 return color
+ up
[color
];
77 canTake([x1
, y1
], [x2
, y2
]) {
78 const c1
= this.getColor(x1
, y1
);
79 const c2
= this.getColor(x2
, y2
);
80 return (c1
!= 'u' && c2
!= c1
);
83 canIplay(side
, [x
, y
]) {
84 return this.turn
== side
&& this.getColor(x
, y
) != V
.GetOppCol(side
);
88 this.kingPos
= { w: [-1, -1], b: [-1, -1] };
89 const fenRows
= V
.ParseFen(fen
).position
.split("/");
90 const startRow
= { 'w': V
.size
.x
- 1, 'b': 0 };
91 const kingSymb
= ['k', 'g', 'm', 'u', 'x'];
92 for (let i
= 0; i
< fenRows
.length
; i
++) {
94 for (let j
= 0; j
< fenRows
[i
].length
; j
++) {
95 const c
= fenRows
[i
].charAt(j
);
96 if (kingSymb
.includes(c
))
97 this.kingPos
["b"] = [i
, k
];
98 else if (kingSymb
.some(s
=> c
== s
.toUpperCase()))
99 this.kingPos
["w"] = [i
, k
];
101 const num
= parseInt(fenRows
[i
].charAt(j
), 10);
102 if (!isNaN(num
)) k
+= num
- 1;
109 setOtherVariables(fen
) {
110 super.setOtherVariables(fen
);
111 // Stack of "last move" only for intermediate chaining
112 this.lastMoveEnd
= [null];
116 const p
= this.board
[i
][j
].charAt(1);
117 if (ChessRules
.PIECES
.includes(p
)) return super.getColor(i
, j
);
121 getPiece(i
, j
, color
) {
122 const p
= this.board
[i
][j
].charAt(1);
126 if (ChessRules
.PIECES
.includes(p
)) return p
;
127 const c
= this.board
[i
][j
].charAt(0);
128 // NOTE: this.turn == HACK, but should work...
129 color
= color
|| this.turn
;
130 return V
.UNIONS
[p
][c
== color
? 0 : 1];
133 getUnionPieces(color
, code
) {
134 const pieces
= V
.UNIONS
[code
];
136 w: pieces
[color
== 'w' ? 0 : 1],
137 b: pieces
[color
== 'b' ? 0 : 1]
141 getUnionCode(p1
, p2
) {
143 Object
.values(V
.UNIONS
).findIndex(v
=> v
[0] == p1
&& v
[1] == p2
)
145 const c
= (uIdx
>= 0 ? 'w' : 'b');
148 Object
.values(V
.UNIONS
).findIndex(v
=> v
[0] == p2
&& v
[1] == p1
)
151 return { c: c
, p: Object
.keys(V
.UNIONS
)[uIdx
] };
154 getBasicMove([sx
, sy
], [ex
, ey
], tr
) {
155 const initColor
= this.board
[sx
][sy
].charAt(0);
156 const initPiece
= this.board
[sx
][sy
].charAt(1);
158 // - union to free square (other cases are illegal: return null)
159 // - normal piece to free square,
160 // to enemy normal piece, or
161 // to union (releasing our piece)
171 end: { x: ex
, y: ey
}
173 // Treat free square cases first:
174 if (this.board
[ex
][ey
] == V
.EMPTY
) {
180 p: !!tr
? tr
.p : initPiece
185 // Now the two cases with union / release:
186 const destColor
= this.board
[ex
][ey
].charAt(0);
187 const destPiece
= this.board
[ex
][ey
].charAt(1);
196 if (ChessRules
.PIECES
.includes(destPiece
)) {
197 // Normal piece: just create union
198 const cp
= this.getUnionCode(!!tr
? tr
.p : initPiece
, destPiece
);
209 // Releasing a piece in an union: keep track of released piece
210 const up
= this.getUnionPieces(destColor
, destPiece
);
212 const oppCol
= V
.GetOppCol(c
);
213 const cp
= this.getUnionCode(!!tr
? tr
.p : initPiece
, up
[oppCol
])
226 getPotentialMoves([x
, y
]) {
227 const L
= this.lastMoveEnd
.length
;
228 const lm
= this.lastMoveEnd
[L
-1];
231 if (x
!= lm
.x
|| y
!= lm
.y
) return [];
235 var unionOnBoard
= this.board
[x
][y
];
236 this.board
[x
][y
] = this.turn
+ piece
;
239 switch (piece
|| this.getPiece(x
, y
)) {
241 baseMoves
= this.getPotentialPawnMoves([x
, y
]);
244 baseMoves
= this.getPotentialRookMoves([x
, y
]);
247 baseMoves
= this.getPotentialKnightMoves([x
, y
]);
250 baseMoves
= this.getPotentialBishopMoves([x
, y
]);
253 baseMoves
= this.getPotentialQueenMoves([x
, y
]);
256 baseMoves
= this.getPotentialKingMoves([x
, y
]);
259 // When a pawn in an union reaches final rank with a non-standard
260 // promotion move: apply promotion anyway
262 baseMoves
.forEach(m
=> {
263 // (move to first rank, which is last rank for opponent [pawn]), should show promotion choices.
264 //if (m. //bring enemy pawn to his first rank ==> union types involved... color...
265 moves
.push(m
); //TODO
267 if (!!piece
) this.board
[x
][y
] = unionOnBoard
;
272 this.epSquares
.push(this.getEpSquare(move));
273 // Check if the move is the last of the turn: all cases except releases
275 move.vanish
.length
== 1 ||
276 ChessRules
.PIECES
.includes(move.vanish
[1].p
)
279 // No more union releases available
280 this.turn
= V
.GetOppCol(this.turn
);
282 this.lastMoveEnd
.push(null);
285 const color
= this.board
[move.end
.x
][move.end
.y
].charAt(0);
286 const oldUnion
= this.board
[move.end
.x
][move.end
.y
].charAt(1);
287 const released
= this.getUnionPieces(color
, oldUnion
)[this.turn
];
288 this.lastMoveEnd
.push(Object
.assign({}, move.end
, { p: released
}));
290 V
.PlayOnBoard(this.board
, move);
294 this.epSquares
.pop();
295 V
.UndoOnBoard(this.board
, move);
296 this.lastMoveEnd
.pop();
298 this.turn
= V
.GetOppCol(this.turn
);
304 // Check kings: if one is dancing, the side lost
305 const [kpW
, kpB
] = [this.kingPos
['w'], this.kingPos
['b']];
306 if (this.board
[kpB
[0]][kpB
[1]].charAt(1) != 'k') return "1-0";
307 if (this.board
[kpW
[0]][kpW
[1]].charAt(1) != 'k') return "0-1";
312 let moves
= this.getAllValidMoves();
313 if (moves
.length
== 0) return null;
314 // Just play random moves (for now at least. TODO?)
316 while (moves
.length
> 0) {
317 const mv
= moves
[randInt(moves
.length
)];
321 // A piece was just released from an union
322 moves
= this.getPotentialMovesFrom([mv
.end
.x
, mv
.end
.y
]);
325 for (let i
= mvArray
.length
- 1; i
>= 0; i
--) this.undo(mvArray
[i
]);
326 return (mvArray
.length
> 1 ? mvArray : mvArray
[0]);
329 // NOTE: evalPosition() is wrong, but unused since bot plays at random
332 // TODO: in case of enemy pawn promoted, add "=..." in the end
333 return super.getNotation(move);