1 class UltimaRules
extends ChessRules
5 if (b
[1] == "m") //'m' for Immobilizer (I is too similar to 1)
7 return b
; //usual piece
12 this.kingPos
= {'w':[-1,-1], 'b':[-1,-1]};
13 const fenParts
= fen
.split(" ");
14 const position
= fenParts
[0].split("/");
15 for (let i
=0; i
<position
.length
; i
++)
18 for (let j
=0; j
<position
[i
].length
; j
++)
20 switch (position
[i
].charAt(j
))
23 this.kingPos
['b'] = [i
,k
];
26 this.kingPos
['w'] = [i
,k
];
29 let num
= parseInt(position
[i
].charAt(j
));
36 this.epSquares
= []; //no en-passant here
41 // TODO: for compatibility?
42 this.castleFlags
= {"w":[false,false], "b":[false,false]};
45 static get IMMOBILIZER() { return 'm'; }
46 // Although other pieces keep their names here for coding simplicity,
48 // - a "rook" is a coordinator, capturing by coordinating with the king
49 // - a "knight" is a long-leaper, capturing as in draughts
50 // - a "bishop" is a chameleon, capturing as its prey
51 // - a "queen" is a withdrawer, capturing by moving away from pieces
53 getPotentialMovesFrom([x
,y
])
55 // TODO: pre-check: is thing on this square immobilized? If yes, return []
56 switch (this.getPiece(x
,y
))
58 case VariantRules
.IMMOBILIZER:
59 return this.getPotentialImmobilizerMoves([x
,y
]);
61 return super.getPotentialMovesFrom([x
,y
]);
63 // TODO: add potential suicides as a move "taking the immobilizer"
64 // TODO: add long-leaper captures
65 // TODO: mark matching coordinator/withdrawer/chameleon moves as captures
66 // (will be a bit tedious for chameleons)
67 // --> filter directly in functions below
70 getSlideNJumpMoves([x
,y
], steps
, oneStep
)
72 const color
= this.getColor(x
,y
);
73 const piece
= this.getPiece(x
,y
);
75 const [sizeX
,sizeY
] = VariantRules
.size
;
77 for (let step
of steps
)
81 while (i
>=0 && i
<sizeX
&& j
>=0 && j
<sizeY
82 && this.board
[i
][j
] == VariantRules
.EMPTY
)
84 moves
.push(this.getBasicMove([x
,y
], [i
,j
]));
85 if (oneStep
!== undefined)
90 // Only king can take on occupied square:
91 if (piece
==VariantRules
.KING
&& i
>=0 && i
<sizeX
&& j
>=0
92 && j
<sizeY
&& this.canTake([x
,y
], [i
,j
]))
94 moves
.push(this.getBasicMove([x
,y
], [i
,j
]));
100 getPotentialPawnMoves([x
,y
])
102 return super.getPotentialRookMoves([x
,y
]);
105 getPotentialRookMoves(sq
)
107 return super.getPotentialQueenMoves(sq
);
110 getPotentialKnightMoves(sq
)
112 return super.getPotentialQueenMoves(sq
);
115 getPotentialBishopMoves(sq
)
117 return super.getPotentialQueenMoves(sq
);
120 getPotentialQueenMoves(sq
)
122 return super.getPotentialQueenMoves(sq
);
125 getPotentialImmobilizerMoves(sq
)
127 return super.getPotentialQueenMoves(sq
);
130 getPotentialKingMoves(sq
)
132 const V
= VariantRules
;
133 return this.getSlideNJumpMoves(sq
,
134 V
.steps
[V
.ROOK
].concat(V
.steps
[V
.BISHOP
]), "oneStep");
137 // isAttacked() is OK because the immobilizer doesn't take
139 isAttackedByPawn([x
,y
], colors
)
141 // Square (x,y) must be surrounded by two enemy pieces,
142 // and one of them at least should be a pawn
146 isAttackedByRook(sq
, colors
)
148 // Enemy king must be on same file and a rook on same row (or reverse)
151 isAttackedByKnight(sq
, colors
)
153 // Square (x,y) must be on same line as a knight,
154 // and there must be empty square(s) behind.
157 isAttackedByBishop(sq
, colors
)
159 // switch on piece nature on square sq: a chameleon attack as this piece
160 // ==> call the appropriate isAttackedBy... (exception of immobilizers)
161 // Other exception: a chameleon cannot attack a chameleon (seemingly...)
164 isAttackedByQueen(sq
, colors
)
166 // Square (x,y) must be adjacent to a queen, and the queen must have
167 // some free space in the opposite direction from (x,y)
170 updateVariables(move)
172 // Just update king position
173 const piece
= this.getPiece(move.start
.x
,move.start
.y
);
174 const c
= this.getColor(move.start
.x
,move.start
.y
);
175 if (piece
== VariantRules
.KING
&& move.appear
.length
> 0)
177 this.kingPos
[c
][0] = move.appear
[0].x
;
178 this.kingPos
[c
][1] = move.appear
[0].y
;
182 static get VALUES() { //TODO: totally experimental!
194 static get SEARCH_DEPTH() { return 2; } //TODO?
196 static GenRandInitFen()
198 let pieces
= { "w": new Array(8), "b": new Array(8) };
199 // Shuffle pieces on first and last rank
200 for (let c
of ["w","b"])
202 let positions
= _
.range(8);
203 // Get random squares for every piece, totally freely
205 let randIndex
= _
.random(7);
206 const bishop1Pos
= positions
[randIndex
];
207 positions
.splice(randIndex
, 1);
209 randIndex
= _
.random(6);
210 const bishop2Pos
= positions
[randIndex
];
211 positions
.splice(randIndex
, 1);
213 randIndex
= _
.random(5);
214 const knight1Pos
= positions
[randIndex
];
215 positions
.splice(randIndex
, 1);
217 randIndex
= _
.random(4);
218 const knight2Pos
= positions
[randIndex
];
219 positions
.splice(randIndex
, 1);
221 randIndex
= _
.random(3);
222 const queenPos
= positions
[randIndex
];
223 positions
.splice(randIndex
, 1);
225 randIndex
= _
.random(2);
226 const kingPos
= positions
[randIndex
];
227 positions
.splice(randIndex
, 1);
229 randIndex
= _
.random(1);
230 const rookPos
= positions
[randIndex
];
231 positions
.splice(randIndex
, 1);
232 const immobilizerPos
= positions
[0];
234 pieces
[c
][bishop1Pos
] = 'b';
235 pieces
[c
][bishop2Pos
] = 'b';
236 pieces
[c
][knight1Pos
] = 'n';
237 pieces
[c
][knight2Pos
] = 'n';
238 pieces
[c
][queenPos
] = 'q';
239 pieces
[c
][kingPos
] = 'k';
240 pieces
[c
][rookPos
] = 'r';
241 pieces
[c
][immobilizerPos
] = 'm';
243 return pieces
["b"].join("") +
244 "/pppppppp/8/8/8/8/PPPPPPPP/" +
245 pieces
["w"].join("").toUpperCase() +
246 " 0000"; //TODO: flags?!
251 return "0000"; //TODO: or "-" ?