This is a chess implementation in javascript and a fork of https://github.com/kbjorklu/chess
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

504 lines
14 KiB

"use strict";
/**
* Chess.Bitboard is an unsigned 64 bit integer, each bit representing a boolean value on the corresponding chessboard square.
* The boolean values represent existence of a piece on the square.
* The 64 bit unsigned integer is implemented as combination of two 32 bit unsigned integers.
* @constructor
* @param {number} low Lower 32 bits of the 64 bit value
* @param {number} high Upper 32 bits of the 64 bit value
* TODO: test using three numbers here instead of two: 31 bit integers are faster than 32 bit ones in chrome (https://v8-io12.appspot.com/#35)
*/
Chess.Bitboard = function(low, high) {
/**
* Lower 32 bits of the 64 bit value
* @type {number}
*/
this.low = low >>> 0;
/**
* Upper 32 bits of the 64 bit value
* @type {number}
*/
this.high = high >>> 0;
};
/**
* @see http://goo.gl/pyzBq (Bit Twiddling Hacks)
* @see http://goo.gl/dnqDn (Bit-peeking bits of Javascript)
* @param {number} v 32 bit integer
* @return {number} 0-32 number of bits set in v
*/
Chess.Bitboard.popcnt32 = function(v) {
v >>>= 0;
v -= (v >>> 1) & 0x55555555;
v = (v & 0x33333333) + ((v >>> 2) & 0x33333333);
return ((v + (v >>> 4) & 0xF0F0F0F) * 0x1010101) >>> 24;
};
/**
* @param {number} v 32 bit integer
* @return {number} v with its lowest bit cleared
*/
Chess.Bitboard.popLowestBit32 = function (v) {
v >>>= 0;
return (v & (v - 1)) >>> 0;
};
/**
* @param {number} v 32 bit integer, non-zero. Undefined behavior if v is zero.
* @return {number} 0-31 Position of first set bit
*/
Chess.Bitboard.getLowestBitPosition32 = function(v) {
v >>>= 0;
return Chess.Bitboard.popcnt32((v & -v) - 1);
};
/** @return {number} 0-64 number of bits set in this Chess.Bitboard */
Chess.Bitboard.prototype.popcnt = function() {
return Chess.Bitboard.popcnt32(this.low) + Chess.Bitboard.popcnt32(this.high);
};
/**
* Clears the lowest set bit.
* @return {!Chess.Bitboard} this with the lowest bit cleared
*/
Chess.Bitboard.prototype.popLowestBit = function() {
if (this.low) {
this.low = Chess.Bitboard.popLowestBit32(this.low);
} else {
this.high = Chess.Bitboard.popLowestBit32(this.high);
}
return this;
};
/** @return {number} 0-63 position of the first set bit. Undefined behavior if this Chess.Bitboard is empty. */
Chess.Bitboard.prototype.getLowestBitPosition = function() {
if (this.low) {
return Chess.Bitboard.getLowestBitPosition32(this.low);
}
return 32 + Chess.Bitboard.getLowestBitPosition32(this.high);
};
/**
* Clears the lowest set bit and returns its position.
* @return {number} 0-63 position of the first set bit. Undefined behavior if this Chess.Bitboard is empty.
*/
Chess.Bitboard.prototype.extractLowestBitPosition = function() {
var index = this.getLowestBitPosition();
this.popLowestBit();
return index;
};
/** @return {boolean} true if all the bits in this Chess.Bitboard are zero */
Chess.Bitboard.prototype.isEmpty = function() {
return !this.low && !this.high;
};
/**
* @param {number} index 0-63
* @return {boolean} true if the bit at index is 0
*/
Chess.Bitboard.prototype.isClear = function(index) {
index >>>= 0;
if (index < 32) {
return !(this.low & (1 << index));
}
return !(this.high & (1 << (index - 32)));
};
/**
* @param {number} index 0-63
* @return {boolean} true if the bit at index is 1
*/
Chess.Bitboard.prototype.isSet = function(index) {
return !this.isClear(index);
};
/**
* @param {number} index 0-63
* @return {!Chess.Bitboard} this or 1 << index
*/
Chess.Bitboard.prototype.setBit = function(index) {
index >>>= 0;
if (index < 32) {
this.low = (this.low | (1 << index)) >>> 0;
} else {
this.high = (this.high | (1 << (index - 32))) >>> 0;
}
return this;
};
/**
* @param {number} index 0-63
* @return {!Chess.Bitboard} this and not 1 << index
*/
Chess.Bitboard.prototype.clearBit = function(index) {
index >>>= 0;
if (index < 32) {
this.low = (this.low & ~(1 << index)) >>> 0;
} else {
this.high = (this.high & ~(1 << (index - 32))) >>> 0;
}
return this;
};
/**
* @param {!Chess.Bitboard} other
* @return {!Chess.Bitboard} this and other
*/
Chess.Bitboard.prototype.and = function(other) {
this.low = (this.low & other.low) >>> 0;
this.high = (this.high & other.high) >>> 0;
return this;
};
/**
* @param {!Chess.Bitboard} other
* @return {!Chess.Bitboard} this and not other
*/
Chess.Bitboard.prototype.and_not = function(other) {
this.low = (this.low & ~other.low) >>> 0;
this.high = (this.high & ~other.high) >>> 0;
return this;
};
/**
* @param {!Chess.Bitboard} other
* @return {!Chess.Bitboard} this or other
*/
Chess.Bitboard.prototype.or = function(other) {
this.low = (this.low | other.low) >>> 0;
this.high = (this.high | other.high) >>> 0;
return this;
};
/**
* @param {!Chess.Bitboard} other
* @return {!Chess.Bitboard} this xor other
*/
Chess.Bitboard.prototype.xor = function(other) {
this.low = (this.low ^ other.low) >>> 0;
this.high = (this.high ^ other.high) >>> 0;
return this;
};
/** @return {!Chess.Bitboard} not this */
Chess.Bitboard.prototype.not = function() {
this.low = (~this.low) >>> 0;
this.high = (~this.high) >>> 0;
return this;
};
/**
* Shifts this Chess.Bitboard left v bits. Undefined behavior if v is not in 0-63.
* @param {number} v 0-63 number of bits to shift
* @return {!Chess.Bitboard} this << v
*/
Chess.Bitboard.prototype.shl = function(v) {
v >>>= 0;
if (v > 31) {
this.high = (this.low << (v - 32)) >>> 0;
this.low = 0 >>> 0;
} else if (v > 0) {
this.high = ((this.high << v) | (this.low >>> (32 - v))) >>> 0;
this.low = (this.low << v) >>> 0;
}
return this;
};
/**
* Shifts this Chess.Bitboard right v bits. Undefined behavior if v is not in 0-63.
* @param {number} v 0-63 number of bits to shift
* @return {!Chess.Bitboard} this >>> v
*/
Chess.Bitboard.prototype.shr = function(v) {
v >>>= 0;
if (v > 31) {
this.low = this.high >>> (v - 32);
this.high = 0 >>> 0;
} else if (v > 0) {
this.low = ((this.low >>> v) | (this.high << (32 - v))) >>> 0;
this.high >>>= v;
}
return this;
};
/**
* Shifts this Chess.Bitboard left v bits, where v can be negative for right shift.
* @param {number} v number of bits to shift
* @return {!Chess.Bitboard} this << v
*/
Chess.Bitboard.prototype.shiftLeft = function(v) {
if (v > 63 || v < -63) {
this.low = this.high = 0 >>> 0;
} else if (v > 0) {
this.shl(v);
} else if (v < 0) {
this.shr(-v);
}
return this;
};
/**
* @param {!Chess.Bitboard} other
* @return {boolean} 'this' equals 'other'
*/
Chess.Bitboard.prototype.isEqual = function(other) {
return this.low === other.low && this.high === other.high;
};
/** @return {!Chess.Bitboard} copy of this */
Chess.Bitboard.prototype.dup = function() {
return Chess.Bitboard.make(this.low, this.high);
};
/**
* @param {number} low Lower 32 bits of the 64 bit value
* @param {number} high Upper 32 bits of the 64 bit value
* @return {!Chess.Bitboard}
*/
Chess.Bitboard.make = function(low, high) {
return new Chess.Bitboard(low, high);
};
/** @return {!Chess.Bitboard} bitboard of all zeros */
Chess.Bitboard.makeZero = function() {
return Chess.Bitboard.make(0, 0);
};
/** @return {!Chess.Bitboard} bitboard of all ones */
Chess.Bitboard.makeOne = function() {
return Chess.Bitboard.make(0xFFFFFFFF, 0xFFFFFFFF);
};
/** @return {!Chess.Bitboard} bitboard of ones in light (white) squares, zeros in dark (black) squares */
Chess.Bitboard.makeLightSquares = function() {
return Chess.Bitboard.make(0x55AA55AA, 0x55AA55AA);
};
/** @return {!Chess.Bitboard} bitboard of ones in dark squares, zeros in light squares */
Chess.Bitboard.makeDarkSquares = function() {
return Chess.Bitboard.make(0xAA55AA55, 0xAA55AA55);
};
/**
* @param {number} file
* @return {!Chess.Bitboard} bitboard of ones in file, zeros elsewhere
*/
Chess.Bitboard.makeFile = function(file) {
return Chess.Bitboard.make(0x01010101, 0x01010101).shl(file);
};
/** @return {!Array.<!Chess.Bitboard>} bitboard for each file */
Chess.Bitboard.makeFiles = function() {
var b = [];
for (var i = 0; i < 8; ++i) {
b.push(Chess.Bitboard.makeFile(i));
}
return b;
};
/**
* @param {number} rank
* @return {!Chess.Bitboard} bitboard of ones in rank, zeros elsewhere
*/
Chess.Bitboard.makeRank = function(rank) {
return Chess.Bitboard.make(0xFF, 0).shl(rank * 8);
};
/** @return {!Array.<!Chess.Bitboard>} bitboard for each rank */
Chess.Bitboard.makeRanks = function() {
var b = [];
for (var i = 0; i < 8; ++i) {
b.push(Chess.Bitboard.makeRank(i));
}
return b;
};
/**
* @param {number} index 0-63
* @return {!Chess.Bitboard} bitboard of 1 at index, zero elsewhere
*/
Chess.Bitboard.makeIndex = function(index) {
return Chess.Bitboard.makeZero().setBit(index);
};
/** @return {!Array.<!Chess.Bitboard>} bitboard for each index */
Chess.Bitboard.makeIndices = function() {
var b = [];
for (var i = 0; i < 64; ++i) {
b.push(Chess.Bitboard.makeIndex(i));
}
return b;
};
/**
* 0 diagonal is the main diagonal, positive numbers are superdiagonals, negative numbers subdiagonals.
* @param {number} diagonal (-7)-7
* @return {!Chess.Bitboard} bitboard with ones on diagonal, zeros elsewhere
*/
Chess.Bitboard.makeDiagonal = function(diagonal) {
return Chess.Bitboard.make(0x10204080, 0x01020408).and(Chess.Bitboard.makeOne().shiftLeft(diagonal * 8)).shiftLeft(diagonal);
};
/** @return {!Array.<!Chess.Bitboard>} bitboard for each diagonal */
Chess.Bitboard.makeDiagonals = function() {
var b = [];
for (var i = -7; i < 8; ++i) {
b.push(Chess.Bitboard.makeDiagonal(i));
}
return b;
};
/**
* 0 diagonal is the main antidiagonal, positive numbers are subantidiagonals (below the main antidiagonal on the chessboard), negative numbers superantidiagonals.
* @param {number} antidiagonal (-7)-7
* @return {!Chess.Bitboard} bitboard with ones on antidiagonal, zeros elsewhere
*/
Chess.Bitboard.makeAntidiagonal = function(antidiagonal) {
return Chess.Bitboard.make(0x08040201, 0x80402010).and(Chess.Bitboard.makeOne().shiftLeft(-antidiagonal * 8)).shiftLeft(antidiagonal);
};
/** @return {!Array.<!Chess.Bitboard>} bitboard for each antidiagonal */
Chess.Bitboard.makeAntidiagonals = function() {
var b = [];
for (var i = -7; i < 8; ++i) {
b.push(Chess.Bitboard.makeAntidiagonal(i));
}
return b;
};
/**
* @see http://goo.gl/MRA5s (Knight Pattern)
* @param {number} index 0-63 chessboard square of the knight
* @return {!Chess.Bitboard} knight target squares
*/
Chess.Bitboard.makeKnightMovement = function(index) {
var b = Chess.Bitboard.makeZero().setBit(index);
var l1 = b.dup().shr(1).and_not(Chess.Bitboard.FILES[7]);
var l2 = b.dup().shr(2).and_not(Chess.Bitboard.FILES[7]).and_not(Chess.Bitboard.FILES[6]);
var r1 = b.dup().shl(1).and_not(Chess.Bitboard.FILES[0]);
var r2 = b.dup().shl(2).and_not(Chess.Bitboard.FILES[0]).and_not(Chess.Bitboard.FILES[1]);
var v1 = l2.or(r2);
var v2 = l1.or(r1);
return v1.dup().shl(8).or(v1.shr(8)).or(v2.dup().shl(16)).or(v2.shr(16));
};
/** @return {!Array.<!Chess.Bitboard>} bitboard for knight movement from each square */
Chess.Bitboard.makeKnightMovements = function() {
var b = [];
for (var i = 0; i < 64; ++i) {
b.push(Chess.Bitboard.makeKnightMovement(i));
}
return b;
};
/**
* @param {number} index 0-63 chessboard square of the king
* @return {!Chess.Bitboard} king target squares
*/
Chess.Bitboard.makeKingMovement = function(index) {
var b = Chess.Bitboard.makeZero().setBit(index);
var c = b.dup().shr(1).and_not(Chess.Bitboard.FILES[7]).or(b.dup().shl(1).and_not(Chess.Bitboard.FILES[0]));
var u = b.dup().or(c).shr(8);
var d = b.dup().or(c).shl(8);
return c.or(u).or(d);
};
/** @return {!Array.<!Chess.Bitboard>} bitboard for king movement from each square */
Chess.Bitboard.makeKingMovements = function() {
var b = [];
for (var i = 0; i < 64; ++i) {
b.push(Chess.Bitboard.makeKingMovement(i));
}
return b;
};
/**
* Chess.Bitboard of all zeros
* @const
* @type {!Chess.Bitboard}
*/
Chess.Bitboard.ZERO = Chess.Bitboard.makeZero();
/**
* Chess.Bitboard of all ones
* @const
* @type {!Chess.Bitboard}
*/
Chess.Bitboard.ONE = Chess.Bitboard.makeOne();
/**
* Chess.Bitboard of ones in light squares, zeros in dark squares
* @const
* @type {!Chess.Bitboard}
*/
Chess.Bitboard.LIGHT_SQUARES = Chess.Bitboard.makeLightSquares();
/**
* Chess.Bitboard of ones in dark squares, zeros in light squares
* @const
* @type {!Chess.Bitboard}
*/
Chess.Bitboard.DARK_SQUARES = Chess.Bitboard.makeDarkSquares();
/**
* Chess.Bitboards ones in corresponding file, zeros elsewhere
* @const
* @type {!Array.<!Chess.Bitboard>}
*/
Chess.Bitboard.FILES = Chess.Bitboard.makeFiles();
/**
* Chess.Bitboards ones in corresponding rank, zeros elsewhere
* @const
* @type {!Array.<!Chess.Bitboard>}
*/
Chess.Bitboard.RANKS = Chess.Bitboard.makeRanks();
/**
* Chess.Bitboards ones in corresponding diagonal, zeros elsewhere. Chess.Bitboard.DIAGONALS[7] = main diagonal, 0-6 = subdiagonals, 8-15 = superdiagonals
* @const
* @type {!Array.<!Chess.Bitboard>}
*/
Chess.Bitboard.DIAGONALS = Chess.Bitboard.makeDiagonals();
/**
* Chess.Bitboards ones in corresponding antidiagonal, zeros elsewhere. Chess.Bitboard.ANTIDIAGONALS[7] = main antidiagonal, 0-6 = superantidiagonals, 8-15 = subantidiagonals
* @const
* @type {!Array.<!Chess.Bitboard>}
*/
Chess.Bitboard.ANTIDIAGONALS = Chess.Bitboard.makeAntidiagonals();
/**
* 64 bitboards, one per chessboard square, for positions where knights can move from the corresponding square.
* @const
* @type {!Array.<!Chess.Bitboard>}
*/
Chess.Bitboard.KNIGHT_MOVEMENTS = Chess.Bitboard.makeKnightMovements();
/**
* 64 bitboards, one per chessboard square, for positions where kings can move from the corresponding square.
* @const
* @type {!Array.<!Chess.Bitboard>}
*/
Chess.Bitboard.KING_MOVEMENTS = Chess.Bitboard.makeKingMovements();