--- /dev/null
+Copyright 2018 Benjamin AUder
+
+Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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 firstRank = (color == 'w' ? sizeX-1 : 0);
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
- if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank)
+ if (x+shiftX >= 0 && x+shiftX < sizeX) //TODO: always true
{
- // Normal moves
- if (this.board[x+shift][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)
{
- moves.push(this.getBasicMove([x,y], [x+shift,y]));
- // Next condition because variants with pawns on 1st rank allow them to jump
- if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
+ 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*shift,y]));
+ moves.push(this.getBasicMove([x,y], [x+2*shiftX,y]));
}
}
// Captures
- if (y>0 && this.board[x+shift][y-1] != V.EMPTY
- && this.canTake([x,y], [x+shift,y-1]))
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
- }
- if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
- && this.canTake([x,y], [x+shift,y+1]))
+ for (let shiftY of [-1,1])
{
- moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
- }
- }
-
- if (x+shift == lastRank)
- {
- // Promotion
- const pawnColor = this.getColor(x,y); //can be different for checkered
- let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
- promotionPieces.forEach(p => {
- // Normal move
- if (this.board[x+shift][y] == V.EMPTY)
- moves.push(this.getBasicMove([x,y], [x+shift,y], {c:pawnColor,p:p}));
- // Captures
- if (y>0 && this.board[x+shift][y-1] != V.EMPTY
- && this.canTake([x,y], [x+shift,y-1]))
+ if (y + shiftY >= 0 && y + shiftY < sizeY
+ && this.board[x+shiftX][y+shiftY] != V.EMPTY
+ && this.canTake([x,y], [x+shiftX,y+shiftY]))
{
- moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:pawnColor,p:p}));
- }
- if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
- && this.canTake([x,y], [x+shift,y+1]))
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:pawnColor,p:p}));
+ for (let piece of finalPieces)
+ {
+ moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
+ {c:pawnColor,p:piece}));
+ }
}
- });
+ }
}
- // En passant
- const Lep = this.epSquares.length;
- const epSquare = this.epSquares[Lep-1]; //always at least one element
- if (!!epSquare && epSquare.x == x+shift && Math.abs(epSquare.y - y) == 1)
+ if (V.HasEnpassant)
{
- const epStep = epSquare.y - y;
- let enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
- enpassantMove.vanish.push({
- x: x,
- y: y+epStep,
- p: 'p',
- c: this.getColor(x,y+epStep)
- });
- moves.push(enpassantMove);
+ // En passant
+ const Lep = this.epSquares.length;
+ const epSquare = this.epSquares[Lep-1]; //always at least one element
+ if (!!epSquare && epSquare.x == x+shiftX && Math.abs(epSquare.y - y) == 1)
+ {
+ let enpassantMove = this.getBasicMove([x,y], [epSquare.x,epSquare.y]);
+ enpassantMove.vanish.push({
+ x: x,
+ y: epSquare.y,
+ p: 'p',
+ c: this.getColor(x,epSquare.y)
+ });
+ moves.push(enpassantMove);
+ }
}
return moves;
},
[h('i', { 'class': { "material-icons": true } }, "accessibility")])
);
- if (["idle","chat","computer"].includes(this.mode))
+ if (variant!="Dark" && ["idle","chat","computer"].includes(this.mode))
{
actionArray.push(
h('button',
[h('i', { 'class': { "material-icons": true } }, "computer")])
);
}
- if (["idle","chat","friend"].includes(this.mode))
+ if (variant!="Dark" && ["idle","chat","friend"].includes(this.mode))
{
actionArray.push(
h('button',
_.range(sizeY).map(j => {
let cj = (this.mycolor=='w' ? j : sizeY-j-1);
let elems = [];
- if (this.vr.board[ci][cj] != VariantRules.EMPTY)
+ if (this.vr.board[ci][cj] != VariantRules.EMPTY && (variant!="Dark"
+ || this.score!="*" || this.vr.isEnlightened(ci,cj,this.mycolor)))
{
elems.push(
h(
'light-square': (i+j)%2==0,
'dark-square': (i+j)%2==1,
[this.color]: true,
+ 'in-shadow': variant=="Dark" && this.score=="*"
+ && !this.vr.isEnlightened(ci,cj,this.mycolor),
'highlight': showLight && !!lm && _.isMatch(lm.end, {x:ci,y:cj}),
'incheck': showLight && incheckSq[ci][cj],
},
)
);
}
- // Show current FEN
- elementArray.push(
- h('div',
- {
- attrs: { id: "fen-div" },
- "class": { "section-content": true },
- },
- [
- h('p',
- {
- attrs: { id: "fen-string" },
- domProps: { innerHTML: this.vr.getBaseFen() },
- "class": { "text-center": true },
- }
- )
- ]
- )
- );
+ if (variant != "Dark" || this.score!="*")
+ {
+ // Show current FEN
+ elementArray.push(
+ h('div',
+ {
+ attrs: { id: "fen-div" },
+ "class": { "section-content": true },
+ },
+ [
+ h('p',
+ {
+ attrs: { id: "fen-string" },
+ domProps: { innerHTML: this.vr.getBaseFen() },
+ "class": { "text-center": true },
+ }
+ )
+ ]
+ )
+ );
+ }
}
return h(
'div',
if (!!menuToggle)
menuToggle.checked = false;
},
+ notDark: function() {
+ return variant != "Dark";
+ },
},
});
--- /dev/null
+class BerolinaRules extends ChessRules
+{
+ // En-passant after 2-sq jump
+ getEpSquare(moveOrSquare)
+ {
+ if (!moveOrSquare)
+ return undefined;
+ if (typeof moveOrSquare === "string")
+ {
+ const square = moveOrSquare;
+ if (square == "-")
+ return undefined;
+ return V.SquareToCoords(square);
+ }
+ // Argument is a move:
+ const move = moveOrSquare;
+ const [sx,ex,sy] = [move.start.x,move.end.x,move.start.y];
+ if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) == 2)
+ {
+ return {
+ x: ex,
+ y: (move.end.y + sy)/2
+ };
+ }
+ return undefined; //default
+ }
+
+ // Special pawn rules: promotions to captured friendly pieces,
+ // optional on ranks 8-9 and mandatory on rank 10.
+ getPotentialPawnMoves([x,y])
+ {
+ const color = this.turn;
+ let moves = [];
+ const [sizeX,sizeY] = [V.size.x,V.size.y];
+ const shiftX = (color == "w" ? -1 : 1);
+ const firstRank = (color == 'w' ? sizeX-1 : 0);
+ const startRank = (color == "w" ? sizeX-2 : 1);
+ const lastRank = (color == "w" ? 0 : sizeX-1);
+
+ if (x+shiftX >= 0 && x+shiftX < sizeX) //TODO: always true
+ {
+ 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)
+ {
+ for (let piece of finalPieces)
+ {
+ moves.push(this.getBasicMove([x,y], [x+shiftX,y+shiftY],
+ {c:pawnColor,p:piece}));
+ }
+ if (x == startRank && this.board[x+2*shiftX][y] == 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+shiftY],
+ {c:pawnColor,p:piece}));
+ }
+ }
+ }
+
+ // En passant
+ const Lep = this.epSquares.length;
+ const epSquare = this.epSquares[Lep-1]; //always at least one element
+ if (!!epSquare && epSquare.x == x+shiftX && epSquare.y == y)
+ {
+ let enpassantMove = this.getBasicMove([x,y], [x+shift,y]);
+ enpassantMove.vanish.push({
+ x: epSquare.x,
+ y: y,
+ p: 'p',
+ c: this.getColor(epSquare.x,y)
+ });
+ moves.push(enpassantMove);
+ }
+
+ return moves;
+ }
+}
+
+const VariantRules = BerolinaRules;
--- /dev/null
+class Chess960Rules extends ChessRules
+{
+ // Standard rules, in the shadow
+ setOtherVariables(fen)
+ {
+ super.setOtherVariables(fen);
+ const [sizeX,sizeY] = {V.size.x,V.size.y};
+ this.enlightened = {
+ "w": doubleArray(sizeX,sizeY,false),
+ "b": doubleArray(sizeX,sizeY,false)
+ };
+ setup enlightened: squares reachable by each side (TODO: one side would be enough)
+ }
+
+ isEnlightened(x, y, color)
+ {
+ //TODO: artificlaly change turn
+ }
+
+ getAllPotentialMoves()
+ {
+ let moves = []; //TODO
+ }
+
+ atLeastOneMove()
+ {
+ if (this.kingPos[this.turn][0] < 0)
+ return false;
+ return true; //TODO: is it right?
+ }
+
+ underCheck(move)
+ {
+ return false; //there is no check
+ }
+
+ getCheckSquares(move)
+ {
+ const c = this.getOppCol(this.turn); //opponent
+ const saveKingPos = this.kingPos[c]; //king might be taken
+ this.play(move);
+ // The only way to be "under check" is to have lost the king (thus game over)
+ let res = this.kingPos[c][0] < 0
+ ? [JSON.parse(JSON.stringify(saveKingPos))]
+ : [];
+ this.undo(move);
+ return res;
+ }
+
+ // NOTE: no (un)updateVariables() because no computer mode
+ // --> but isEnlightened() should have its variable updated
+ // --> in fact an array is enough (no need for a function)
+ // recomputed after every play/undo (although there are no undo here for now)
+
+ checkGameEnd()
+ {
+ // No valid move: our king disappeared
+ return this.turn == "w" ? "0-1" : "1-0";
+ }
+
+ static get THRESHOLD_MATE()
+ {
+ return 500; //checkmates evals may be slightly below 1000
+ }
+}
+
+const VariantRules = DarkRules;
// TODO: some redundant checks
if (epsq.x == x+shift && Math.abs(epsq.y - y) == 1)
{
- let epStep = epsq.y - y;
- var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
+ var enpassantMove = this.getBasicMove([x,y], [x+shift,epsq.y]);
enpassantMove.vanish.push({
x: x,
- y: y+epStep,
+ y: epsq.y,
p: 'p',
- c: this.getColor(x,y+epStep)
+ c: this.getColor(x,epsq.y)
});
moves.push(enpassantMove);
}
return moves;
}
- getPotentialPawnMoves([x,y])
- {
- const color = this.turn;
- let moves = [];
- const [sizeX,sizeY] = [V.size.x,V.size.y];
- const shift = (color == "w" ? -1 : 1);
- const firstRank = (color == 'w' ? sizeX-1 : 0);
- const startRank = (color == "w" ? sizeX-2 : 1);
- const lastRank = (color == "w" ? 0 : sizeX-1);
-
- if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank)
- {
- // Normal moves
- if (this.board[x+shift][y] == V.EMPTY)
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y]));
- // Next condition because variants with pawns on 1st rank allow them to jump
- if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
- {
- // Two squares jump
- moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
- }
- }
- // Captures
- if (y>0 && this.board[x+shift][y-1] != V.EMPTY
- && this.canTake([x,y], [x+shift,y-1]))
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
- }
- if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
- && this.canTake([x,y], [x+shift,y+1]))
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
- }
- }
-
- if (x+shift == lastRank)
- {
- // Promotion
- const pawnColor = this.getColor(x,y); //can be different for checkered
- let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
- promotionPieces.forEach(p => {
- // Normal move
- if (this.board[x+shift][y] == V.EMPTY)
- moves.push(this.getBasicMove([x,y], [x+shift,y], {c:pawnColor,p:p}));
- // Captures
- if (y>0 && this.board[x+shift][y-1] != V.EMPTY
- && this.canTake([x,y], [x+shift,y-1]))
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:pawnColor,p:p}));
- }
- if (y<sizeY-1 && this.board[x+shift][y+1] != V.EMPTY
- && this.canTake([x,y], [x+shift,y+1]))
- {
- moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:pawnColor,p:p}));
- }
- });
- }
- return moves; //no en-passant
- }
-
// Complete a move with magnetic actions
// TODO: job is done multiple times for (normal) promotions.
applyMagneticLaws(move)
// TODO: some redundant checks
if (epsq.x == x+shift && Math.abs(epsq.y - y) == 1)
{
- let epStep = epsq.y - y;
- var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
+ var enpassantMove = this.getBasicMove([x,y], [x+shift,epsq.y]);
enpassantMove.vanish.push({
x: x,
- y: y+epStep,
+ y: epsq.y,
p: 'p',
- c: this.getColor(x,y+epStep)
+ c: this.getColor(x,epsq.y)
});
moves.push(enpassantMove);
}
.highlight
background-color: #00cc66 !important
+.in-shadow
+ opacity: 0.5
+
.incheck
background-color: #cc3300 !important
=translations["Rules"]
a(href="#play" @click="setDisplay('play')")
=translations["Play"]
- a(href="#problems" @click="setDisplay('problems')")
+ a(href="#problems" v-if="notDark()" @click="setDisplay('problems')")
=translations["Problems"]
#flagMenu.clickable(
onClick="document.getElementById('modalLang').checked=true")
.row
my-rules(v-show="display=='rules'")
my-game(v-show="display=='play'" v-bind:problem="problem")
- my-problems(v-show="display=='problems'" v-on:show-problem="showProblem($event)")
+ my-problems(v-if="notDark()" v-show="display=='problems'"
+ v-on:show-problem="showProblem($event)")
block javascripts
script(src="/javascripts/utils/misc.js")