Add Minishogi
[vchess.git] / client / src / variants / Minishogi.js
1 import { ChessRules } from "@/base_rules";
2 import { ShogiRules } from "@/variants/Shogi";
3
4 export class MinishogiRules extends ShogiRules {
5 static IsGoodFen(fen) {
6 if (!ChessRules.IsGoodFen(fen)) return false;
7 const fenParsed = V.ParseFen(fen);
8 // 3) Check reserves
9 if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{10,10}$/))
10 return false;
11 return true;
12 }
13
14 // No knight or lance
15 static get PIECES() {
16 return [
17 ChessRules.PAWN,
18 ChessRules.ROOK,
19 ChessRules.BISHOP,
20 ChessRules.KING,
21 V.GOLD_G,
22 V.SILVER_G,
23 V.P_PAWN,
24 V.P_SILVER,
25 V.P_ROOK,
26 V.P_BISHOP
27 ];
28 }
29
30 static GenRandInitFen() {
31 return "rbsgk/4p/5/P4/KGSBR w 0 0000000000";
32 }
33
34 getReserveFen() {
35 let counts = new Array(10);
36 for (let i = 0; i < V.RESERVE_PIECES.length; i++) {
37 counts[i] = this.reserve["w"][V.RESERVE_PIECES[i]];
38 counts[5 + i] = this.reserve["b"][V.RESERVE_PIECES[i]];
39 }
40 return counts.join("");
41 }
42
43 setOtherVariables(fen) {
44 super.setOtherVariables(fen);
45 const fenParsed = V.ParseFen(fen);
46 // Also init reserves (used by the interface to show landable pieces)
47 this.reserve = {
48 w: {
49 [V.PAWN]: parseInt(fenParsed.reserve[0]),
50 [V.ROOK]: parseInt(fenParsed.reserve[1]),
51 [V.BISHOP]: parseInt(fenParsed.reserve[2]),
52 [V.GOLD_G]: parseInt(fenParsed.reserve[3]),
53 [V.SILVER_G]: parseInt(fenParsed.reserve[4])
54 },
55 b: {
56 [V.PAWN]: parseInt(fenParsed.reserve[5]),
57 [V.ROOK]: parseInt(fenParsed.reserve[6]),
58 [V.BISHOP]: parseInt(fenParsed.reserve[7]),
59 [V.GOLD_G]: parseInt(fenParsed.reserve[8]),
60 [V.SILVER_G]: parseInt(fenParsed.reserve[9])
61 }
62 };
63 }
64
65 static get size() {
66 return { x: 5, y: 5 };
67 }
68
69 static get RESERVE_PIECES() {
70 return (
71 [V.PAWN, V.ROOK, V.BISHOP, V.GOLD_G, V.SILVER_G]
72 );
73 }
74
75 getReserveMoves([x, y]) {
76 const color = this.turn;
77 const p = V.RESERVE_PIECES[y];
78 if (p == V.PAWN) {
79 var oppCol = V.GetOppCol(color);
80 var allowedFiles =
81 [...Array(5).keys()].filter(j =>
82 [...Array(5).keys()].every(i => {
83 return (
84 this.board[i][j] == V.EMPTY ||
85 this.getColor(i, j) != color ||
86 this.getPiece(i, j) != V.PAWN
87 );
88 })
89 )
90 }
91 if (this.reserve[color][p] == 0) return [];
92 let moves = [];
93 const forward = color == 'w' ? -1 : 1;
94 const lastRank = color == 'w' ? 0 : 4;
95 for (let i = 0; i < V.size.x; i++) {
96 if (p == V.PAWN && i == lastRank) continue;
97 for (let j = 0; j < V.size.y; j++) {
98 if (
99 this.board[i][j] == V.EMPTY &&
100 (p != V.PAWN || allowedFiles.includes(j))
101 ) {
102 let mv = new Move({
103 appear: [
104 new PiPo({
105 x: i,
106 y: j,
107 c: color,
108 p: p
109 })
110 ],
111 vanish: [],
112 start: { x: x, y: y }, //a bit artificial...
113 end: { x: i, y: j }
114 });
115 if (p == V.PAWN) {
116 // Do not drop on checkmate:
117 this.play(mv);
118 const res = (this.underCheck(oppCol) && !this.atLeastOneMove());
119 this.undo(mv);
120 if (res) continue;
121 }
122 moves.push(mv);
123 }
124 }
125 }
126 return moves;
127 }
128
129 getSlideNJumpMoves([x, y], steps, options) {
130 options = options || {};
131 const color = this.turn;
132 const oneStep = options.oneStep;
133 const forcePromoteOnLastRank = options.force;
134 const promoteInto = options.promote;
135 const lastRank = (color == 'w' ? 0 : 4);
136 let moves = [];
137 outerLoop: for (let step of steps) {
138 let i = x + step[0];
139 let j = y + step[1];
140 while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
141 if (i != lastRank || !forcePromoteOnLastRank)
142 moves.push(this.getBasicMove([x, y], [i, j]));
143 if (i == lastRank && !!promoteInto) {
144 moves.push(
145 this.getBasicMove(
146 [x, y], [i, j], { c: color, p: promoteInto })
147 );
148 }
149 if (oneStep) continue outerLoop;
150 i += step[0];
151 j += step[1];
152 }
153 if (V.OnBoard(i, j) && this.canTake([x, y], [i, j])) {
154 if (i != lastRank || !forcePromoteOnLastRank)
155 moves.push(this.getBasicMove([x, y], [i, j]));
156 if (i == lastRank && !!promoteInto) {
157 moves.push(
158 this.getBasicMove(
159 [x, y], [i, j], { c: color, p: promoteInto })
160 );
161 }
162 }
163 }
164 return moves;
165 }
166
167 static get SEARCH_DEPTH() {
168 return 3;
169 }
170 };