explain why the player is in check using chess notation
This commit is contained in:
parent
6e552cfa4e
commit
6d4923755e
|
|
@ -116,8 +116,8 @@ button:disabled .silhouette {
|
|||
overflow: auto;
|
||||
}
|
||||
|
||||
#cb_scrollable p {
|
||||
margin-top: 0;
|
||||
#cb_scrollable > *:last-child {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#cb_controls, #cb_theme, #cb_names, #cb_message, #cb_navigate {
|
||||
|
|
@ -191,12 +191,18 @@ button:disabled .silhouette {
|
|||
|
||||
#cb_history {
|
||||
max-width: 7.5in;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
#cb_history_future {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
#cb_explain_check {
|
||||
color: red;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.cb-hbox {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
|
|
|
|||
|
|
@ -221,7 +221,10 @@
|
|||
<div id="cb_message"></div>
|
||||
</form>
|
||||
<div id="cb_scrollable">
|
||||
<p id="cb_history"><span id="cb_history_past"></span><span id="cb_history_future"></span></p>
|
||||
<div id="cb_history">
|
||||
<span id="cb_history_past"></span><span id="cb_history_future"></span>
|
||||
<div id="cb_explain_check"></div>
|
||||
</div>
|
||||
<details>
|
||||
<summary>Rule Reference</summary>
|
||||
<p>The basic movement for each piece is the same as traditional chess.</p>
|
||||
|
|
|
|||
142
js/pacosako.js
142
js/pacosako.js
|
|
@ -394,22 +394,6 @@ function addHistory(game) {
|
|||
result += '(' + move.promotion.toUpperCase() + ')';
|
||||
}
|
||||
|
||||
if (game.status === CHECKMATE) {
|
||||
result += '#';
|
||||
} else if (game.isInCheck()) {
|
||||
result += '+';
|
||||
}
|
||||
|
||||
let winner = game.winner;
|
||||
|
||||
if (winner === LIGHT) {
|
||||
result += ' 1-0';
|
||||
} else if (winner === DARK) {
|
||||
result += ' 0-1';
|
||||
} else if (game.status !== PLAYING) {
|
||||
result += ' \u00bd-\u00bd'; /* 1/2-1/2 */
|
||||
}
|
||||
|
||||
game._history += result;
|
||||
}
|
||||
|
||||
|
|
@ -420,20 +404,16 @@ class Game {
|
|||
throw { message: 'can only clone from another Game instance' };
|
||||
}
|
||||
|
||||
this._board = new Board(original._board);
|
||||
this._player = original._player;
|
||||
this._status = original._status;
|
||||
this._moves = original._moves.slice();
|
||||
this._redo = original._redo.slice();
|
||||
this._history = original._history;
|
||||
this._turn = original._turn;
|
||||
this._undo = original._undo;
|
||||
this._checkCache = Object.assign({}, original._checkCache);
|
||||
|
||||
this._castling = {
|
||||
[LIGHT]: Object.assign({}, original._castling[LIGHT]),
|
||||
[DARK]: Object.assign({}, original._castling[DARK]),
|
||||
};
|
||||
Object.assign(this, original, {
|
||||
_board: new Board(original._board),
|
||||
_moves: original._moves.slice(),
|
||||
_redo: original._redo.slice(),
|
||||
_checkCache: Object.assign({}, original._checkCache),
|
||||
_castling: {
|
||||
[LIGHT]: Object.assign({}, original._castling[LIGHT]),
|
||||
[DARK]: Object.assign({}, original._castling[DARK]),
|
||||
},
|
||||
});
|
||||
} else {
|
||||
this._board = new Board();
|
||||
this._player = LIGHT;
|
||||
|
|
@ -444,6 +424,7 @@ class Game {
|
|||
this._turn = 0;
|
||||
this._undo = null;
|
||||
this._checkCache = {};
|
||||
this._ignoreCheck = false;
|
||||
|
||||
/* set to false when the king or rook moves */
|
||||
this._castling = {
|
||||
|
|
@ -473,6 +454,14 @@ class Game {
|
|||
return JSON.parse(JSON.stringify(this._redo));
|
||||
}
|
||||
|
||||
get ignoreCheck() {
|
||||
return this._ignoreCheck;
|
||||
}
|
||||
|
||||
set ignoreCheck(value) {
|
||||
this._ignoreCheck = !!value;
|
||||
}
|
||||
|
||||
canCapture(side, from) {
|
||||
if (from === PHANTOM) {
|
||||
return true;
|
||||
|
|
@ -688,6 +677,7 @@ class Game {
|
|||
/* check is evaluated as if the current player passed without moving */
|
||||
const sim = new Game(this);
|
||||
sim.dropHistory();
|
||||
sim.ignoreCheck = true;
|
||||
sim._player = other;
|
||||
|
||||
/* start with the opponent's unpaired pieces, the ones that can capture */
|
||||
|
|
@ -698,7 +688,30 @@ class Game {
|
|||
if (sim._board.getPiece(other, from) !== KING) {
|
||||
if (sim.isLegalMove(other, from, king, true)) {
|
||||
/* this piece can directly capture the king */
|
||||
return recordCheck(this, true);
|
||||
let check = true;
|
||||
try {
|
||||
if (this._history !== undefined) {
|
||||
const hist = new Game(this);
|
||||
hist.ignoreCheck = true;
|
||||
if (hist._player !== other) {
|
||||
if (hist._player === LIGHT) {
|
||||
hist._turn += 1;
|
||||
hist._history += ` ${hist._turn}.${NBSP}…`;
|
||||
} else {
|
||||
hist._history += ` …`;
|
||||
}
|
||||
hist._player = other;
|
||||
}
|
||||
const turn_before = hist._turn;
|
||||
const history_before = hist._history;
|
||||
hist.move(from, king);
|
||||
check = hist.history.slice(history_before.length).trimStart();
|
||||
if (!check.match(/^\d+\./)) {
|
||||
check = `${turn_before}.${NBSP}… ${check}`;
|
||||
}
|
||||
}
|
||||
} catch(err) {}
|
||||
return recordCheck(this, check);
|
||||
}
|
||||
|
||||
queue.push({ game: sim, from });
|
||||
|
|
@ -747,7 +760,33 @@ class Game {
|
|||
|
||||
if (game2.isLegalMove(other, PHANTOM, king, true)) {
|
||||
/* we have our answer */
|
||||
return recordCheck(this, true);
|
||||
let check = true;
|
||||
try {
|
||||
if (this._history !== undefined) {
|
||||
const hist = new Game(this);
|
||||
hist.ignoreCheck = true;
|
||||
if (hist._player !== other) {
|
||||
if (hist._player === LIGHT) {
|
||||
hist._turn += 1;
|
||||
hist._history += ` ${hist._turn}.${NBSP}…`;
|
||||
} else {
|
||||
hist._history += ` …`;
|
||||
}
|
||||
hist._player = other;
|
||||
}
|
||||
const turn_before = hist._turn;
|
||||
const history_before = hist._history;
|
||||
for (const move of game2._moves.slice(hist._moves.length)) {
|
||||
hist.replayMove(move);
|
||||
}
|
||||
hist.move(PHANTOM, king);
|
||||
check = hist.history.slice(history_before.length).trimStart();
|
||||
if (!check.match(/^\d+\./)) {
|
||||
check = `${turn_before}.${NBSP}… ${check}`;
|
||||
}
|
||||
}
|
||||
} catch(err) {}
|
||||
return recordCheck(this, check);
|
||||
}
|
||||
|
||||
queue.push({ game: game2, from: PHANTOM });
|
||||
|
|
@ -762,6 +801,7 @@ class Game {
|
|||
const side = this._player;
|
||||
const sim = new Game(this);
|
||||
sim.dropHistory();
|
||||
sim.ignoreCheck = true;
|
||||
|
||||
try {
|
||||
sim.move(from, to);
|
||||
|
|
@ -936,14 +976,14 @@ class Game {
|
|||
this._moves.push(move);
|
||||
this._redo = [];
|
||||
|
||||
addHistory(this);
|
||||
|
||||
let inCheck = false;
|
||||
|
||||
if (took === KING) {
|
||||
this._status = CHECKMATE;
|
||||
} else if (this._history && this.isInCheck(this._player)) {
|
||||
/*
|
||||
* NOTE: Preemptive checkmate detection is not performed on
|
||||
* simulated games (without history) to prevent recursion.
|
||||
*/
|
||||
|
||||
} else if (!this.ignoreCheck && this.isInCheck(this._player)) {
|
||||
inCheck = true;
|
||||
let canEscape = false;
|
||||
|
||||
for (const tryFrom of this._board.findPieces(this._player, true)) {
|
||||
|
|
@ -964,7 +1004,23 @@ class Game {
|
|||
}
|
||||
}
|
||||
|
||||
addHistory(this);
|
||||
if (this._history !== undefined) {
|
||||
if (this._status === CHECKMATE) {
|
||||
this._history += '#';
|
||||
} else if (inCheck) {
|
||||
this._history += '+';
|
||||
}
|
||||
|
||||
let winner = this.winner;
|
||||
|
||||
if (winner === LIGHT) {
|
||||
this._history += ' 1-0';
|
||||
} else if (winner === DARK) {
|
||||
this._history += ' 0-1';
|
||||
} else if (this._status !== PLAYING) {
|
||||
this._history += ' \u00bd-\u00bd'; /* 1/2-1/2 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resign(meta) {
|
||||
|
|
@ -988,6 +1044,14 @@ class Game {
|
|||
this._status = RESIGNED;
|
||||
|
||||
addHistory(this);
|
||||
|
||||
if (this._history !== undefined) {
|
||||
if (move.side === LIGHT) {
|
||||
this._history += ' 0-1';
|
||||
} else {
|
||||
this._history += ' 1-0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get lastMove() {
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ $(function (){
|
|||
$('#cb_board .cb-end').removeClass('cb-end');
|
||||
$('#cb_board .cb-legal').removeClass('cb-legal');
|
||||
$('#cb_phantom').appendTo('#cb_hidden');
|
||||
$('#cb_explain_check').text('');
|
||||
|
||||
const game = visibleGame;
|
||||
const board = game.board;
|
||||
|
|
@ -294,6 +295,7 @@ $(function (){
|
|||
|
||||
const liveView = !game.canRedo;
|
||||
const playing = game.status === PS.PLAYING;
|
||||
const clss = game.player === PS.LIGHT ? '.cb-lt-piece' : '.cb-dk-piece';
|
||||
|
||||
const phantom = board.phantom;
|
||||
if (phantom) {
|
||||
|
|
@ -306,15 +308,20 @@ $(function (){
|
|||
pieceStartMove(piece, 'click');
|
||||
}
|
||||
} else if (liveView && playing) {
|
||||
const clss = game.player === PS.LIGHT ? '.cb-lt-piece' : '.cb-dk-piece';
|
||||
const pieces = $('#cb_board ' + clss)
|
||||
pieces.parent().on('click.select', squareClickSelect);
|
||||
pieces.draggable('enable');
|
||||
}
|
||||
|
||||
if (game.isInCheck()) {
|
||||
const clss = game.player === PS.LIGHT ? '.cb-lt-piece' : '.cb-dk-piece';
|
||||
$('#cb_board ' + clss + '.cb-king').addClass('cb-in-check');
|
||||
const check = game.isInCheck();
|
||||
const king = $('#cb_board ' + clss + '.cb-king');
|
||||
|
||||
if (check) {
|
||||
king.addClass('cb-in-check');
|
||||
}
|
||||
|
||||
if (typeof check === 'string') {
|
||||
$('#cb_explain_check').text(`(Check: ${check})`);
|
||||
}
|
||||
|
||||
let msg = '';
|
||||
|
|
|
|||
Loading…
Reference in New Issue