--- /dev/null
+Finish rules translation in Spanish
+
+Design: final touch (gain extra space on top, using space on the right)
+Crazyhouse: my reserve vertically on the right, opponent just below board
+
+Bug MarseilleRules turn issue in computer game (last move is wrong)
+
+[Site "vchess.club"]
+[Variant "Marseille"]
+[Date "2018-12-26"]
+[White "Myself"]
+[Black "Computer"]
+[FenStart "nrqkbrnb/pppppppp/8/8/8/8/PPPPPPPP/RKQBNRBN"]
+[Fen "1rq3nb/1pk1p1p1/1Np1p2p/p7/8/2P5/PP2P1PP/RK1B2B1 w1 1000 -"]
+[Result "0-1"]
+
+1.f4 a5,h6 2.d3,c3 c6,Kc7 3.Qe3,Qe5 d6,dxe5 4.fxe5,e6 fxe6,Rxf1 5.Ng3,Nxf1 Nb6,Bg6 6.Nd2,Ne4 Bxe4,Bxd3 7.Nxd3,Nc5 Na4,Nxb6
+
+1.f2f4 a7a5,h7h6 2.d2d3,c2c3 c7c6,d8c7 3.c1e3,e3e5 d7d6,d6e5 4.f4e5,e5e6 f7e6,f8f1 5.h1g3,g3f1 a8b6,e8g6 6.f1d2,d2e4 g6e4,e4d3 7.e1d3,d3c5 c5a4,a4b6
const lastRank = (color == "w" ? 0 : sizeX-1);
const pawnColor = this.getColor(x,y); //can be different for checkered
- if (x+shiftX >= 0 && x+shiftX < sizeX) //TODO: always true
+ // NOTE: next condition is generally true (no pawn on last rank)
+ if (x+shiftX >= 0 && x+shiftX < sizeX)
{
const finalPieces = x + shiftX == lastRank
? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
{
this.kingPos[c][0] = move.appear[0].x;
this.kingPos[c][1] = move.appear[0].y;
- this.castleFlags[c] = [false,false];
+ if (V.HasFlags)
+ this.castleFlags[c] = [false,false];
return;
}
- const oppCol = this.getOppCol(c);
- const oppFirstRank = (V.size.x-1) - firstRank;
- if (move.start.x == firstRank //our rook moves?
- && this.INIT_COL_ROOK[c].includes(move.start.y))
- {
- const flagIdx = (move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1);
- this.castleFlags[c][flagIdx] = false;
- }
- else if (move.end.x == oppFirstRank //we took opponent rook?
- && this.INIT_COL_ROOK[oppCol].includes(move.end.y))
+ if (V.HasFlags)
{
- const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1);
- this.castleFlags[oppCol][flagIdx] = false;
+ // Update castling flags if rooks are moved
+ const oppCol = this.getOppCol(c);
+ const oppFirstRank = (V.size.x-1) - firstRank;
+ if (move.start.x == firstRank //our rook moves?
+ && this.INIT_COL_ROOK[c].includes(move.start.y))
+ {
+ const flagIdx = (move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1);
+ this.castleFlags[c][flagIdx] = false;
+ }
+ else if (move.end.x == oppFirstRank //we took opponent rook?
+ && this.INIT_COL_ROOK[oppCol].includes(move.end.y))
+ {
+ const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1);
+ this.castleFlags[oppCol][flagIdx] = false;
+ }
}
}
}
else
return currentBest;
- //console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; }));
+// console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; }));
candidates = [0];
for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
},
clickComputerGame: function(e) {
this.getRidOfTooltip(e.currentTarget);
+ if (this.mode == "computer" && this.score == "*"
+ && this.vr.turn != this.mycolor)
+ {
+ // Wait for computer reply first (avoid potential "ghost move" bug)
+ return;
+ }
this.newGame("computer");
},
clickFriendGame: function(e) {
return;
}
}
- else if (score == "*")
- return this.continueGame("computer");
}
}
else if (mode == "friend")
else if (mode == "computer")
{
this.compWorker.postMessage(["init",fen]);
- if (this.mycolor != this.vr.turn)
+ if (score == "*" && this.mycolor != this.vr.turn)
this.playComputerMove();
}
//else: nothing special to do in friend mode
shadowArray[i][colnum] = true;
continue;
}
+ if (squares[i].indexOf("-") >= 0)
+ {
+ // Shadow a range of squares, horizontally or vertically
+ const firstLastSq = squares[i].split("-");
+ const range =
+ [
+ V.SquareToCoords(firstLastSq[0]),
+ V.SquareToCoords(firstLastSq[1])
+ ];
+ const step =
+ [
+ range[1].x == range[0].x
+ ? 0
+ : (range[1].x - range[0].x) / Math.abs(range[1].x - range[0].x),
+ range[1].y == range[0].y
+ ? 0
+ : (range[1].y - range[0].y) / Math.abs(range[1].y - range[0].y)
+ ];
+ // Convention: range always from smaller to larger number
+ for (let x=range[0].x, y=range[0].y; x <= range[1].x && y <= range[1].y;
+ x += step[0], y += step[1])
+ {
+ shadowArray[x][y] = true;
+ }
+ continue;
+ }
// Shadow just one square:
const coords = V.SquareToCoords(squares[i]);
shadowArray[coords.x][coords.y] = true;
-class UltimaRules extends ChessRules
+class BaroqueRules extends ChessRules
{
static get HasFlags() { return false; }
static getPpath(b)
{
if (b[1] == "m") //'m' for Immobilizer (I is too similar to 1)
- return "Ultima/" + b;
+ return "Baroque/" + b;
return b; //usual piece
}
});
}
- // "Pincher"
+ // "Pincer"
getPotentialPawnMoves([x,y])
{
let moves = super.getPotentialRookMoves([x,y]);
return false;
}
- updateVariables(move)
- {
- // Just update king(s) position(s)
- const piece = move.vanish[0].p;
- const c = move.vanish[0].c;
- if (piece == V.KING && move.appear.length > 0)
- {
- this.kingPos[c][0] = move.appear[0].x;
- this.kingPos[c][1] = move.appear[0].y;
- }
- }
-
static get VALUES()
{
// TODO: totally experimental!
}
}
-const VariantRules = UltimaRules;
+const VariantRules = BaroqueRules;
const firstRank = (color == 'w' ? sizeX-1 : 0);
const startRank = (color == "w" ? sizeX-2 : 1);
const lastRank = (color == "w" ? 0 : sizeX-1);
+ const finalPieces = x + shiftX == lastRank
+ ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
+ : [V.PAWN];
- if (x+shiftX >= 0 && x+shiftX < sizeX) //TODO: always true
+ // One square diagonally
+ for (let shiftY of [-1,1])
{
- const finalPieces = x + shiftX == lastRank
- ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
- : [V.PAWN]
- // One square diagonally
- for (let shiftY of [-1,1])
+ if (this.board[x+shiftX][y+shiftY] == V.EMPTY)
{
- if (this.board[x+shiftX][y+shiftY] == V.EMPTY)
+ for (let piece of finalPieces)
{
- for (let piece of finalPieces)
- {
- moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
- {c:color,p:piece}));
- }
- if (x == startRank && y+2*shiftY>=0 && y+2*shiftY<sizeY
- && this.board[x+2*shiftX][y+2*shiftY] == V.EMPTY)
- {
- // Two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shiftX,y+2*shiftY]));
- }
+ moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
+ {c:color,p:piece}));
+ }
+ if (x == startRank && y+2*shiftY>=0 && y+2*shiftY<sizeY
+ && this.board[x+2*shiftX][y+2*shiftY] == V.EMPTY)
+ {
+ // Two squares jump
+ moves.push(this.getBasicMove([x,y], [x+2*shiftX,y+2*shiftY]));
}
}
- // Capture
- if (this.board[x+shiftX][y] != V.EMPTY
- && this.canTake([x,y], [x+shiftX,y]))
- {
- for (let piece of finalPieces)
- moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece}));
- }
+ }
+ // Capture
+ if (this.board[x+shiftX][y] != V.EMPTY
+ && this.canTake([x,y], [x+shiftX,y]))
+ {
+ for (let piece of finalPieces)
+ moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece}));
}
// En passant
updateEnlightened()
{
+ const pawnShift = {"w":-1, "b":1};
// Initialize with pieces positions (which are seen)
for (let i=0; i<V.size.x; i++)
{
this.enlightened["w"][i][j] = false;
this.enlightened["b"][i][j] = false;
if (this.board[i][j] != V.EMPTY)
- this.enlightened[this.getColor(i,j)][i][j] = true;
+ {
+ const color = this.getColor(i,j);
+ this.enlightened[color][i][j] = true;
+ // Add potential squares visible by "impossible pawn capture"
+ if (this.getPiece(i,j) == V.PAWN)
+ {
+ for (let shiftY of [-1,1])
+ {
+ if (V.OnBoard(i+pawnShift[color],j+shiftY)
+ && this.board[i+pawnShift[color]][j+shiftY] == V.EMPTY)
+ {
+ this.enlightened[color][i+pawnShift[color]][j+shiftY] = true;
+ }
+ }
+ }
+ }
}
}
const currentTurn = this.turn;
updateVariables(move)
{
- // Update kings positions
- const piece = move.vanish[0].p;
- const c = move.vanish[0].c;
- if (piece == V.KING && move.appear.length > 0)
- {
- this.kingPos[c][0] = move.appear[0].x;
- this.kingPos[c][1] = move.appear[0].y;
- }
+ super.updateVariables(move);
if (move.vanish.length >= 2 && move.vanish[1].p == V.KING)
{
// We took opponent king !
- const oppCol = this.getOppCol(c);
- this.kingPos[oppCol] = [-1,-1];
+ this.kingPos[this.turn] = [-1,-1];
}
// Update moves for both colors:
return false;
const fenParsed = V.ParseFen(fen);
// 5) Check captures
- if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{10,10}$/))
+ if (!fenParsed.captured || !fenParsed.captured.match(/^[0-9]{14,14}$/))
return false;
return true;
}
getCapturedFen()
{
- let counts = _.map(_.range(10), 0);
- for (let i=0; i<V.PIECES.length-1; i++) //-1: no king captured
+ let counts = _.map(_.range(14), 0);
+ let i = 0;
+ for (let j=0; j<V.PIECES.length; j++)
{
+ if (V.PIECES[j] == V.KING) //no king captured
+ continue;
counts[i] = this.captured["w"][V.PIECES[i]];
- counts[5+i] = this.captured["b"][V.PIECES[i]];
+ counts[7+i] = this.captured["b"][V.PIECES[i]];
+ i++;
}
return counts.join("");
}
[V.KNIGHT]: parseInt(fenParsed.captured[2]),
[V.BISHOP]: parseInt(fenParsed.captured[3]),
[V.QUEEN]: parseInt(fenParsed.captured[4]),
+ [V.MARSHALL]: parseInt(fenParsed.captured[5]),
+ [V.CARDINAL]: parseInt(fenParsed.captured[6]),
},
"b":
{
- [V.PAWN]: parseInt(fenParsed.captured[5]),
- [V.ROOK]: parseInt(fenParsed.captured[6]),
- [V.KNIGHT]: parseInt(fenParsed.captured[7]),
- [V.BISHOP]: parseInt(fenParsed.captured[8]),
- [V.QUEEN]: parseInt(fenParsed.captured[9]),
+ [V.PAWN]: parseInt(fenParsed.captured[7]),
+ [V.ROOK]: parseInt(fenParsed.captured[8]),
+ [V.KNIGHT]: parseInt(fenParsed.captured[9]),
+ [V.BISHOP]: parseInt(fenParsed.captured[10]),
+ [V.QUEEN]: parseInt(fenParsed.captured[11]),
+ [V.MARSHALL]: parseInt(fenParsed.captured[12]),
+ [V.CARDINAL]: parseInt(fenParsed.captured[13]),
}
};
}
const color = this.turn;
let moves = [];
const [sizeX,sizeY] = [V.size.x,V.size.y];
- const shift = (color == "w" ? -1 : 1);
+ const shiftX = (color == "w" ? -1 : 1);
const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]);
const lastRanks = (color == "w" ? [0,1,2] : [sizeX-1,sizeX-2,sizeX-3]);
+ const promotionPieces =
+ [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN,V.MARSHALL,V.CARDINAL];
- if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRanks[0])
+ // Always x+shiftX >= 0 && x+shiftX < sizeX, because no pawns on last rank
+ let finalPieces = undefined;
+ if (lastRanks.includes(x + shiftX))
{
- // Normal moves
- if (this.board[x+shift][y] == V.EMPTY)
+ finalPieces = promotionPieces.filter(p => this.captured[color][p] > 0);
+ if (x + shiftX != lastRanks[0])
+ finalPieces.push(V.PAWN);
+ }
+ else
+ finalPieces = [V.PAWN];
+ if (this.board[x+shiftX][y] == V.EMPTY)
+ {
+ // One square forward
+ for (let piece of finalPieces)
+ moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece}));
+ if (startRanks.includes(x))
{
- moves.push(this.getBasicMove([x,y], [x+shift,y]));
- if (startRanks.includes(x) && this.board[x+2*shift][y] == V.EMPTY)
+ if (this.board[x+2*shiftX][y] == V.EMPTY)
{
// Two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
- if (x == startRanks[0] && this.board[x+3*shift][y] == V.EMPTY)
+ moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
+ if (x==startRanks[0] && this.board[x+3*shiftX][y] == V.EMPTY)
{
- // 3-squares jump
- moves.push(this.getBasicMove([x,y], [x+3*shift,y]));
+ // Three squares jump
+ moves.push(this.getBasicMove([x,y], [x+3*shiftX,y]));
}
}
}
- // Captures
- if (y>0 && this.canTake([x,y], [x+shift,y-1])
- && this.board[x+shift][y-1] != V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
- }
- if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
- && this.board[x+shift][y+1] != V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
- }
}
-
- if (lastRanks.includes(x+shift))
+ // Captures
+ for (let shiftY of [-1,1])
{
- // Promotion
- let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN,V.MARSHALL,V.CARDINAL];
- promotionPieces.forEach(p => {
- if (this.captured[color][p]==0)
- return;
- // Normal move
- if (this.board[x+shift][y] == V.EMPTY)
- moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
- // Captures
- if (y>0 && this.canTake([x,y], [x+shift,y-1])
- && this.board[x+shift][y-1] != V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p}));
- }
- if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
- && this.board[x+shift][y+1] != V.EMPTY)
+ if (y + shiftY >= 0 && y + shiftY < sizeY
+ && this.board[x+shiftX][y+shiftY] != V.EMPTY
+ && this.canTake([x,y], [x+shiftX,y+shiftY]))
+ {
+ for (let piece of finalPieces)
{
- moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p}));
+ moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
+ {c:color,p:piece}));
}
- });
+ }
}
// En passant
const Lep = this.epSquares.length;
- const epSquare = Lep>0 ? this.epSquares[Lep-1] : undefined;
+ const epSquare = this.epSquares[Lep-1];
if (!!epSquare)
{
for (let epsq of epSquare)
{
// TODO: some redundant checks
- if (epsq.x == x+shift && Math.abs(epsq.y - y) == 1)
+ if (epsq.x == x+shiftX && Math.abs(epsq.y - y) == 1)
{
- var enpassantMove = this.getBasicMove([x,y], [x+shift,epsq.y]);
+ var enpassantMove = this.getBasicMove([x,y], [epsq.x,epsq.y]);
+ // WARNING: the captured pawn may be diagonally behind us,
+ // if it's a 3-squares jump and we take on 1st passing square
+ const px = (this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX);
enpassantMove.vanish.push({
- x: x,
+ x: px,
y: epsq.y,
p: 'p',
- c: this.getColor(x,epsq.y)
+ c: this.getColor(px,epsq.y)
});
moves.push(enpassantMove);
}
updateVariables(move)
{
super.updateVariables(move);
- if (move.vanish.length==2 && move.appear.length==1 && move.vanish[1].p != V.PAWN)
+ if (move.vanish.length == 2 && move.appear.length == 1)
{
// Capture: update this.captured
this.captured[move.vanish[1].c][move.vanish[1].p]++;
}
+ if (move.vanish[0].p != move.appear[0].p)
+ {
+ // Promotion: update this.captured
+ this.captured[move.vanish[0].c][move.appear[0].p]--;
+ }
}
unupdateVariables(move)
{
super.unupdateVariables(move);
- if (move.vanish.length==2 && move.appear.length==1 && move.vanish[1].p != V.PAWN)
+ if (move.vanish.length == 2 && move.appear.length == 1)
this.captured[move.vanish[1].c][move.vanish[1].p]--;
+ if (move.vanish[0].p != move.appear[0].p)
+ this.captured[move.vanish[0].c][move.appear[0].p]++;
}
static get VALUES()
return pieces["b"].join("") +
"/pppppppppp/10/10/10/10/10/10/PPPPPPPPPP/" +
pieces["w"].join("").toUpperCase() +
- " w 1111 - 0000000000";
+ " w 1111 - 00000000000000";
}
}
const startRank = (color == "w" ? sizeX-2 : 1);
const lastRank = (color == "w" ? 0 : sizeX-1);
const pawnColor = this.getColor(x,y); //can be different for checkered
+ const finalPieces = x + shiftX == lastRank
+ ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
+ : [V.PAWN];
- if (x+shiftX >= 0 && x+shiftX < sizeX) //TODO: always true
+ // One square forward
+ if (this.board[x+shiftX][y] == V.EMPTY)
{
- const finalPieces = x + shiftX == lastRank
- ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
- : [V.PAWN]
- // One square forward
- if (this.board[x+shiftX][y] == V.EMPTY)
+ for (let piece of finalPieces)
{
- for (let piece of finalPieces)
- {
- moves.push(this.getBasicMove([x,y], [x+shiftX,y],
- {c:pawnColor,p:piece}));
- }
- // Next condition because pawns on 1st rank can generally jump
- if ([startRank,firstRank].includes(x)
- && this.board[x+2*shiftX][y] == V.EMPTY)
- {
- // Two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
- }
+ moves.push(this.getBasicMove([x,y], [x+shiftX,y],
+ {c:pawnColor,p:piece}));
}
- // Captures
- for (let shiftY of [-1,1])
+ // Next condition because pawns on 1st rank can generally jump
+ if ([startRank,firstRank].includes(x)
+ && this.board[x+2*shiftX][y] == V.EMPTY)
{
- if (y + shiftY >= 0 && y + shiftY < sizeY
- && this.board[x+shiftX][y+shiftY] != V.EMPTY
- && this.canTake([x,y], [x+shiftX,y+shiftY]))
+ // Two squares jump
+ moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
+ }
+ }
+ // Captures
+ for (let shiftY of [-1,1])
+ {
+ if (y + shiftY >= 0 && y + shiftY < sizeY
+ && this.board[x+shiftX][y+shiftY] != V.EMPTY
+ && this.canTake([x,y], [x+shiftX,y+shiftY]))
+ {
+ for (let piece of finalPieces)
{
- for (let piece of finalPieces)
- {
- moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
- {c:pawnColor,p:piece}));
- }
+ moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
+ {c:pawnColor,p:piece}));
}
}
}
{
if (this.subTurn == 1 || (epSqs.length == 2 &&
// Was this en-passant capture already played at subturn 1 ?
+ // (Or maybe the opponent filled the en-passant square with a piece)
this.board[epSqs[0].x][epSqs[0].y] != V.EMPTY))
{
if (sq.x == x+shiftX && Math.abs(sq.y - y) == 1)
return pgn;
}
-
}
const VariantRules = MarseilleRules;
class UpsidedownRules extends ChessRules
{
- static HasFlags() { return false; }
+ static get HasFlags() { return false; }
- static HasEnpassant() { return false; }
+ static get HasEnpassant() { return false; }
getPotentialKingMoves(sq)
{
return pieces["w"].join("").toUpperCase() +
"/PPPPPPPP/8/8/8/8/pppppppp/" +
pieces["b"].join("") +
- " w 1111 -"; //add turn + flags + enpassant
+ " w"; //no castle, no en-passant
}
}
const color = this.turn;
let moves = [];
const [sizeX,sizeY] = [V.size.x,V.size.y];
- const shift = (color == "w" ? -1 : 1);
+ const shiftX = (color == "w" ? -1 : 1);
const startRanks = (color == "w" ? [sizeX-2,sizeX-3] : [1,2]);
const lastRank = (color == "w" ? 0 : sizeX-1);
+ const finalPieces = x + shiftX == lastRank
+ ? [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN]
+ : [V.PAWN];
- if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank)
+ if (this.board[x+shiftX][y] == V.EMPTY)
{
- // Normal moves
- if (this.board[x+shift][y] == V.EMPTY)
+ // One square forward
+ for (let piece of finalPieces)
+ moves.push(this.getBasicMove([x,y], [x+shiftX,y], {c:color,p:piece}));
+ if (startRanks.includes(x))
{
- moves.push(this.getBasicMove([x,y], [x+shift,y]));
- if (startRanks.includes(x) && this.board[x+2*shift][y] == V.EMPTY)
+ if (this.board[x+2*shiftX][y] == V.EMPTY)
{
// Two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
- if (x == startRanks[0] && this.board[x+3*shift][y] == V.EMPTY)
+ moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
+ if (x==startRanks[0] && this.board[x+3*shiftX][y] == V.EMPTY)
{
- // 3-squares jump
- moves.push(this.getBasicMove([x,y], [x+3*shift,y]));
+ // Three squares jump
+ moves.push(this.getBasicMove([x,y], [x+3*shiftX,y]));
}
}
}
- // Captures
- if (y>0 && this.canTake([x,y], [x+shift,y-1])
- && this.board[x+shift][y-1] != V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
- }
- if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
- && this.board[x+shift][y+1] != V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
- }
}
-
- if (x+shift == lastRank)
+ // Captures
+ for (let shiftY of [-1,1])
{
- // Promotion
- let promotionPieces = [V.QUEEN,V.WILDEBEEST];
- promotionPieces.forEach(p => {
- // Normal move
- if (this.board[x+shift][y] == V.EMPTY)
- moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
- // Captures
- if (y>0 && this.canTake([x,y], [x+shift,y-1])
- && this.board[x+shift][y-1] != V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p}));
- }
- if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1])
- && this.board[x+shift][y+1] != V.EMPTY)
+ if (y + shiftY >= 0 && y + shiftY < sizeY
+ && this.board[x+shiftX][y+shiftY] != V.EMPTY
+ && this.canTake([x,y], [x+shiftX,y+shiftY]))
+ {
+ for (let piece of finalPieces)
{
- moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p}));
+ moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
+ {c:color,p:piece}));
}
- });
+ }
}
// En passant
const Lep = this.epSquares.length;
- const epSquare = Lep>0 ? this.epSquares[Lep-1] : undefined;
+ const epSquare = this.epSquares[Lep-1];
if (!!epSquare)
{
for (let epsq of epSquare)
{
// TODO: some redundant checks
- if (epsq.x == x+shift && Math.abs(epsq.y - y) == 1)
+ if (epsq.x == x+shiftX && Math.abs(epsq.y - y) == 1)
{
- var enpassantMove = this.getBasicMove([x,y], [x+shift,epsq.y]);
+ var enpassantMove = this.getBasicMove([x,y], [epsq.x,epsq.y]);
+ // WARNING: the captured pawn may be diagonally behind us,
+ // if it's a 3-squares jump and we take on 1st passing square
+ const px = (this.board[x][epsq.y] != V.EMPTY ? x : x - shiftX);
enpassantMove.vanish.push({
- x: x,
+ x: px,
y: epsq.y,
p: 'p',
- c: this.getColor(x,epsq.y)
+ c: this.getColor(px,epsq.y)
});
moves.push(enpassantMove);
}
const firstRank = (color == 'w' ? sizeY-1 : 0);
const lastRank = (color == "w" ? 0 : sizeY-1);
- if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank)
+ if (x+shift != lastRank)
{
// Normal moves
if (this.board[x+shift][y] == V.EMPTY)
}
}
- if (x+shift == lastRank)
+ else //promotion
{
- // Promotion
let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
promotionPieces.forEach(p => {
// Normal move
background-color: #e6ee9c
&:hover
background-color: skyblue
- .choice-piece
- width: 90%
- max-width: 100%
+ &.choice-piece
+ width: 100%
height: auto
display: block
display: block
clear: both
padding-top: 5px
+ font-size: 0.8em
p.boxed
background-color: #FFCC66
p.
Only the pawn movements change, but since there are many on the board it's a
- consequent change. They move diagonally instead of moving forward, and capture by
- advancing to the next square vertically. The initial 2-squares jump is allowed,
- as well as en-passant captures: after 1.d2b4 on the diagram,
- 1...Pxc3 e.p. is possible.
+ consequent change. They move forward diagonally instead of moving straight,
+ and capture by advancing to the next square vertically.
+
+figure.diagram-container
+ .diagram
+ | fen:8/8/5p2/5P2/P7/8/5P2/8 b5,e3,d4,g3,h4,e6,f6,g6:
+ figcaption Possible pawn moves
p.
- Note about notation: pawns
+ The initial 2-squares jump is allowed, as well as en-passant captures:
+ after 1.d2b4 on the diagram, 1...Pxc3 e.p. is possible.
+
+p.
+ About notation: since pawn captures are non-ambigous they writes e.g.
+ "Pxe6" ('P' is redundant but looks nicer); simple pawn moves are often
+ ambiguous, so they write for example "f2g3" (again, the starting row is
+ redundant but this also looks better).
figure.diagram-container
.diagram
- | fen:r3kbnr/pp3ppp/3p4/4p3/8/8/PPPPPPPP/R1BQKBNR:
- figcaption After the moves 1.Nc3 d6?? 2.Nd5 e5 3.Nxc7
+ | fen:rnbqkbnr/p1pppppp/8/8/1Pp2P2/5N2/PPP1PPP1/R1BQKBNR c3:
+ figcaption.
+ After 1.Nf3 b7d5 2.h2f4 d5c4 3.d2b4, en-passant capture on marked square
+ is possible
-h3 More information
+h3 Other source
-p.
- Possible starting point Wikipedia page ?
+p
+ | See for example the
+ a(href="https://brainking.com/en/GameRules?tp=59") Berolina chess
+ | page on brainking.com.
--- /dev/null
+p.boxed
+ | Les pions avancent en diagonale, et capturent en montant
+ | d'une case verticalement.
+
+h3 Caractéristiques
+
+ul
+ li Échiquier: standard.
+ li Matériel: standard.
+ li Coups non capturants: mouvements de pions différents.
+ li Coups spéciaux: standards (prise en passant adaptée.
+ li Captures: standards (excepté les pions).
+ li Fin de partie: standard.
+
+h3 Bases
+
+p.
+ Seuls les déplacements de pions changent, mais puisqu'il y en a beaucoup sur
+ l'échiquier c'est un changement important. Ils se déplacent en diagonale
+ (toujours vers l'avant) au lieu d'avancer tout droit, et capturent en
+ montant d'une case verticalement.
+
+figure.diagram-container
+ .diagram
+ | fen:8/8/5p2/5P2/P7/8/5P2/8 b5,e3,d4,g3,h4,e6,f6,g6:
+ figcaption Possibles coups de pion
+
+p.
+ Le saut initial de deux cases est permis, ainsi que la prise en passant :
+ après 3.d2b4 sur le diagrame, 3...Pxc3 e.p. est possible.
+
+p.
+ Au sujet de la notation : puisque les captures effectuées par les pions ne
+ sont pas ambigues on les note par exemple "Pxe6" ('P' est redondant mais
+ l'écriture est plus jolie ainsi) ; en revanche les simples déplacements sont
+ en général ambigus, et donc notés par exemple "f2g3" (ici encore,
+ la rangée de départ est inutile mais l'écriture paraît mieux ainsi).
+
+figure.diagram-container
+ .diagram
+ | fen:rnbqkbnr/p1pppppp/8/8/1Pp2P2/5N2/PPP1PPP1/R1BQKBNR c3:
+ figcaption.
+ Après 1.Nf3 b7d5 2.h2f4 d5c4 3.d2b4, il est possible de capturer en passant
+ sur la case marquée
+
+h3 Autre source
+
+p
+ | Visitez par exemple la page
+ a(href="https://brainking.com/fr/GameRules?tp=59") Échecs Berolina
+ | page sur brainking.com.
p.boxed
| You only see what your pieces can reach. Incomplete information game.
-h3 Specifications
+h3 Divergences
ul
- li Chessboard: standard.
- li Material: standard.
- li Non-capturing moves: standard.
- li Special moves: standard.
- li Captures: standard.
li End of game: capture the king.
-p Incomplete information version of the orthodox game (also shuffled).
+p.
+ Incomplete information version of the orthodox game
+ (with randomized initial position).
h3 Basics
That is to say: empty squares where your pieces can go,
but also occupied squares where captures can be made.
For example on the illustration next, after 1.e4 d5 white sees a black
- pawn on d5, the squares e5, h5, b5, a6 and all four first ranks.
+ pawn on d5, the squares e5, f5, h5, b5, a6 and all four first ranks.
+ The f5 square is visible (and empty) because of the
+ special case of pawn capture.
figure.diagram-container
.diagram
- | fen:8/8/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR - w 8,7,b6,c6,d6,e6,f6,g6,h6,a5,c5,f5,g5:
+ | fen:8/8/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR - w 8,7,b6-h6,a5,c5,g5:
figcaption Standard initial position after 1.e4 d5
p.
Good luck!
p.
- Note: the bot is not cheating - it really uses only the information described earlier.
- Moreover, it is very basic and clearly less challenging that a human.
- But it may be a fun start :)
+ Note: the bot is not cheating - it really uses only the information
+ described earlier. Moreover, it is very basic and clearly less
+ challenging that a human. But it may be a fun start :)
h3 End of the game
-p Win by capturing the king (no checks, no stalemate).
+p Win by capturing the enemy king (no checks, no stalemate).
h3 More information
--- /dev/null
+p.boxed
+ | Vous ne voyez que ce que vos pièces peuvent atteindre.
+ | Jeu à information incomplète.
+
+h3 Divergences
+
+ul
+ li Fin de partie: capturer le roi.
+
+p.
+ Version à information incomplète du jeu d'échecs orthodoxe
+ (avec position initiale aléatoire).
+
+p Incomplete information version of the orthodox game (also shuffled).
+
+h3 Bases
+
+p.
+ Avant chaque coup, les joueurs ne voient que ce que leurs pièces peuvent
+ atteindre. C'est-à-dire : les cases vides où peuvent se déplacer les pièces,
+ mais aussi les cases occupées où des captures sont possibles.
+ Par exemple sur l'illustration suivante, après 1.e4 d5 les blancs voient
+ un pion noir en d5, les cases e5, f5, h5, b5, a6 et les quatre premières
+ rangées. La case f5 est visible (et vide) grâce au mode de capture
+ particulier des pions.
+
+figure.diagram-container
+ .diagram
+ | fen:8/8/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR - w 8,7,b6-h6,a5,c5,g5:
+ figcaption Position de départ habituelle après 1.e4 d5
+
+p.
+ Choisissez votre coup prudemment, en vous basant sur ce qui est devinable des
+ derniers coups adverses. En particulier, compter le matériel est
+ indispensable. Bonne chance !
+
+p.
+ Note : le robot joueur ne triche pas - il n'utilise que l'information décrite
+ plus haut. De plus il est très basique et clairement moins performant qu'un
+ humain. Ceci dit il peut être amusant de commencer contre lui :)
+
+h3 Fin de partie
+
+p Gagnez en capturant le roi adverse (pas d'échecs, pas de pat).
+
+h3 Plus d'information
+
+p
+ | J'ai découvert cette variante sur
+ a(href="https://www.buho21.com/") Buho21
+ | , qui dispose d'une belle interface mais dont l'esprit est très différent
+ | d'ici (système de classement, abonnement VIP ou publicités, etc.).
+ | Il semblait être le seul endroit où jouer en direct à cette variante.
h3 Basics
p.
- TODO: explain, every turn twice except if check on 1st turn, or
- very first move in game.
- En-passant: possible in any order if 2 ep squares,
- otherwise has to be the first move.
- OK even if opponent moved his pawn at subturn 1.
+ At the very first move of the game, white make only one move - as usual.
+ However, after that and for all the game each side must play twice at
+ every turn. There are two exceptions:
+
+ul
+ li.
+ If the first move gives check (maybe checkmate),
+ then a second move isn't played.
+ li.
+ If no move is available after the first move, then it's stalemate
+ and again, there is no second move.
p.
- PGN game notation: since there are two moves at each turn except on move 1,
- a double move in the game is indicated as two comma-separated (ordered) moves,
- as in 3.Na5,Bd3 e6,f4 (the two first are white moves,
+ About the PGN game notation: when a side plays two moves in a row,
+ they are separated (in order) by a comma in the PGN.
+ Example: 3.Na5,Bd3 e6,f4 (the two first are white moves,
the two others are black moves).
- Sometimes a move gives check, or stalemate occurs after subTurn 1 :: just one move
figure.diagram-container
.diagram
| fen:r1bqkbnr/pppp1p1p/2n5/4p2p/4P3/5N2/PPPP1PPP/RNB1KB1R:
figcaption After the moves 1.e4 e5,Nc6 2.Qh5,Nf3 g6,gxh5
+h3 En-passant capture
+
+p.
+ Capturing en-passant is allowed under certain conditions.
+ If the opponent moved a pawn allowing such a capture (once or twice),
+ then (to take it) you must capture en-passant at the first move of your turn.
+ After that, if (and only if) there is another en-passant capture available
+ you can play it on the second move.
+
+p.
+ Note: if a pawn 2-squares jump was made and then a piece landed at the
+ en-passant square at the second move, a pawn capture on this square
+ takes only the piece.
+
h3 More information
p
--- /dev/null
+p.boxed
+ | Jouez deux coups à chaque tour.
+
+h3 Divergences
+
+p.
+ La seule différence avec le jeu orthodoxe est la règle du double-coup, mais cela
+ affecte beaucoup le jeu.
+
+h3 Bases
+
+p.
+ Au tout début de la partie les blancs ne jouent qu'un seul coup, comme
+ d'habitude. Cependant, après cela et ce pour tout le reste de la partie
+ chaque camp doit jouer deux coups à chaque tour. Avec deux exceptions :
+
+ul
+ li.
+ Si le premier coup donne échec (peut-être mat),
+ alors un second coup n'est pas joué.
+ li.
+ Si aucun coup n'est autorisé après le premier, alors c'est pat et
+ une fois encore il n'y a pas de deuxième coup.
+
+p.
+ Au sujet du format PGN de la partie : quand un camp joue deux coups d'affilée,
+ ils sont séparés (dans l'ordre) par une virgule. Exemple : 3.Na5,Bd3 e6,f4
+ (les deux premiers sont des coups blancs, les deux suivants
+ sont des coups noirs).
+
+figure.diagram-container
+ .diagram
+ | fen:r1bqkbnr/pppp1p1p/2n5/4p2p/4P3/5N2/PPPP1PPP/RNB1KB1R:
+ figcaption Après les coups 1.e4 e5,Nc6 2.Qh5,Nf3 g6,gxh5
+
+h3 Prise en passant
+
+p.
+ Capturer en passant est autorisé sous certaines conditions.
+ Si l'adversaire a déplacé un pion permettant une telle capture
+ (une fois ou deux fois), alors pour en profiter il faut prendre en passant
+ dès le premier coup de votre tour. Ensuite, si (et seulement si) il y a
+ une autre prise en passant disponible, vous pouvez l'exécuter au second coup.
+
+p.
+ Note : si un pion se déplace de deux cases puis qu'une pièce occupe la case de
+ prise en passant au second coup d'un tour, une capture sur cette case ne
+ prendra que la pièce.
+
+h3 Plus d'information
+
+p
+ | Voir par exemple la page
+ a(href="https://www.chessvariants.com/multimove.dir/marseill.html")
+ | Échecs Marseillais
+ | sur chessvariants.com.
p.boxed
| Pawns start on the 7th rank. Move a knight to promote them.
-h3 Specifications
+h3 Divergences
-ul
- li Chessboard: standard.
- li Material: standard.
- li Non-capturing moves: standard.
- li Special moves: no castling (and no en-passant).
- li Captures: standard.
- li End of game: standard.
+p No castle, no en-passant capture.
p.
- ...(Almost) Only the initial position changes, but this is a very big change.
+ ...Only the initial position changes, but this makes a huge difference.
In particular, castling would be rather pointless so it's disabled here.
En-passant captures are impossible because all pawns already reached 7th rank.
-h3 Note on initial position
+h3 About the initial position
p.
- Since truly random start can allow un-defendable mate in 3 with a knight,
- the kings touch at least one knight in the initial position.
+ Since truly random start can allow a mate in 3 with a knight,
+ the kings have at least one knight neighbor in the initial position.
This allows to move free out of potential check from the very beginning.
p.
- To illustrate this phenomenon, although it's defendable the standard initial
- position allows the attack as 1.Na6 (threatening Nc5-Nd3#),
- forcing the defense Nf3-Ne5. With a knight next to the king you have more options.
+ A less constraining condition would be to require the two knights to stand on
+ two squares of different colors, but it's not enough as proved by the
+ following diagram.
+ White can mate in 3: 1.Nc6 followed by Nb4 threatening both a2 and d3.
figure.diagram-container
.diagram
- | fen:R1BQKBNR/PPPPPPP/N7/8/8/8/pppppppp/rnbqkbnr:
- figcaption Standard initial position after 1.Na6
+ | fen:RBN1BRRQ/PPPPPPP/8/4n3/8/8/Nppppppp/brkbqr1n:
+ figcaption After 1.Nc6 Nf3 2.Nb4 Ne5 (covers d3 but not a2) 3.Nxa2#
h3 Source
--- /dev/null
+p.boxed
+ | Pawns start on the 7th rank. Move a knight to promote them.
+
+h3 Specifications
+
+p Pas de roque ni prise en passant.
+
+p.
+ ...Seule la position de départ change, mais c'est une énorme différence.
+ En particulier, le roque serait sans intérêt et est donc désactivé ici.
+ Les captures en passant sont également impossible car tous les pions
+ sont déjà sur la 7eme rangée.
+
+h3 Au sujet de la position initiale
+
+p.
+ Un placement complètement aléatoire des pièces peut permettre un mat en 3
+ à l'aide d'un cavalier : c'est pourquoi le roi est toujours à côté d'au moins
+ un cavalier en début de partie. Cela permet de se dégager des échecs dès le
+ premier coup.
+
+p.
+ Pour illustrer ce phénomène, les blancs peuvent mater en 3 dans la position
+ du diagramme suivant : 1.Na6 suivi de Nc5 puis Nd3#.
+ Si le cavalier noir était en g1 une défense serait possible par Nf3-Ne5 ;
+ mais avec un cavalier voisin du roi plus d'options sont offertes.
+
+figure.diagram-container
+ .diagram
+ | fen:R1BQKBNR/PPPPPPP/N7/8/8/8/pppppppp/rnbqkbrn c5,d3:
+ figcaption Standard initial position after 1.Na6
+
+h3 Source
+
+p
+ | Voir par exemple la page
+ a(href="https://www.chessvariants.com/diffsetup.dir/upside.html") Échecs Upside down
+ | sur chessvariants.com.