switch scanPath and legalMoves to return generators instead of arrays
This commit is contained in:
parent
5f9ce09181
commit
b228f1f626
|
|
@ -0,0 +1,136 @@
|
|||
'use strict';
|
||||
|
||||
export const IteratorMixin = {
|
||||
map: function* map(f) {
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
yield f(y.value);
|
||||
}
|
||||
return y.value;
|
||||
},
|
||||
|
||||
filter: function* filter(f) {
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
if (f(y.value)) {
|
||||
yield y.value;
|
||||
}
|
||||
}
|
||||
return y.value;
|
||||
},
|
||||
|
||||
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;
|
||||
} else {
|
||||
remaining -= 1;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
asIndexedPairs: function* asIndexedPairs() {
|
||||
let index = 0;
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
yield [index++, x];
|
||||
}
|
||||
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) {
|
||||
if (typeof state === 'undefined') {
|
||||
const first = this.next();
|
||||
if (first.done) {
|
||||
throw new TypeError('reduce: empty iterator');
|
||||
}
|
||||
state = first.value;
|
||||
}
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
state = f(state, y.value);
|
||||
}
|
||||
return state;
|
||||
},
|
||||
|
||||
toArray: function toArray() {
|
||||
return [...this];
|
||||
},
|
||||
|
||||
forEach: function* forEach(f) {
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
f(y.value);
|
||||
}
|
||||
},
|
||||
|
||||
some: function some(f) {
|
||||
/* extension: if f is undefined, assume identity function */
|
||||
let iter = (typeof f === 'undefined') ? this : IteratorMixin.map.call(this, f);
|
||||
let y;
|
||||
while (!(y = iter.next()).done) {
|
||||
if (y.value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
every: function every(f) {
|
||||
/* extension: if f is undefined, assume identity function */
|
||||
let iter = (typeof f === 'undefined') ? this : IteratorMixin.map.call(this, f);
|
||||
let y;
|
||||
while (!(y = iter.next()).done) {
|
||||
if (!y.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
find: function find(f) {
|
||||
if (typeof f === undefined) {
|
||||
/* extension */
|
||||
f = function identity(x) { return x; };
|
||||
}
|
||||
let y;
|
||||
while (!(y = this.next()).done) {
|
||||
if (f(y.value)) {
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
export default Iterator;
|
||||
141
js/pacosako.js
141
js/pacosako.js
|
|
@ -1,15 +1,6 @@
|
|||
'use strict';
|
||||
(function(factory) {
|
||||
if (typeof define !== 'undefined' && define.amd) {
|
||||
define([], factory);
|
||||
} else if (typeof define !== 'undefined' && define.petal) {
|
||||
define(['paco-sako'], [], factory);
|
||||
} else if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = factory();
|
||||
} else {
|
||||
window.PacoSako = factory();
|
||||
}
|
||||
})(function(){
|
||||
|
||||
import {Iterator} from './iterator.js';
|
||||
|
||||
/* Game states */
|
||||
const PLAYING = 'playing';
|
||||
|
|
@ -136,6 +127,10 @@ class Board {
|
|||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
return new Buffer(this._board).toString('hex')
|
||||
}
|
||||
|
||||
get phantom() {
|
||||
let phantom = this._phantom;
|
||||
if (phantom) {
|
||||
|
|
@ -236,7 +231,7 @@ class Board {
|
|||
return ((theirs === EMPTY) ? (ours === EMPTY) : (canCapture ? true : false));
|
||||
}
|
||||
|
||||
scanPath(accum, side, from, canCapture, columnsRight, rowsUp, remainder) {
|
||||
*scanPath(side, from, canCapture, columnsRight, rowsUp, remainder) {
|
||||
while (true) {
|
||||
let there = offsetSquare(from, columnsRight, rowsUp);
|
||||
|
||||
|
|
@ -244,7 +239,7 @@ class Board {
|
|||
return;
|
||||
}
|
||||
|
||||
accum.push(there);
|
||||
yield there;
|
||||
|
||||
if (remainder < 1 || this.getPiece(otherSide(side), there) !== EMPTY) {
|
||||
return;
|
||||
|
|
@ -255,26 +250,41 @@ class Board {
|
|||
}
|
||||
}
|
||||
|
||||
findPieces(side, type, alongside) {
|
||||
*findPieces(side, type, alongside) {
|
||||
const other = otherSide(side);
|
||||
let pieces = [];
|
||||
|
||||
if (this._phantom && this._phantom.side === side && this._phantom.type === type) {
|
||||
pieces.push(this._phantom.from);
|
||||
function testFunction(getPiece, match) {
|
||||
if (match === undefined) {
|
||||
return function(here) { return true; }
|
||||
} else if (match === false) {
|
||||
return function(here) { return getPiece(here) === EMPTY; };
|
||||
} else if (match === true) {
|
||||
return function(here) { return getPiece(here) !== EMPTY; };
|
||||
} else {
|
||||
return function(here) { return getPiece(here) === match; };
|
||||
}
|
||||
}
|
||||
|
||||
const testSide = testFunction(this.getPiece.bind(this, side), type);
|
||||
const testOther = testFunction(this.getPiece.bind(this, other), alongside);
|
||||
const test = function(here) { return testSide(here) && testOther(here); };
|
||||
|
||||
if (this._phantom && this._phantom.side === side) {
|
||||
const getPhantom = (here) => this._phantom.type;
|
||||
const testPhantom = testFunction(getPhantom, type);
|
||||
if (testPhantom(PHANTOM)) {
|
||||
yield this._phantom.from;
|
||||
}
|
||||
}
|
||||
|
||||
for (const row of ROWS) {
|
||||
for (const column of COLUMNS) {
|
||||
const here = column + row;
|
||||
if (this.getPiece(side, here) === type) {
|
||||
if (!alongside || this.getPiece(other, here) === alongside) {
|
||||
pieces.push(here);
|
||||
}
|
||||
if (test(here)) {
|
||||
yield here;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pieces;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -347,12 +357,12 @@ class Game {
|
|||
}
|
||||
}
|
||||
|
||||
legalMoves(side, from, canCapture) {
|
||||
*legalMoves(side, from, canCapture) {
|
||||
const board = this._board;
|
||||
const type = board.getPiece(side, from);
|
||||
|
||||
if (this._status !== PLAYING || type === EMPTY) {
|
||||
return [];
|
||||
return;
|
||||
}
|
||||
|
||||
if (canCapture === undefined) {
|
||||
|
|
@ -364,11 +374,9 @@ class Game {
|
|||
from = board.phantom.from;
|
||||
}
|
||||
|
||||
let legals = [];
|
||||
|
||||
if (type === KING) {
|
||||
for (const dir of ANY_DIR) {
|
||||
board.scanPath(legals, side, from, false, dir[0], dir[1], 0);
|
||||
yield* board.scanPath(side, from, false, dir[0], dir[1], 0);
|
||||
}
|
||||
|
||||
/* check for castling conditions */
|
||||
|
|
@ -378,34 +386,34 @@ class Game {
|
|||
if (this._castling[side].king &&
|
||||
board.isEmpty('f' + row) &&
|
||||
board.isEmpty('g' + row)) {
|
||||
legals.push('g' + row);
|
||||
yield 'g' + row;
|
||||
}
|
||||
|
||||
if (this._castling[side].queen &&
|
||||
board.isEmpty('d' + row) &&
|
||||
board.isEmpty('c' + row) &&
|
||||
board.isEmpty('b' + row)) {
|
||||
legals.push('c' + row);
|
||||
yield 'c' + row;
|
||||
}
|
||||
}
|
||||
} else if (type === QUEEN) {
|
||||
for (const dir of ANY_DIR) {
|
||||
board.scanPath(legals, side, from, canCapture, dir[0], dir[1], 8);
|
||||
yield* board.scanPath(side, from, canCapture, dir[0], dir[1], 8);
|
||||
}
|
||||
} else if (type === BISHOP) {
|
||||
for (const dir of DIAG) {
|
||||
board.scanPath(legals, side, from, canCapture, dir[0], dir[1], 8);
|
||||
yield* board.scanPath(side, from, canCapture, dir[0], dir[1], 8);
|
||||
}
|
||||
} else if (type === KNIGHT) {
|
||||
for (const dir of KNIGHT_DIR) {
|
||||
const there = offsetSquare(from, dir[0], dir[1]);
|
||||
if (there && board.validDestination(side, there, canCapture)) {
|
||||
legals.push(there);
|
||||
yield there;
|
||||
}
|
||||
}
|
||||
} else if (type === ROOK) {
|
||||
for (const dir of ORTHO) {
|
||||
board.scanPath(legals, side, from, canCapture, dir[0], dir[1], 8);
|
||||
yield* board.scanPath(side, from, canCapture, dir[0], dir[1], 8);
|
||||
}
|
||||
} else if (type === PAWN) {
|
||||
const dark = side === DARK;
|
||||
|
|
@ -415,10 +423,10 @@ class Game {
|
|||
const diagR = offsetSquare(from, 1, dark ? -1 : 1);
|
||||
|
||||
if (forward && board.validDestination(side, forward, false)) {
|
||||
legals.push(forward);
|
||||
yield forward;
|
||||
if (dark ? (from[1] >= '7') : (from[1] <= '2')) {
|
||||
if (forward2 && board.validDestination(side, forward2, false)) {
|
||||
legals.push(forward2);
|
||||
yield forward2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -427,7 +435,7 @@ class Game {
|
|||
for (const there of [diagL, diagR]) {
|
||||
if (there) {
|
||||
if (board.getPiece(otherSide(side), there) !== EMPTY) {
|
||||
legals.push(there);
|
||||
yield there;
|
||||
} else if (forward2) {
|
||||
let lastMove = null;
|
||||
for (let i = this._moves.length; i > 0; --i) {
|
||||
|
|
@ -442,15 +450,29 @@ class Game {
|
|||
lastMove.from === there[0] + forward2[1] &&
|
||||
lastMove.to === there[0] + from[1]) {
|
||||
/* en passant */
|
||||
legals.push(there);
|
||||
yield there;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return legals;
|
||||
isLegalMove(side, from, to, canCapture) {
|
||||
const legals = this.legalMoves(side, from, canCapture);
|
||||
return Iterator.some(legals, (where) => where === to);
|
||||
}
|
||||
|
||||
isInCheck(_side) {
|
||||
const side = _side || this._player;
|
||||
const kings = [...this._board.findPieces(side, KING)];
|
||||
|
||||
if (kings.length !== 1) {
|
||||
throw { message: "there should be exactly one king" };
|
||||
}
|
||||
|
||||
const king = kings[0];
|
||||
}
|
||||
|
||||
move(from, to, meta) {
|
||||
|
|
@ -472,8 +494,7 @@ class Game {
|
|||
|
||||
const fromSquare = (from === PHANTOM) ? board.phantom.from : from;
|
||||
|
||||
const legals = this.legalMoves(side, from, alongside === EMPTY);
|
||||
if (!legals.includes(to)) {
|
||||
if (!this.isLegalMove(side, from, to, alongside === EMPTY)) {
|
||||
throw { message: "illegal move", side: side, from: fromSquare, to: to };
|
||||
}
|
||||
|
||||
|
|
@ -724,7 +745,7 @@ class Game {
|
|||
let sameRank = 0; /* row / number */
|
||||
|
||||
for (const where of sameKind) {
|
||||
if (replay.legalMoves(move.side, where, true).includes(move.to)) {
|
||||
if (replay.isLegalMove(move.side, where, move.to, true)) {
|
||||
legalFrom.push(where);
|
||||
|
||||
if (where[0] === move.from[0]) {
|
||||
|
|
@ -783,34 +804,24 @@ class Game {
|
|||
}
|
||||
}
|
||||
|
||||
return {
|
||||
Board: Board,
|
||||
Game: Game,
|
||||
export default {
|
||||
/* Classes */
|
||||
Board, Game,
|
||||
|
||||
PLAYING: PLAYING,
|
||||
ENDED: ENDED,
|
||||
/* Game States */
|
||||
PLAYING, ENDED,
|
||||
|
||||
LIGHT: LIGHT,
|
||||
DARK: DARK,
|
||||
/* Sides */
|
||||
LIGHT, DARK,
|
||||
|
||||
KING: KING,
|
||||
QUEEN: QUEEN,
|
||||
ROOK: ROOK,
|
||||
KNIGHT: KNIGHT,
|
||||
BISHOP: BISHOP,
|
||||
PAWN: PAWN,
|
||||
EMPTY: EMPTY,
|
||||
/* Pieces */
|
||||
KING, QUEEN, ROOK, KNIGHT, BISHOP, PAWN, EMPTY,
|
||||
|
||||
ROWS: ROWS,
|
||||
COLUMNS: COLUMNS,
|
||||
PHANTOM: PHANTOM,
|
||||
/* Coordinates */
|
||||
ROWS, COLUMNS, PHANTOM,
|
||||
|
||||
Util: {
|
||||
otherSide: otherSide,
|
||||
offsetSquare: offsetSquare,
|
||||
},
|
||||
/* Miscellaneous */
|
||||
Util: { otherSide, offsetSquare },
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
/* vim:set expandtab sw=3 ts=8: */
|
||||
|
|
|
|||
Loading…
Reference in New Issue