Fix capturing promotions in Absorption variant
[vchess.git] / client / src / variants / Absorption.js
1 import { ChessRules } from "@/base_rules";
2
3 export class AbsorptionRules extends ChessRules {
4 getPpath(b) {
5 if ([V.BN, V.RN, V.QN].includes(b[1])) return "Absorption/" + b;
6 return b;
7 }
8
9 // Three new pieces: rook+knight, bishop+knight and queen+knight
10 static get RN() {
11 // Empress
12 return 'e';
13 }
14 static get BN() {
15 // Princess
16 return 's';
17 }
18 static get QN() {
19 // Amazon
20 return 'a';
21 }
22
23 static get PIECES() {
24 return ChessRules.PIECES.concat([V.RN, V.BN, V.QN]);
25 }
26
27 static get MergeComposed() {
28 return {
29 "be": "a",
30 "bs": "s",
31 "er": "e",
32 "rs": "a",
33 "eq": "a",
34 "qs": "a",
35 "ee": "e",
36 "es": "a",
37 "ss": "s"
38 };
39 }
40
41 static Fusion(p1, p2) {
42 if (p1 == V.KING) return p1;
43 if (p1 == V.PAWN) return p2;
44 if (p2 == V.PAWN) return p1;
45 if ([p1, p2].includes(V.KNIGHT)) {
46 if ([p1, p2].includes(V.QUEEN)) return V.QN;
47 if ([p1, p2].includes(V.ROOK)) return V.RN;
48 if ([p1, p2].includes(V.BISHOP)) return V.BN;
49 // p1 or p2 already have knight + other piece
50 return (p1 == V.KNIGHT ? p2 : p1);
51 }
52 for (let p of [p1, p2]) {
53 if (p == V.QN) return V.QN;
54 if ([V.BN, V.RN].includes(p))
55 return V.MergeComposed[[p1, p2].sort().join("")];
56 }
57 // bishop + rook, or queen + [bishop or rook]
58 return V.QUEEN;
59 }
60
61 getPotentialMovesFrom(sq) {
62 let moves = [];
63 const piece = this.getPiece(sq[0], sq[1]);
64 switch (piece) {
65 case V.RN:
66 moves =
67 super.getPotentialRookMoves(sq).concat(
68 super.getPotentialKnightMoves(sq));
69 break;
70 case V.BN:
71 moves =
72 super.getPotentialBishopMoves(sq).concat(
73 super.getPotentialKnightMoves(sq));
74 break;
75 case V.QN:
76 moves =
77 super.getPotentialQueenMoves(sq).concat(
78 super.getPotentialKnightMoves(sq));
79 break;
80 default:
81 moves = super.getPotentialMovesFrom(sq);
82 }
83 // Filter out capturing promotions (except one),
84 // because they are all the same.
85 moves = moves.filter(m => {
86 return (
87 m.vanish.length == 1 ||
88 m.vanish[0].p != V.PAWN ||
89 [V.PAWN, V.QUEEN].includes(m.appear[0].p)
90 );
91 });
92 moves.forEach(m => {
93 if (m.vanish.length == 2) {
94 // Augment pieces abilities in case of captures
95 const piece2 = m.vanish[1].p;
96 if (piece != piece2) m.appear[0].p = V.Fusion(piece, piece2);
97 }
98 });
99 return moves;
100 }
101
102 isAttacked(sq, color) {
103 return (
104 super.isAttacked(sq, color) ||
105 this.isAttackedByBN(sq, color) ||
106 this.isAttackedByRN(sq, color) ||
107 this.isAttackedByQN(sq, color)
108 );
109 }
110
111 isAttackedByBN(sq, color) {
112 return (
113 this.isAttackedBySlideNJump(sq, color, V.BN, V.steps[V.BISHOP]) ||
114 this.isAttackedBySlideNJump(
115 sq, color, V.BN, V.steps[V.KNIGHT], "oneStep")
116 );
117 }
118
119 isAttackedByRN(sq, color) {
120 return (
121 this.isAttackedBySlideNJump(sq, color, V.RN, V.steps[V.ROOK]) ||
122 this.isAttackedBySlideNJump(
123 sq, color, V.RN, V.steps[V.KNIGHT], "oneStep")
124 );
125 }
126
127 isAttackedByQN(sq, color) {
128 return (
129 this.isAttackedBySlideNJump(
130 sq, color, V.QN, V.steps[V.BISHOP].concat(V.steps[V.ROOK])) ||
131 this.isAttackedBySlideNJump(
132 sq, color, V.QN, V.steps[V.KNIGHT], "oneStep")
133 );
134 }
135
136 static get VALUES() {
137 return Object.assign(
138 { a: 12, e: 7, s: 5 },
139 ChessRules.VALUES
140 );
141 }
142
143 getNotation(move) {
144 let notation = super.getNotation(move);
145 if (move.vanish[0].p != V.PAWN && move.appear[0].p != move.vanish[0].p)
146 // Fusion (not from a pawn: handled in ChessRules)
147 notation += "=" + move.appear[0].p.toUpperCase();
148 return notation;
149 }
150 };