add ability to detect obvious check (chains of 6 moves or less)
This commit is contained in:
parent
75b66e6ce3
commit
fcb14d489c
217
js/iterator.js
217
js/iterator.js
|
|
@ -1,63 +1,120 @@
|
|||
'use strict';
|
||||
|
||||
export const IteratorMixin = {
|
||||
map: function* map(f) {
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
yield f(y.value);
|
||||
function identity(x) {
|
||||
return x;
|
||||
}
|
||||
return y.value;
|
||||
},
|
||||
|
||||
filter: function* filter(f) {
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
if (f(y.value)) {
|
||||
yield y.value;
|
||||
}
|
||||
}
|
||||
return y.value;
|
||||
},
|
||||
export class Iterator {
|
||||
constructor(from) {
|
||||
let next;
|
||||
|
||||
take: function* take(limit) {
|
||||
let remaining = Number(limit) & -1;
|
||||
let y;
|
||||
while (remaining > 0 && !(y = this.next()).done) {
|
||||
yield y.value;
|
||||
remaining -= 1;
|
||||
}
|
||||
},
|
||||
|
||||
drop: function* drop(limit) {
|
||||
let remaining = Number(limit) & -1;
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
if (remaining <= 0) {
|
||||
yield y.value;
|
||||
if (Symbol.iterator in from.__proto__) {
|
||||
const iter = from.__proto__[Symbol.iterator].call(from);
|
||||
next = iter.next.bind(iter);
|
||||
} else if (typeof from === 'function') {
|
||||
next = from;
|
||||
} else {
|
||||
throw new TypeError('Iterator class needs iterable input');
|
||||
}
|
||||
|
||||
Object.defineProperty(this, 'next', {
|
||||
value: next,
|
||||
writable: false,
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
});
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this;
|
||||
}
|
||||
|
||||
map(f) {
|
||||
const next = this.next;
|
||||
return new Iterator(function() {
|
||||
const y = next();
|
||||
if (y.done) {
|
||||
return y;
|
||||
} else {
|
||||
return { value: f(y.value), done: false };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
filter(f) {
|
||||
const next = this.next;
|
||||
return new Iterator(function() {
|
||||
let y;
|
||||
while (!(y = next()).done && !f(y.value)) {
|
||||
continue;
|
||||
}
|
||||
return y;
|
||||
});
|
||||
}
|
||||
|
||||
take(limit) {
|
||||
let remaining = Number(limit) & -1;
|
||||
const next = this.next;
|
||||
return new Iterator(function() {
|
||||
if (remaining > 0) {
|
||||
remaining -= 1;
|
||||
return next();
|
||||
} else {
|
||||
return { value: undefined, done: true };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
drop(limit) {
|
||||
let remaining = Number(limit) & -1;
|
||||
const next = this.next;
|
||||
return new Iterator(function() {
|
||||
while (remaining > 0 && !next().done) {
|
||||
remaining -= 1;
|
||||
}
|
||||
return next();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
asIndexedPairs: function* asIndexedPairs() {
|
||||
asIndexedPairs() {
|
||||
const next = this.next;
|
||||
let index = 0;
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
yield [index++, x];
|
||||
return new Iterator(function() {
|
||||
const y = next();
|
||||
if (y.done) {
|
||||
return y;
|
||||
} else {
|
||||
return { value: [index++, y.value], done: false };
|
||||
}
|
||||
return y.value;
|
||||
},
|
||||
|
||||
flatMap: function* flatMap(f) {
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
yield* f(y.value);
|
||||
});
|
||||
}
|
||||
return y.value;
|
||||
},
|
||||
|
||||
reduce: function reduce(f, state) {
|
||||
flatMap(f) {
|
||||
const next = this.next;
|
||||
let innerNext;
|
||||
|
||||
return new Iterator(function() {
|
||||
for (;;) {
|
||||
if (innerNext) {
|
||||
let y = innerNext();
|
||||
if (!y.done) {
|
||||
return y;
|
||||
}
|
||||
}
|
||||
|
||||
let z = next();
|
||||
if (z.done) {
|
||||
innerNext = undefined;
|
||||
return z;
|
||||
}
|
||||
|
||||
const iter = y.value.__proto__[Symbol.iterator].call(y.value);
|
||||
innerNext = iter.next.bind(iter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reduce(f, state) {
|
||||
if (typeof state === 'undefined') {
|
||||
const first = this.next();
|
||||
if (first.done) {
|
||||
|
|
@ -65,52 +122,60 @@ export const IteratorMixin = {
|
|||
}
|
||||
state = first.value;
|
||||
}
|
||||
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
state = f(state, y.value);
|
||||
}
|
||||
|
||||
return state;
|
||||
},
|
||||
}
|
||||
|
||||
toArray: function toArray() {
|
||||
toArray() {
|
||||
return [...this];
|
||||
},
|
||||
}
|
||||
|
||||
forEach: function* forEach(f) {
|
||||
forEach(f) {
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
f(y.value);
|
||||
}
|
||||
},
|
||||
/* extension: return final value from underlying iterator */
|
||||
return y.value;
|
||||
}
|
||||
|
||||
some: function some(f) {
|
||||
some(f) {
|
||||
/* extension: if f is undefined, assume identity function */
|
||||
let iter = (typeof f === 'undefined') ? this : IteratorMixin.map.call(this, f);
|
||||
if (typeof f === 'undefined') {
|
||||
f = identity;
|
||||
}
|
||||
let y;
|
||||
while (!(y = iter.next()).done) {
|
||||
if (y.value) {
|
||||
while (!(y = this.next()).done) {
|
||||
if (f(y.value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
}
|
||||
|
||||
every: function every(f) {
|
||||
every(f) {
|
||||
/* extension: if f is undefined, assume identity function */
|
||||
let iter = (typeof f === 'undefined') ? this : IteratorMixin.map.call(this, f);
|
||||
if (typeof f === 'undefined') {
|
||||
f = identity;
|
||||
}
|
||||
let y;
|
||||
while (!(y = iter.next()).done) {
|
||||
if (!y.value) {
|
||||
while (!(y = this.next()).done) {
|
||||
if (!f(y.value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
find: function find(f) {
|
||||
find(f) {
|
||||
/* extension: if f is undefined, return the first 'truthy' value */
|
||||
if (typeof f === undefined) {
|
||||
/* extension */
|
||||
f = function identity(x) { return x; };
|
||||
f = identity;
|
||||
}
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
|
|
@ -118,19 +183,17 @@ export const IteratorMixin = {
|
|||
return y.value;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/* extension */
|
||||
includes: function includes(x) {
|
||||
return IteratorMixin.some.call(this, function equalsX(y) { return y === x; });
|
||||
},
|
||||
};
|
||||
|
||||
export const Iterator = {};
|
||||
for (const fn in IteratorMixin) {
|
||||
Iterator[fn] = function() {
|
||||
return IteratorMixin[fn].call(...arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/* extension */
|
||||
includes(x) {
|
||||
return this.some(function matches(y) { return y == x; });
|
||||
}
|
||||
|
||||
/* extension */
|
||||
strictlyIncludes(x) {
|
||||
return this.some(function matches(y) { return y === x; });
|
||||
}
|
||||
};
|
||||
|
||||
export default Iterator;
|
||||
|
|
|
|||
337
js/pacosako.js
337
js/pacosako.js
|
|
@ -128,7 +128,14 @@ class Board {
|
|||
}
|
||||
|
||||
toString() {
|
||||
return new Buffer(this._board).toString('hex')
|
||||
const bufferStr = new Buffer(this._board).toString('hex');
|
||||
if (this._phantom) {
|
||||
const phantom = this._phantom;
|
||||
const phantomStr = ':' + phantom.side[0] + phantom.type + phantom.from;
|
||||
return bufferStr + phantomStr;
|
||||
} else {
|
||||
return bufferStr;
|
||||
}
|
||||
}
|
||||
|
||||
get phantom() {
|
||||
|
|
@ -302,6 +309,114 @@ const NBSP = '\u00a0'; /* non-breaking space */
|
|||
const SHY = '\u00ad' /* soft hyphen */
|
||||
const ZWSP = '\u200b'; /* zero-width space */
|
||||
|
||||
function addHistory(game) {
|
||||
const prior = game._undo;
|
||||
const move = game.lastMove;
|
||||
|
||||
if (game._history === undefined) {
|
||||
if (!move.phantom && !move.resign && move.side === LIGHT) {
|
||||
game._turn += 1;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let result = '';
|
||||
if (move.phantom) {
|
||||
result += SHY + '*';
|
||||
} else if (!move.resign) {
|
||||
if (game._turn > 0 || move.side === DARK) {
|
||||
result += ' ';
|
||||
}
|
||||
|
||||
if (move.side === LIGHT) {
|
||||
game._turn += 1;
|
||||
result += String(game._turn) + '.' + NBSP;
|
||||
}
|
||||
}
|
||||
|
||||
if (move.castle) {
|
||||
result += 'O-O';
|
||||
} else if (move.queen_castle) {
|
||||
result += 'O-O-O';
|
||||
} else if (move.side && move.type && move.from && move.to) {
|
||||
let piece = '';
|
||||
|
||||
if (move.alongside) {
|
||||
if (move.side === LIGHT) {
|
||||
piece = move.type.toUpperCase() + move.alongside.toUpperCase();
|
||||
} else {
|
||||
piece = move.alongside.toUpperCase() + move.type.toUpperCase();
|
||||
}
|
||||
} else {
|
||||
piece = move.type === PAWN ? '' : move.type.toUpperCase();
|
||||
}
|
||||
|
||||
/* the second condition below is for en passant of a joined piece */
|
||||
if (!move.phantom || move.from !== prior.lastMove.to) {
|
||||
const sameKind = prior.board.findPieces(move.side, move.type, move.alongside || EMPTY);
|
||||
const legalFrom = [];
|
||||
let sameFile = 0; /* column / letter */
|
||||
let sameRank = 0; /* row / number */
|
||||
|
||||
for (const where of sameKind) {
|
||||
if (prior.isLegalMove(move.side, where, move.to, true)) {
|
||||
legalFrom.push(where);
|
||||
|
||||
if (where[0] === move.from[0]) {
|
||||
sameFile += 1;
|
||||
}
|
||||
|
||||
if (where[1] === move.from[1]) {
|
||||
sameRank += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* always disambiguate captures by pawns (standard convention) */
|
||||
if (legalFrom.length !== 1 || (move.type === PAWN && move.took)) {
|
||||
/* append file, rank, or both to disambiguate */
|
||||
if (sameFile === 1) {
|
||||
piece += move.from[0];
|
||||
} else if (sameRank === 1) {
|
||||
piece += move.from[1];
|
||||
} else {
|
||||
piece += move.from;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const took = move.took ? 'x' : '';
|
||||
result += piece + took + move.to;
|
||||
}
|
||||
|
||||
if (move.en_passant) {
|
||||
result += 'e.p.';
|
||||
}
|
||||
|
||||
if (move.promotion) {
|
||||
result += '(' + move.promotion.toUpperCase() + ')';
|
||||
}
|
||||
|
||||
if (move.took === KING) {
|
||||
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;
|
||||
}
|
||||
|
||||
class Game {
|
||||
constructor(original) {
|
||||
if (original !== undefined) {
|
||||
|
|
@ -314,13 +429,19 @@ class Game {
|
|||
this._status = original._status;
|
||||
this._moves = JSON.parse(JSON.stringify(original._moves));
|
||||
this._redo = JSON.parse(JSON.stringify(original._redo));
|
||||
this._history = original._history;
|
||||
this._turn = original._turn;
|
||||
this._castling = JSON.parse(JSON.stringify(original._castling));
|
||||
this._undo = original._undo;
|
||||
} else {
|
||||
this._board = new Board();
|
||||
this._player = LIGHT;
|
||||
this._status = PLAYING;
|
||||
this._moves = [];
|
||||
this._redo = [];
|
||||
this._history = '';
|
||||
this._turn = 0;
|
||||
this._undo = null;
|
||||
|
||||
/* set to false when the king or rook moves */
|
||||
this._castling = {};
|
||||
|
|
@ -460,19 +581,88 @@ class Game {
|
|||
}
|
||||
|
||||
isLegalMove(side, from, to, canCapture) {
|
||||
const legals = this.legalMoves(side, from, canCapture);
|
||||
return Iterator.some(legals, (where) => where === to);
|
||||
const legals = new Iterator(this.legalMoves(side, from, canCapture));
|
||||
return legals.strictlyIncludes(to);
|
||||
}
|
||||
|
||||
isInCheck(_side) {
|
||||
if (this._status !== PLAYING || this._board.phantom) {
|
||||
/* can't be in check mid-move, or if the game is already over */
|
||||
return false;
|
||||
}
|
||||
|
||||
const side = _side || this._player;
|
||||
const other = otherSide(side);
|
||||
const kings = [...this._board.findPieces(side, KING)];
|
||||
|
||||
if (kings.length !== 1) {
|
||||
throw { message: "there should be exactly one king" };
|
||||
throw { message: 'there should be exactly one king per side' };
|
||||
}
|
||||
|
||||
const king = kings[0];
|
||||
|
||||
/* check is evaluated as if the current player passed without moving */
|
||||
const sim = new Game(this);
|
||||
sim.dropHistory();
|
||||
sim._player = other;
|
||||
|
||||
/* start with the opponent's unpaired pieces, the ones that can capture */
|
||||
let queue = [];
|
||||
for (const from of sim._board.findPieces(other, true, false)) {
|
||||
if (sim.isLegalMove(other, from, king, true)) {
|
||||
/* this piece can directly capture the king */
|
||||
return true;
|
||||
}
|
||||
|
||||
queue.push({ game: sim, from });
|
||||
}
|
||||
|
||||
/* arbitrary limit, but a human player would probably miss a 7-move chain too */
|
||||
const moveLimit = this._moves.length + 6;
|
||||
const seen = new Set();
|
||||
|
||||
while (queue.length > 0) {
|
||||
const game = queue[0].game;
|
||||
const from = queue[0].from;
|
||||
queue = queue.slice(1);
|
||||
|
||||
seen.add(game.toString());
|
||||
|
||||
/* look for another piece that can reach the king or continue the chain */
|
||||
const pairs = [...game.board.findPieces(other, true, true)];
|
||||
for (const pair of pairs) {
|
||||
if (!game.isLegalMove(other, from, pair, true)) {
|
||||
/* can't reach it */
|
||||
continue;
|
||||
}
|
||||
|
||||
const game2 = new Game(game);
|
||||
try {
|
||||
game2.move(from, pair);
|
||||
} catch (err) {
|
||||
/* internal error, but keep looking at the rest of the queue */
|
||||
console.log('isInCheck:', err);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (seen.has(game2._board.toString())) {
|
||||
/* we've already been here via another path */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (game2.isLegalMove(other, PHANTOM, king, true)) {
|
||||
/* we have our answer */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (game2._moves.length < moveLimit) {
|
||||
queue.push({ game: game2, from: PHANTOM });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* didn't find anything */
|
||||
return false;
|
||||
}
|
||||
|
||||
move(from, to, meta) {
|
||||
|
|
@ -525,6 +715,7 @@ class Game {
|
|||
move.meta = JSON.parse(JSON.stringify(meta));
|
||||
}
|
||||
|
||||
this._undo = new Game(this);
|
||||
board.move(from, to);
|
||||
|
||||
if (type === KING) {
|
||||
|
|
@ -582,12 +773,14 @@ class Game {
|
|||
this._player = otherSide(this._player);
|
||||
}
|
||||
|
||||
this._moves.push(move);
|
||||
this._redo = [];
|
||||
|
||||
if (took === KING) {
|
||||
this._status = ENDED;
|
||||
}
|
||||
|
||||
this._moves.push(move);
|
||||
this._redo = [];
|
||||
addHistory(this);
|
||||
}
|
||||
|
||||
resign(meta) {
|
||||
|
|
@ -604,9 +797,12 @@ class Game {
|
|||
move.meta = meta;
|
||||
}
|
||||
|
||||
this._status = ENDED;
|
||||
this._undo = new Game(this);
|
||||
this._moves.push(move);
|
||||
this._redo = [];
|
||||
this._status = ENDED;
|
||||
|
||||
addHistory(this);
|
||||
}
|
||||
|
||||
get lastMove() {
|
||||
|
|
@ -656,15 +852,10 @@ class Game {
|
|||
const lastMove = this._moves[this._moves.length - 1];
|
||||
const savedRedo = this._redo;
|
||||
|
||||
/* replay all moves except the last in a new game object */
|
||||
const replay = new this.constructor();
|
||||
for (let i = 0; i < this._moves.length - 1; ++i) {
|
||||
replay.replayMove(this._moves[i]);
|
||||
}
|
||||
|
||||
/* copy all the properties from the replayed game to this one */
|
||||
for (const prop in replay) {
|
||||
this[prop] = replay[prop];
|
||||
/* copy all the properties from the saved prior game to this one */
|
||||
const savedUndo = this._undo;
|
||||
for (const prop in savedUndo) {
|
||||
this[prop] = savedUndo[prop];
|
||||
}
|
||||
|
||||
/* restore the original redo history and add the undone move */
|
||||
|
|
@ -685,122 +876,28 @@ class Game {
|
|||
this._redo = [];
|
||||
}
|
||||
|
||||
countTurns() {
|
||||
let n = 0;
|
||||
let player = null;
|
||||
|
||||
for (const move of this._moves) {
|
||||
/* multiple consecutive moves by the same player are a single turn */
|
||||
if (move.side !== player) {
|
||||
++n;
|
||||
get turns() {
|
||||
return this._turn;
|
||||
}
|
||||
|
||||
player = move.side;
|
||||
get history() {
|
||||
return this._history;
|
||||
}
|
||||
|
||||
return n;
|
||||
dropHistory() {
|
||||
this._history = undefined;
|
||||
}
|
||||
|
||||
renderHistory() {
|
||||
let replay = new Game();
|
||||
let result = '';
|
||||
let n = 0;
|
||||
|
||||
if (this._history === undefined) {
|
||||
const replay = new Game();
|
||||
for (const move of this._moves) {
|
||||
if (move.phantom) {
|
||||
result += SHY + '*';
|
||||
} else if (!move.resign) {
|
||||
if (n > 0 || move.side === DARK) {
|
||||
result += ' ';
|
||||
}
|
||||
|
||||
if (move.side === LIGHT) {
|
||||
++n;
|
||||
result += String(n) + '.' + NBSP;
|
||||
}
|
||||
}
|
||||
|
||||
if (move.castle) {
|
||||
result += 'O-O';
|
||||
} else if (move.queen_castle) {
|
||||
result += 'O-O-O';
|
||||
} else if (move.side && move.type && move.from && move.to) {
|
||||
let piece = '';
|
||||
|
||||
if (move.alongside) {
|
||||
if (move.side === LIGHT) {
|
||||
piece = move.type.toUpperCase() + move.alongside.toUpperCase();
|
||||
} else {
|
||||
piece = move.alongside.toUpperCase() + move.type.toUpperCase();
|
||||
}
|
||||
} else {
|
||||
piece = move.type === PAWN ? '' : move.type.toUpperCase();
|
||||
}
|
||||
|
||||
/* the second condition below is for en passant of a joined piece */
|
||||
if (!move.phantom || move.from !== replay.lastMove.to) {
|
||||
const sameKind = replay.board.findPieces(move.side, move.type, move.alongside || EMPTY);
|
||||
const legalFrom = [];
|
||||
let sameFile = 0; /* column / letter */
|
||||
let sameRank = 0; /* row / number */
|
||||
|
||||
for (const where of sameKind) {
|
||||
if (replay.isLegalMove(move.side, where, move.to, true)) {
|
||||
legalFrom.push(where);
|
||||
|
||||
if (where[0] === move.from[0]) {
|
||||
sameFile += 1;
|
||||
}
|
||||
|
||||
if (where[1] === move.from[1]) {
|
||||
sameRank += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* always disambiguate captures by pawns (standard convention) */
|
||||
if (legalFrom.length !== 1 || (move.type === PAWN && move.took)) {
|
||||
/* append file, rank, or both to disambiguate */
|
||||
if (sameFile === 1) {
|
||||
piece += move.from[0];
|
||||
} else if (sameRank === 1) {
|
||||
piece += move.from[1];
|
||||
} else {
|
||||
piece += move.from;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const took = move.took ? 'x' : '';
|
||||
result += piece + took + move.to;
|
||||
}
|
||||
|
||||
if (move.en_passant) {
|
||||
result += 'e.p.';
|
||||
}
|
||||
|
||||
if (move.promotion) {
|
||||
result += '(' + move.promotion.toUpperCase() + ')';
|
||||
}
|
||||
|
||||
if (move.took === KING) {
|
||||
result += '#';
|
||||
}
|
||||
|
||||
replay.replayMove(move);
|
||||
}
|
||||
|
||||
let winner = replay.winner;
|
||||
|
||||
if (winner === LIGHT) {
|
||||
result += ' 1-0';
|
||||
} else if (winner === DARK) {
|
||||
result += ' 0-1';
|
||||
} else if (replay.status !== PLAYING) {
|
||||
result += ' \u00bd-\u00bd'; /* 1/2-1/2 */
|
||||
this._history = replay._history;
|
||||
}
|
||||
|
||||
return result;
|
||||
return this._history;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -144,6 +144,15 @@ $(function (){
|
|||
const from = piece.data('location');
|
||||
const legals = currentGame.legalMoves(side, from);
|
||||
for (const there of legals) {
|
||||
try {
|
||||
const preview = new PS.Game(currentGame);
|
||||
preview.dropHistory();
|
||||
preview.move(from, there);
|
||||
if (preview.isInCheck(side)) {
|
||||
continue;
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
const square = cbSquare(there);
|
||||
square.addClass('cb-legal')
|
||||
if (event === 'drag') {
|
||||
|
|
@ -336,13 +345,16 @@ $(function (){
|
|||
}
|
||||
msg += (winner === PS.LIGHT ? 'Light' : 'Dark') + ' player won!';
|
||||
} else if (playing) {
|
||||
if (game.isInCheck()) {
|
||||
msg += 'Check! ';
|
||||
}
|
||||
msg += (game.player === PS.LIGHT ? 'Light' : 'Dark') + ' player\'s turn.';
|
||||
} else {
|
||||
msg += 'Game ended in a draw.';
|
||||
}
|
||||
$('#cb_message').text(msg);
|
||||
|
||||
$('#cb_history').text(game.renderHistory());
|
||||
$('#cb_history').text(game.history || '');
|
||||
|
||||
$('#cb_nav_first').attr('disabled', !game.canUndo);
|
||||
$('#cb_nav_prev_turn').attr('disabled', !game.canUndo);
|
||||
|
|
@ -438,7 +450,7 @@ $(function (){
|
|||
const gameId = $('#cb_board').data('gameId');
|
||||
const lightName = $('#cb_light_name').val();
|
||||
const darkName = $('#cb_dark_name').val();
|
||||
const turns = currentGame.countTurns();
|
||||
const turns = currentGame.turns;
|
||||
const winner = currentGame.winner;
|
||||
const lastMove = currentGame.lastMove || {};
|
||||
const lastMeta = lastMove.meta || {};
|
||||
|
|
|
|||
Loading…
Reference in New Issue