chess-tools
Version:
A collection of tools / libraries for programming chess applications.
176 lines (172 loc) • 5.19 kB
JavaScript
"use strict";
const Uint64BE = require("int64-buffer").Uint64BE;
const EventEmitter = require('events');
const Chess = require('chess.js').Chess;
const Transform = require('stream').Transform;
const utils = require("../../utils.js");
const files = utils.board.FILES;
const { peiceTypes, Random64, RandomPiece,RandomCastle, RandomEnPassant,RandomTurn,PromotionPieces,encode_move, decode_move} = require("./encoding.js")
const PolyglotEntry = require("./entry.js");
class PolyglotStream extends Transform {
constructor() {
super({readableObjectMode : true });
}
// _flush(callback) {
// console.log("flush");
// callback();
// }
_transform(chunk, encoding, callback) {
if (this._data) {
this._data = Buffer.concat(this._data, chunk);
} else {
this._data = chunk;
}
let entries=[];
if (this._data.length >= 16) {
let i = 16;
let remainder = this._data.length % 16;
let extra_data;
if (remainder > 0) {
//take the last bit of the buffer and save it.
extra_data = Buffer.from(this._data.buffer.slice(this._data.length-remainder, this._data.length));
}
for (i = 16; i < this._data.length; i = i + 16) {
let b = this._data.buffer.slice(i-16,i);
let entry = PolyglotEntry.fromBuffer(b);
this.push(entry);
}
this._data = extra_data;
}
//this.push(entries);
callback();
}
}
class Polyglot extends EventEmitter {
constructor() {
super();
this.loaded = false;
this.entries = [];
this.stream = new PolyglotStream();
}
load_book(stream) {
this.entries = [];
this.loaded = false;
this.stream.on( "data", (entry)=>{
if (!this.entries[entry.key]) {
this.entries[entry.key] = [];
}
this.entries[entry.key].push(entry);
});
this.stream.on('finish', ()=>{
this.loaded= true;
this.emit("loaded");
});
this.stream.on('error', (error)=>{
console.log("error", error);
this.emit("error", error);
})
stream.pipe(this.stream);
}
find(fen) {
if (!this.loaded) {
throw new Error("No book is loaded")
}
let hash = this.generate_hash(fen);
return this.entries[hash];
}
generate_hash(fen) {
/**From the polyglot module **/
return hash(fen);
}
}
function xor_64uint(a, b) {
let output;
let a_view = new DataView(a.toArrayBuffer());
let b_view = new DataView(b.toArrayBuffer());
let a_32_hi = a_view.getUint32(0, false);
let a_32_lo = a_view.getUint32(4, false);
let b_32_hi = b_view.getUint32(0, false);
let b_32_lo = b_view.getUint32(4, false);
let n_hi = a_32_hi ^ b_32_hi;
let n_lo = a_32_lo ^ b_32_lo;
return Uint64BE(n_hi, n_lo);
}
function hash(fen) {
let game = new Chess(fen);
let result = game.validate_fen(fen);
if (!result.valid) {
throw result.error;
}
//Calculate piece offsets
let pieceOffsets = [];
for (let file = 0;file < 8;file++) {
for (let rank = 1;rank <= 8;rank++) {
let piece = game.get(files[file] + rank);
if (piece) {
pieceOffsets.push(64 * pieceTypes[piece.color + piece.type] + 8 * (rank - 1) + file);
}
}
}
//Calculate castling offsets
let castlingOffsets = [];
let fenTokens = game.fen().split(' ');
let castlingField = fenTokens[2];
if (castlingField.indexOf('K') != -1) {
castlingOffsets.push(0);
}
if (castlingField.indexOf('Q') != -1) {
castlingOffsets.push(1);
}
if (castlingField.indexOf('k') != -1) {
castlingOffsets.push(2);
}
if (castlingField.indexOf('q') != -1) {
castlingOffsets.push(3);
}
//Calculate enpassant offsets
let epOffset = -1;
let fenEpSquare = fenTokens[3];
if (fenEpSquare !== '-') {
fenEpSquare = fenEpSquare[0] + (game.turn() === 'w' ? '5' : '4');
let epSquareIndex = files.indexOf(fenEpSquare[0]);
if (epSquareIndex > 0) {
let leftPiece = game.get(files[epSquareIndex - 1] + fenEpSquare[1]);
if (leftPiece && leftPiece.type === 'p' &&
leftPiece.color === game.turn()) {
epOffset = epSquareIndex;
}
}
if (epSquareIndex < 7) {
let rightPiece = game.get(files[epSquareIndex + 1] + fenEpSquare[1]);
if (rightPiece && rightPiece.type === 'p' &&
rightPiece.color === game.turn()) {
epOffset = epSquareIndex;
}
}
}
let isWhitesTurn = game.turn() === 'w';
let fen_hash = Uint64BE(0);
for (let offset of pieceOffsets) {
fen_hash = xor_64uint(fen_hash, RandomPiece[offset]);
}
for (let offset of castlingOffsets) {
fen_hash = xor_64uint(fen_hash, RandomCastle[offset]);
}
if (epOffset >= 0) {
fen_hash = xor_64uint(fen_hash, RandomEnPassant[epOffset]);
}
if (isWhitesTurn) {
fen_hash = xor_64uint(fen_hash, RandomTurn[0]);
}
let output = fen_hash.toString(16);
if (output.length < 16) {
let pad = 16 - output.length;
for (let x =0; x < pad; x++ ) {
output = '0' + output;
}
}
return output;
}
Polyglot.PolyglotStream = PolyglotStream;
Polyglot.PolyglotEntry = PolyglotEntry;
module.exports = Polyglot;