public class Board {
	public static final int MAX_MOVES = 1024;
	public static final String FEN_START_POSITION = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
	public static final String CHESS960_START_POSITIONS[] = {"QNNRKR", "NQNRKR", "NNQRKR", "NNRQKR", "NNRKQR", "NNRKRQ", "QNRNKR", "NQRNKR", "NRQNKR", "NRNQKR", "NRNKQR", "NRNKRQ", "QNRKNR", "NQRKNR", "NRQKNR", "NRKQNR", "NRKNQR", "NRKNRQ", "QNRKRN", "NQRKRN", "NRQKRN", "NRKQRN", "NRKRQN", "NRKRNQ", "QRNNKR", "RQNNKR", "RNQNKR", "RNNQKR", "RNNKQR", "RNNKRQ", "QRNKNR", "RQNKNR", "RNQKNR", "RNKQNR", "RNKNQR", "RNKNRQ", "QRNKRN", "RQNKRN", "RNQKRN", "RNKQRN", "RNKRQN", "RNKRNQ", "QRKNNR", "RQKNNR", "RKQNNR", "RKNQNR", "RKNNQR", "RKNNRQ", "QRKNRN", "RQKNRN", "RKQNRN", "RKNQRN", "RKNRQN", "RKNRNQ", "QRKRNN", "RQKRNN", "RKQRNN", "RKRQNN", "RKRNQN", "RKRNNQ"};
	public static final String CHESS960_START_POSITIONS_BISHOPS[] = {"BB------", "B--B----", "B----B--", "B------B", "-BB-----", "--BB----", "--B--B--", "--B----B", "-B--B---", "---BB---", "----BB--", "----B--B", "-B----B-", "---B--B-", "-----BB-", "------BB"};

	// Flags: must be changed only when moving
	private static final long FLAG_TURN = 0x0001L;
	private static final long FLAG_WHITE_KINGSIDE_CASTLING = 0x0002L;
	private static final long FLAG_WHITE_QUEENSIDE_CASTLING = 0x0004L;
	private static final long FLAG_BLACK_KINGSIDE_CASTLING = 0x0008L;
	private static final long FLAG_BLACK_QUEENSIDE_CASTLING = 0x0010L;
	private static final long FLAG_CHECK = 0x0020L;
	// Position on boarch in which is captured
	private static final long FLAGS_PASSANT = 0x0000ff0000ff0000L;

	// For the castlings {White Kingside, White Queenside, Black Kingside, Black Queenside}
	public static final int CASTLING_KING_DESTINY_INDEX[] = {1, 5, 57, 61};
	public static final long CASTLING_KING_DESTINY_SQUARE[] = {1L << 1, 1L << 5, 1L << 57, 1L << 61};
	public static final int CASTLING_ROOK_DESTINY_INDEX[] = {2, 4, 58, 60};
	public static final long CASTLING_ROOK_DESTINY_SQUARE[] = {1L << 2, 1L << 4, 1L << 58, 1L << 60};

	// For the SEE SWAP algorithm
	public static final int[] SEE_PIECE_VALUES = {0, 100, 325, 330, 500, 900, 9999};

	LegalMoveGenerator legalMoveGenerator = new LegalMoveGenerator();
	int[] legalMoves = new int[256];
	int legalMoveCount = -1; // if -1 then legal moves not generated
	long[] legalMovesKey = {0, 0};
	public HashMap<Integer, String> movesSan;

	// Bitboard arrays
	public long whites = 0;
	public long blacks = 0;
	public long pawns = 0;
	public long rooks = 0;
	public long queens = 0;
	public long bishops = 0;
	public long knights = 0;
	public long kings = 0;
	public long flags = 0;

	public int fiftyMovesRule = 0;
	public int initialMoveNumber = 0;
	public int moveNumber = 0;
	public int outBookMove = Integer.MAX_VALUE;
	public long[] key = {0, 0};

	public String initialFen;

	// History array indexed by moveNumber
	public long[][] keyHistory; // to detect draw by treefold
	public int[] moveHistory;
	public long[] whitesHistory;
	public long[] blacksHistory;
	public long[] pawnsHistory;
	public long[] rooksHistory;
	public long[] queensHistory;
	public long[] bishopsHistory;
	public long[] knightsHistory;
	public long[] kingsHistory;
	public long[] flagsHistory;
	public int[] fiftyMovesRuleHistory;
	public int[] seeGain;

	// Origin squares for the castling rook {White Kingside, White Queenside, Black Kingside, Black Queenside}
	public long castlingRooks[] = {0, 0, 0, 0};

	public boolean chess960; // basically decides the destiny square of the castlings

	BitboardAttacks bbAttacks;

	public Board() {
		whitesHistory = new long[MAX_MOVES];
		blacksHistory = new long[MAX_MOVES];
		pawnsHistory = new long[MAX_MOVES];
		knightsHistory = new long[MAX_MOVES];
		bishopsHistory = new long[MAX_MOVES];
		rooksHistory = new long[MAX_MOVES];
		queensHistory = new long[MAX_MOVES];
		kingsHistory = new long[MAX_MOVES];
		flagsHistory = new long[MAX_MOVES];
		keyHistory = new long[MAX_MOVES][2];
		fiftyMovesRuleHistory = new int[MAX_MOVES];

		seeGain = new int[32];

		moveHistory = new int[MAX_MOVES];

		movesSan = new HashMap<>();

		bbAttacks = BitboardAttacks.getInstance();
	}

	/**
	 * It also computes the zobrist key
	 */
	public void startPosition() {
		setFen(FEN_START_POSITION);
	}

	/**
	 * Set a Chess960 start position
	 * http://en.wikipedia.org/wiki/Chess960_numbering_scheme
	 */
	public void startPosition(int chess960Position) {
		String base = CHESS960_START_POSITIONS_BISHOPS[chess960Position & 0x0f];
		String otherPieces = CHESS960_START_POSITIONS[chess960Position >>> 4];
		StringBuilder oSB = new StringBuilder();
		int j = 0;
		for (int i = 0; i < 8; i++) {
			if (base.charAt(i) == '-') {
				oSB.append(otherPieces.charAt(j));
				j++;
			} else {
				oSB.append('B');
			}
		}

		String fen = oSB.toString().toLowerCase() + "/pppppppp/8/8/8/8/PPPPPPPP/" + oSB.toString() + " w KQkq - 0 1";
		setFen(fen);
		chess960 = true;
	}

	public long getKey() {
		return key[0] ^ key[1];
	}

	public long getExclusionKey() {
		return key[0] ^ key[1] ^ ZobristKey.exclusionKey;
	}

	/**
	 * An alternative key to avoid collisions in the TT
	 */
	public long getKey2() {
		return key[0] ^ ~key[1];
	}

	public int getMoveNumber() {
		return moveNumber;
	}

	/**
	 * @return true if white moves
	 */
	public final boolean getTurn() {
		return (flags & FLAG_TURN) == 0;
	}

	public boolean canCastle(int color) {
		return (flags & ((flags & FLAG_TURN) == 0 ?
				FLAG_WHITE_KINGSIDE_CASTLING | FLAG_WHITE_QUEENSIDE_CASTLING :
				FLAG_BLACK_KINGSIDE_CASTLING | FLAG_BLACK_QUEENSIDE_CASTLING)) != 0;
	}

	public boolean getWhiteKingsideCastling() {
		return (flags & FLAG_WHITE_KINGSIDE_CASTLING) != 0;
	}

	public boolean getWhiteQueensideCastling() {
		return (flags & FLAG_WHITE_QUEENSIDE_CASTLING) != 0;
	}

	public boolean getBlackKingsideCastling() {
		return (flags & FLAG_BLACK_KINGSIDE_CASTLING) != 0;
	}

	public boolean getBlackQueensideCastling() {
		return (flags & FLAG_BLACK_QUEENSIDE_CASTLING) != 0;
	}

	public long getPassantSquare() {
		return flags & FLAGS_PASSANT;
	}

	public boolean getCheck() {
		return (flags & FLAG_CHECK) != 0;
	}

	public long getAll() {
		return whites | blacks;
	}

	public long getMines() {
		return (flags & FLAG_TURN) == 0 ? whites : blacks;
	}

	public long getOthers() {
		return (flags & FLAG_TURN) == 0 ? blacks : whites;
	}

	public int getPieceIntAt(long square) {
		return ((pawns & square) != 0 ? Piece.PAWN : //
				((knights & square) != 0 ? Piece.KNIGHT : //
						((bishops & square) != 0 ? Piece.BISHOP : //
								((rooks & square) != 0 ? Piece.ROOK : //
										((queens & square) != 0 ? Piece.QUEEN : //
												((kings & square) != 0 ? Piece.KING : '.'))))));
	}

	public char getPieceAt(long square) {
		char p = ((pawns & square) != 0 ? 'p' : //
				((knights & square) != 0 ? 'n' : //
						((bishops & square) != 0 ? 'b' : //
								((rooks & square) != 0 ? 'r' : //
										((queens & square) != 0 ? 'q' : //
												((kings & square) != 0 ? 'k' : '.'))))));
		return ((whites & square) != 0 ? Character.toUpperCase(p) : p);
	}

	public char getPieceUnicodeAt(long square) {
		if ((whites & square) != 0) {
			return ((pawns & square) != 0 ? '♙' : //
					((knights & square) != 0 ? '♘' : //
							((bishops & square) != 0 ? '♗' : //
									((rooks & square) != 0 ? '♖' : //
											((queens & square) != 0 ? '♕' : //
													((kings & square) != 0 ? '♔' : '.'))))));

		} else if ((blacks & square) != 0) {
			return ((pawns & square) != 0 ? '♟' : //
					((knights & square) != 0 ? '♞' : //
							((bishops & square) != 0 ? '♝' : //
									((rooks & square) != 0 ? '♜' : //
											((queens & square) != 0 ? '♛' : //
													((kings & square) != 0 ? '♚' : '.'))))));
		} else {
			return '_';
		}
	}

	public void setPieceAt(long square, char piece) {
		pawns &= ~square;
		queens &= ~square;
		rooks &= ~square;
		bishops &= ~square;
		knights &= ~square;
		kings &= ~square;

		if (piece == ' ' || piece == '.') {
			whites &= ~square;
			blacks &= ~square;
			return;
		} else if (piece == Character.toLowerCase(piece)) {
			whites &= ~square;
			blacks |= square;
		} else {
			whites |= square;
			blacks &= ~square;
		}

		switch (Character.toLowerCase(piece)) {
			case 'p':
				pawns |= square;
				break;
			case 'q':
				queens |= square;
				break;
			case 'r':
				rooks |= square;
				break;
			case 'b':
				bishops |= square;
				break;
			case 'n':
				knights |= square;
				break;
			case 'k':
				kings |= square;
				break;
		}

		key = ZobristKey.getKey(this);
		setCheckFlags();
	}

	/**
	 * Converts board to its fen notation
	 */
	public String getFen() {
		StringBuilder sb = new StringBuilder();
		long i = Square.A8;
		int j = 0;
		while (i != 0) {
			char p = getPieceAt(i);
			if (p == '.') {
				j++;
			}
			if ((j != 0) && (p != '.' || ((i & BitboardUtils.b_r) != 0))) {
				sb.append(j);
				j = 0;
			}
			if (p != '.') {
				sb.append(p);
			}
			if ((i != 1) && (i & BitboardUtils.b_r) != 0) {
				sb.append("/");
			}
			i >>>= 1;
		}
		sb.append(" ");
		sb.append((getTurn() ? "w" : "b"));
		sb.append(" ");
		if (getWhiteKingsideCastling()) {
			sb.append("K");
		}
		if (getWhiteQueensideCastling()) {
			sb.append("Q");
		}
		if (getBlackKingsideCastling()) {
			sb.append("k");
		}
		if (getBlackQueensideCastling()) {
			sb.append("q");
		}
		if (!getWhiteQueensideCastling() && !getWhiteKingsideCastling() && !getBlackQueensideCastling() && !getBlackKingsideCastling()) {
			sb.append("-");
		}
		sb.append(" ");
		sb.append((getPassantSquare() != 0 ? BitboardUtils.square2Algebraic(getPassantSquare()) : "-"));
		sb.append(" ");
		sb.append(fiftyMovesRule);
		sb.append(" ");
		sb.append((moveNumber >> 1) + 1); // 0,1->1.. 2,3->2
		return sb.toString();
	}

	/**
	 * Loads board from a fen notation
	 */
	public void setFen(String fen) {
		setFenMove(fen, null);
	}

	/**
	 * Sets fen without destroying move history. If lastMove = null destroy the move history
	 */
	public void setFenMove(String fen, String lastMove) {
		long tmpWhites = 0;
		long tmpBlacks = 0;
		long tmpPawns = 0;
		long tmpRooks = 0;
		long tmpQueens = 0;
		long tmpBishops = 0;
		long tmpKnights = 0;
		long tmpKings = 0;
		long tmpFlags;
		int tmpFiftyMovesRule = 0;
		long tmpCastlingRooks[] = {0, 0, 0, 0};
		int fenMoveNumber = 0;

		int i = 0;
		long j = Square.A8;
		String[] tokens = fen.split("[ \\t\\n\\x0B\\f\\r]+");
		String board = tokens[0];

		while ((i < board.length()) && (j != 0)) {
			char p = board.charAt(i++);
			if (p != '/') {
				int number = 0;
				try {
					number = Integer.parseInt(String.valueOf(p));
				} catch (Exception ignored) {
				}

				for (int k = 0; k < (number == 0 ? 1 : number); k++) {
					tmpWhites = (tmpWhites & ~j) | ((number == 0) && (p == Character.toUpperCase(p)) ? j : 0);
					tmpBlacks = (tmpBlacks & ~j) | ((number == 0) && (p == Character.toLowerCase(p)) ? j : 0);
					tmpPawns = (tmpPawns & ~j) | (Character.toUpperCase(p) == 'P' ? j : 0);
					tmpRooks = (tmpRooks & ~j) | (Character.toUpperCase(p) == 'R' ? j : 0);
					tmpQueens = (tmpQueens & ~j) | (Character.toUpperCase(p) == 'Q' ? j : 0);
					tmpBishops = (tmpBishops & ~j) | (Character.toUpperCase(p) == 'B' ? j : 0);
					tmpKnights = (tmpKnights & ~j) | (Character.toUpperCase(p) == 'N' ? j : 0);
					tmpKings = (tmpKings & ~j) | (Character.toUpperCase(p) == 'K' ? j : 0);
					j >>>= 1;
					if (j == 0) {
						break; // security
					}
				}
			}
		}

		// Now the rest ...
		String turn = tokens[1];
		tmpFlags = 0;
		if ("b".equals(turn)) {
			tmpFlags |= FLAG_TURN;
		}
		if (tokens.length > 2) {
			// Set castling rights supporting XFEN to disambiguate positions in Chess960
			String castlings = tokens[2];

			chess960 = false;
			// Squares to the sides of the kings {White Kingside, White Queenside, Black Kingside, Black Queenside}
			long whiteKingLateralSquares[] = {
					BitboardUtils.b_d & ((tmpKings & tmpWhites) - 1), BitboardUtils.b_d & ~(((tmpKings & tmpWhites) - 1) | tmpKings & tmpWhites),
					BitboardUtils.b_u & ((tmpKings & tmpBlacks) - 1), BitboardUtils.b_u & ~(((tmpKings & tmpBlacks) - 1) | tmpKings & tmpBlacks)};

			// Squares where we can find a castling rook
			long possibleCastlingRookSquares[] = {0, 0, 0, 0};

			for (int k = 0; k < castlings.length(); k++) {
				char c = castlings.charAt(k);
				switch (c) {
					case 'K':
						possibleCastlingRookSquares[0] = whiteKingLateralSquares[0];
						break;
					case 'Q':
						possibleCastlingRookSquares[1] = whiteKingLateralSquares[1];
						break;
					case 'k':
						possibleCastlingRookSquares[2] = whiteKingLateralSquares[2];
						break;
					case 'q':
						possibleCastlingRookSquares[3] = whiteKingLateralSquares[3];
						break;
					default:
						// Shredder-FEN receives the name of the file where the castling rook is
						int whiteFile = "ABCDEFGH".indexOf(c);
						int blackFile = "abcdefgh".indexOf(c);
						if (whiteFile >= 0) {
							long rookSquare = BitboardUtils.b_d & BitboardUtils.FILE[whiteFile];
							if ((rookSquare & whiteKingLateralSquares[0]) != 0) {
								possibleCastlingRookSquares[0] = rookSquare;
							} else if ((rookSquare & whiteKingLateralSquares[1]) != 0) {
								possibleCastlingRookSquares[1] = rookSquare;
							}
						} else if (blackFile >= 0) {
							long rookSquare = BitboardUtils.b_u & BitboardUtils.FILE[blackFile];
							if ((rookSquare & whiteKingLateralSquares[2]) != 0) {
								possibleCastlingRookSquares[2] = rookSquare;
							} else if ((rookSquare & whiteKingLateralSquares[3]) != 0) {
								possibleCastlingRookSquares[3] = rookSquare;
							}
						}
				}
			}

			// Now store the squares of the castling rooks
			tmpCastlingRooks[0] = BitboardUtils.lsb(tmpRooks & tmpWhites & possibleCastlingRookSquares[0]);
			tmpCastlingRooks[1] = BitboardUtils.msb(tmpRooks & tmpWhites & possibleCastlingRookSquares[1]);
			tmpCastlingRooks[2] = BitboardUtils.lsb(tmpRooks & tmpBlacks & possibleCastlingRookSquares[2]);
			tmpCastlingRooks[3] = BitboardUtils.msb(tmpRooks & tmpBlacks & possibleCastlingRookSquares[3]);

			// Set the castling flags and detect Chess960
			if (tmpCastlingRooks[0] != 0) {
				tmpFlags |= FLAG_WHITE_KINGSIDE_CASTLING;
				if ((tmpWhites & tmpKings) != 1L << 3 || tmpCastlingRooks[0] != 1L) {
					chess960 = true;
				}
			}
			if (tmpCastlingRooks[1] != 0) {
				tmpFlags |= FLAG_WHITE_QUEENSIDE_CASTLING;
				if ((tmpWhites & tmpKings) != 1L << 3 || tmpCastlingRooks[1] != 1L << 7) {
					chess960 = true;
				}
			}
			if (tmpCastlingRooks[2] != 0) {
				tmpFlags |= FLAG_BLACK_KINGSIDE_CASTLING;
				if ((tmpBlacks & tmpKings) != 1L << 59 || tmpCastlingRooks[2] != 1L << 56) {
					chess960 = true;
				}
			}
			if (tmpCastlingRooks[3] != 0) {
				tmpFlags |= FLAG_BLACK_QUEENSIDE_CASTLING;
				if ((tmpBlacks & tmpKings) != 1L << 59 || tmpCastlingRooks[3] != 1L << 63) {
					chess960 = true;
				}
			}
			// END FEN castlings

			if (tokens.length > 3) {
				String passant = tokens[3];
				tmpFlags |= FLAGS_PASSANT & BitboardUtils.algebraic2Square(passant);
				if (tokens.length > 4) {
					try {
						tmpFiftyMovesRule = Integer.parseInt(tokens[4]);
					} catch (Exception e) {
						tmpFiftyMovesRule = 0;
					}
					if (tokens.length > 5) {
						String moveNumberString = tokens[5];
						int aux = Integer.parseInt(moveNumberString);
						fenMoveNumber = ((aux > 0 ? aux - 1 : aux) << 1) + ((tmpFlags & FLAG_TURN) == 0 ? 0 : 1);
						if (fenMoveNumber < 0) {
							fenMoveNumber = 0;
						}
					}
				}
			}
		}

		// try to apply the last move to see if we are advancing or undoing moves
		if ((moveNumber + 1) == fenMoveNumber && lastMove != null) {
			doMove(Move.getFromString(this, lastMove, true));
		} else if (fenMoveNumber < moveNumber) {
			for (int k = moveNumber; k > fenMoveNumber; k--) {
				undoMove();
			}
		}

		// Check if board changed or if we can keep the history
		if (whites != tmpWhites //
				|| blacks != tmpBlacks //
				|| pawns != tmpPawns //
				|| rooks != tmpRooks //
				|| queens != tmpQueens //
				|| bishops != tmpBishops //
				|| knights != tmpKnights //
				|| kings != tmpKings //
				|| (flags & FLAG_TURN) != (tmpFlags & FLAG_TURN)) {

			// board reset
			movesSan.clear();

			initialFen = fen;
			initialMoveNumber = fenMoveNumber;
			moveNumber = fenMoveNumber;
			outBookMove = Integer.MAX_VALUE;

			whites = tmpWhites;
			blacks = tmpBlacks;
			pawns = tmpPawns;
			rooks = tmpRooks;
			queens = tmpQueens;
			bishops = tmpBishops;
			knights = tmpKnights;
			kings = tmpKings;
			fiftyMovesRule = tmpFiftyMovesRule;

			// Flags are not completed till verify, so skip checking
			flags = tmpFlags;

			castlingRooks[0] = tmpCastlingRooks[0];
			castlingRooks[1] = tmpCastlingRooks[1];
			castlingRooks[2] = tmpCastlingRooks[2];
			castlingRooks[3] = tmpCastlingRooks[3];

			// Set zobrist key and check flags
			key = ZobristKey.getKey(this);
			setCheckFlags();

			// and save history
			resetHistory();
			saveHistory(0, false);
		} else {
			if (moveNumber < outBookMove) {
				outBookMove = Integer.MAX_VALUE;
			}
		}
	}

	/**
	 * Prints board in one string
	 */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		int j = 8;
		long i = Square.A8;
		while (i != 0) {
			sb.append(getPieceUnicodeAt(i));
			sb.append(" ");
			if ((i & BitboardUtils.b_r) != 0) {
				sb.append(j--);
				if (i == Square.H1) {
					sb.append(" ");
					sb.append(getFen());
				}
				sb.append("\n");
			}
			i >>>= 1;
		}
		sb.append("a b c d e f g h   ");
		sb.append((getTurn() ? "white moves " : "black moves "));
		sb.append((getWhiteKingsideCastling() ? " W:0-0" : "") + (getWhiteQueensideCastling() ? " W:0-0-0" : "") + (getBlackKingsideCastling() ? " B:0-0" : "") + (getBlackQueensideCastling() ? " B:0-0-0" : ""));

		return sb.toString();
	}

	/**
	 * TODO is it necessary??
	 */
	private void resetHistory() {
		Arrays.fill(whitesHistory, 0);
		Arrays.fill(blacksHistory, 0);
		Arrays.fill(pawnsHistory, 0);
		Arrays.fill(knightsHistory, 0);
		Arrays.fill(bishopsHistory, 0);
		Arrays.fill(rooksHistory, 0);
		Arrays.fill(queensHistory, 0);
		Arrays.fill(kingsHistory, 0);
		Arrays.fill(flagsHistory, 0);
		for (int i = 0; i < MAX_MOVES; i++) {
			Arrays.fill(keyHistory[i], 0);
		}
		Arrays.fill(fiftyMovesRuleHistory, 0);
		Arrays.fill(moveHistory, 0);
		movesSan.clear();
	}

	private void saveHistory(int move, boolean fillSanInfo) {
		if (fillSanInfo) {
			movesSan.put(moveNumber, Move.toSan(this, move));
		}

		moveHistory[moveNumber] = move;
		whitesHistory[moveNumber] = whites;
		blacksHistory[moveNumber] = blacks;
		pawnsHistory[moveNumber] = pawns;
		knightsHistory[moveNumber] = knights;
		bishopsHistory[moveNumber] = bishops;
		rooksHistory[moveNumber] = rooks;
		queensHistory[moveNumber] = queens;
		kingsHistory[moveNumber] = kings;
		flagsHistory[moveNumber] = flags;
		keyHistory[moveNumber][0] = key[0];
		keyHistory[moveNumber][1] = key[1];
		fiftyMovesRuleHistory[moveNumber] = fiftyMovesRule;
	}

	/**
	 * This is very inefficient because it fills the San info, so it must not be called from inside search
	 */
	public boolean doMove(int move) {
		return doMove(move, true, true);
	}

	/**
	 * Moves and also updates the board's zobrist key verify legality, if not
	 * legal undo move and return false
	 */
	public boolean doMove(int move, boolean verifyCheck, boolean fillSanInfo) {
		if (move == Move.NONE) {
			return false;
		}
		// Save history
		saveHistory(move, fillSanInfo);

		// Count consecutive moves without capture or without pawn move
		fiftyMovesRule++;
		moveNumber++; // Count Ply moves

		boolean turn = getTurn();
		int color = turn ? Color.W : Color.B;

		if ((flags & FLAGS_PASSANT) != 0) {
			// Remove passant flags: from the zobrist key
			key[1 - color] ^= ZobristKey.passantFile[BitboardUtils.getFile(flags & FLAGS_PASSANT)];
			// and from the flags
			flags &= ~FLAGS_PASSANT;
		}

		if (move == Move.NULL) {
			// Change turn
			flags ^= FLAG_TURN;
			key[0] ^= ZobristKey.whiteMove;
			return true;
		}

		int fromIndex = Move.getFromIndex(move);
		long from = Move.getFromSquare(move);

		// Check if we are applying a move in the other turn
		if ((from & getMines()) == 0) {
			undoMove();
			return false;
		}

		int toIndex = Move.getToIndex(move);
		long to = Move.getToSquare(move);
		long moveMask = from | to; // Move is as easy as xor with this mask (exceptions are promotions, captures and en-passant captures)
		int moveType = Move.getMoveType(move);
		int pieceMoved = Move.getPieceMoved(move);
		boolean capture = Move.isCapture(move);

		// Is it is a capture, remove pieces in destination square
		if (capture) {
			fiftyMovesRule = 0;
			// En-passant pawn captures remove captured pawn, put the pawn in to
			int toIndexCapture = toIndex;
			if (moveType == Move.TYPE_PASSANT) {
				to = (getTurn() ? (to >>> 8) : (to << 8));
				toIndexCapture += (getTurn() ? -8 : 8);
			}
			key[1 - color] ^= ZobristKey.getKeyPieceIndex(toIndexCapture, getPieceAt(to));

			whites &= ~to;
			blacks &= ~to;
			pawns &= ~to;
			queens &= ~to;
			rooks &= ~to;
			bishops &= ~to;
			knights &= ~to;
		}

		// Pawn movements
		switch (pieceMoved) {
			case Piece.PAWN:
				fiftyMovesRule = 0;
				// Set new passant flags if pawn is advancing two squares (marks
				// the destination square where the pawn can be captured)
				// Set only passant flags when the other side can capture
				if (((from << 16) & to) != 0 && (bbAttacks.pawn[Color.W][toIndex - 8] & pawns & getOthers()) != 0) { // white
					flags |= (from << 8);
				}
				if (((from >>> 16) & to) != 0 && (bbAttacks.pawn[Color.B][toIndex + 8] & pawns & getOthers()) != 0) { // blask
					flags |= (from >>> 8);
				}
				if ((flags & FLAGS_PASSANT) != 0) {
					key[color] ^= ZobristKey.passantFile[BitboardUtils.getFile(flags & FLAGS_PASSANT)];
				}

				if (moveType == Move.TYPE_PROMOTION_QUEEN || moveType == Move.TYPE_PROMOTION_KNIGHT || moveType == Move.TYPE_PROMOTION_BISHOP
						|| moveType == Move.TYPE_PROMOTION_ROOK) { // Promotions:
					// change
					// the piece
					pawns &= ~from;
					key[color] ^= ZobristKey.pawn[color][fromIndex];
					switch (moveType) {
						case Move.TYPE_PROMOTION_QUEEN:
							queens |= to;
							key[color] ^= ZobristKey.queen[color][toIndex];
							break;
						case Move.TYPE_PROMOTION_KNIGHT:
							knights |= to;
							key[color] ^= ZobristKey.knight[color][toIndex];
							break;
						case Move.TYPE_PROMOTION_BISHOP:
							bishops |= to;
							key[color] ^= ZobristKey.bishop[color][toIndex];
							break;
						case Move.TYPE_PROMOTION_ROOK:
							rooks |= to;
							key[color] ^= ZobristKey.rook[color][toIndex];
							break;
					}
				} else {
					pawns ^= moveMask;
					key[color] ^= ZobristKey.pawn[color][fromIndex] ^ ZobristKey.pawn[color][toIndex];
				}
				break;
			case Piece.ROOK:
				rooks ^= moveMask;
				key[color] ^= ZobristKey.rook[color][fromIndex] ^ ZobristKey.rook[color][toIndex];
				break;
			case Piece.BISHOP:
				bishops ^= moveMask;
				key[color] ^= ZobristKey.bishop[color][fromIndex] ^ ZobristKey.bishop[color][toIndex];
				break;
			case Piece.KNIGHT:
				knights ^= moveMask;
				key[color] ^= ZobristKey.knight[color][fromIndex] ^ ZobristKey.knight[color][toIndex];
				break;
			case Piece.QUEEN:
				queens ^= moveMask;
				key[color] ^= ZobristKey.queen[color][fromIndex] ^ ZobristKey.queen[color][toIndex];
				break;
			case Piece.KING: // if castling, moves rooks too
				if (moveType == Move.TYPE_KINGSIDE_CASTLING || moveType == Move.TYPE_QUEENSIDE_CASTLING) {
					// {White Kingside, White Queenside, Black Kingside, Black Queenside}
					int j = (color << 1) + (moveType == Move.TYPE_QUEENSIDE_CASTLING ? 1 : 0);

					toIndex = CASTLING_KING_DESTINY_INDEX[j];
					int originRookIndex = BitboardUtils.square2Index(castlingRooks[j]);
					int destinyRookIndex = CASTLING_ROOK_DESTINY_INDEX[j];
					// Recalculate move mask for chess960 castlings
					moveMask = from ^ (1L << toIndex);
					long rookMoveMask = (1L << originRookIndex) ^ (1L << destinyRookIndex);
					key[color] ^= ZobristKey.rook[color][originRookIndex] ^ ZobristKey.rook[color][destinyRookIndex];

					if (getTurn()) {
						whites ^= rookMoveMask;
					} else {
						blacks ^= rookMoveMask;
					}
					rooks ^= rookMoveMask;
				}
				kings ^= moveMask;
				key[color] ^= ZobristKey.king[color][fromIndex] ^ ZobristKey.king[color][toIndex];
				break;
		}
		// Move pieces in colour fields
		if (getTurn()) {
			whites ^= moveMask;
		} else {
			blacks ^= moveMask;
		}

		// Tests to disable castling
		if ((flags & FLAG_WHITE_KINGSIDE_CASTLING) != 0 && //
				((turn && pieceMoved == Piece.KING) || from == castlingRooks[0] || to == castlingRooks[0])) {
			flags &= ~FLAG_WHITE_KINGSIDE_CASTLING;
			key[0] ^= ZobristKey.whiteKingSideCastling;
		}
		if ((flags & FLAG_WHITE_QUEENSIDE_CASTLING) != 0 && //
				((turn && pieceMoved == Piece.KING) || from == castlingRooks[1] || to == castlingRooks[1])) {
			flags &= ~FLAG_WHITE_QUEENSIDE_CASTLING;
			key[0] ^= ZobristKey.whiteQueenSideCastling;
		}
		if ((flags & FLAG_BLACK_KINGSIDE_CASTLING) != 0 && //
				((!turn && pieceMoved == Piece.KING) || from == castlingRooks[2] || to == castlingRooks[2])) {
			flags &= ~FLAG_BLACK_KINGSIDE_CASTLING;
			key[1] ^= ZobristKey.blackKingSideCastling;
		}
		if ((flags & FLAG_BLACK_QUEENSIDE_CASTLING) != 0 && //
				((!turn && pieceMoved == Piece.KING) || from == castlingRooks[3] || to == castlingRooks[3])) {
			flags &= ~FLAG_BLACK_QUEENSIDE_CASTLING;
			key[1] ^= ZobristKey.blackQueenSideCastling;
		}
		// Change turn
		flags ^= FLAG_TURN;
		key[0] ^= ZobristKey.whiteMove;

		if (verifyCheck) {
			if (isValid()) {
				setCheckFlags();

				if (fillSanInfo) {
					if (isMate()) { // Append # when mate
						movesSan.put(moveNumber - 1, movesSan.get(moveNumber - 1).replace("+", "#"));
					}
				}
			} else {
				undoMove();
				return false;
			}
		} else {
			// Trust move check flag
			if (Move.isCheck(move)) {
				flags |= FLAG_CHECK;
			} else {
				flags &= ~FLAG_CHECK;
			}
		}
		return true;
	}

	/**
	 * It checks if a state is valid basically, if the other king is not in check
	 */
	private boolean isValid() {
		return (!bbAttacks.isSquareAttacked(this, kings & getOthers(), !getTurn()));
	}

	/**
	 * Sets check flag if the own king is in check
	 */
	private void setCheckFlags() {
		if (bbAttacks.isSquareAttacked(this, kings & getMines(), getTurn())) {
			flags |= FLAG_CHECK;
		} else {
			flags &= ~FLAG_CHECK;
		}
	}

	public void undoMove() {
		undoMove(moveNumber - 1);
	}

	public void undoMove(int moveNumber) {
		if (moveNumber < 0 || moveNumber < initialMoveNumber) {
			return;
		}
		this.moveNumber = moveNumber;

		whites = whitesHistory[moveNumber];
		blacks = blacksHistory[moveNumber];
		pawns = pawnsHistory[moveNumber];
		knights = knightsHistory[moveNumber];
		bishops = bishopsHistory[moveNumber];
		rooks = rooksHistory[moveNumber];
		queens = queensHistory[moveNumber];
		kings = kingsHistory[moveNumber];
		flags = flagsHistory[moveNumber];
		key[0] = keyHistory[moveNumber][0];
		key[1] = keyHistory[moveNumber][1];
		fiftyMovesRule = fiftyMovesRuleHistory[moveNumber];
	}

	/**
	 * 0 no, 1 whites won, -1 blacks won, 99 draw
	 */
	public int isEndGame() {
		int endGame = 0;
		generateLegalMoves();
		if (legalMoveCount == 0) {
			if (getCheck()) {
				endGame = (getTurn() ? -1 : 1);
			} else {
				endGame = 99;
			}
		} else if (isDraw()) {
			endGame = 99;
		}
		return endGame;
	}

	public boolean isMate() {
		int endgameState = isEndGame();
		return endgameState == 1 || endgameState == -1;
	}

	/**
	 * checks draw by fifty move rule and threefold repetition
	 */
	public boolean isDraw() {
		if (fiftyMovesRule >= 100) {
			return true;
		}
		int repetitions = 0;
		for (int i = 0; i < (moveNumber - 1); i++) {
			if (keyHistory[i][0] == key[0] && keyHistory[i][1] == key[1]) {
				repetitions++;
			}
			if (repetitions >= 2) { // with the last one they are 3
				return true;
			}
		}
		// Draw by no material to mate by FIDE rules
		// https://en.wikipedia.org/wiki/Rules_of_chess#Draws
		// Kk, KNk, KNNk (KNnk IS NOT a draw), KBk, KBbk (with bishops in the same color)
		return (pawns == 0 && rooks == 0 && queens == 0) &&
				((bishops == 0 && knights == 0)
						|| (knights == 0 && BitboardUtils.popCount(bishops) == 1)
						|| (bishops == 0 &&
						(BitboardUtils.popCount(knights) == 1
								|| (BitboardUtils.popCount(knights) == 2 // KNNk, check same color
								&& (BitboardUtils.popCount(knights & whites) == 2
								|| BitboardUtils.popCount(knights & ~whites) == 2))))
						|| (knights == 0
						&& BitboardUtils.popCount(bishops & whites) == 1
						&& BitboardUtils.popCount(bishops & ~whites) == 1
						&& (BitboardUtils.getSameColorSquares(bishops & whites) & bishops & ~whites) != 0
				)
				);
	}

	public int see(int move) {
		return see(Move.getFromIndex(move), Move.getToIndex(move), Move.getPieceMoved(move), Move.isCapture(move) ? Move.getPieceCaptured(this, move) : 0);
	}

	public int see(int move, AttacksInfo attacksInfo) {
		int them = getTurn() ? 1 : 0;
		if (attacksInfo.boardKey == getKey()
				&& (attacksInfo.attackedSquares[them] & Move.getToSquare(move)) == 0
				&& (attacksInfo.mayPin[them] & Move.getFromSquare(move)) == 0) {
			return Move.isCapture(move) ? Board.SEE_PIECE_VALUES[Move.getPieceCaptured(this, move)] : 0;
		} else {
			return see(move);
		}
	}

	/**
	 * The SWAP algorithm https://chessprogramming.wikispaces.com/SEE+-+The+Swap+Algorithm
	 */
	public int see(int fromIndex, int toIndex, int pieceMoved, int targetPiece) {
		int d = 0;
		long mayXray = pawns | bishops | rooks | queens; // not kings nor knights
		long fromSquare = 0x1L << fromIndex;
		long all = getAll();
		long attacks = bbAttacks.getIndexAttacks(this, toIndex);
		long fromCandidates;

		seeGain[d] = SEE_PIECE_VALUES[targetPiece];
		do {
			long side = (d & 1) == 0 ? getOthers() : getMines();
			d++; // next depth and side speculative store, if defended
			seeGain[d] = SEE_PIECE_VALUES[pieceMoved] - seeGain[d - 1];
			attacks ^= fromSquare; // reset bit in set to traverse
			all ^= fromSquare; // reset bit in temporary occupancy (for X-Rays)
			if ((fromSquare & mayXray) != 0) {
				attacks |= bbAttacks.getXrayAttacks(this, toIndex, all);
			}

			// Gets the next attacker
			if ((fromCandidates = attacks & pawns & side) != 0) {
				pieceMoved = Piece.PAWN;
			} else if ((fromCandidates = attacks & knights & side) != 0) {
				pieceMoved = Piece.KNIGHT;
			} else if ((fromCandidates = attacks & bishops & side) != 0) {
				pieceMoved = Piece.BISHOP;
			} else if ((fromCandidates = attacks & rooks & side) != 0) {
				pieceMoved = Piece.ROOK;
			} else if ((fromCandidates = attacks & queens & side) != 0) {
				pieceMoved = Piece.QUEEN;
			} else if ((fromCandidates = attacks & kings & side) != 0) {
				pieceMoved = Piece.KING;
			}
			fromSquare = BitboardUtils.lsb(fromCandidates);

		} while (fromSquare != 0);

		while (--d != 0) {
			seeGain[d - 1] = -Math.max(-seeGain[d - 1], seeGain[d]);
		}
		return seeGain[0];
	}

	public boolean isUsingBook() {
		return outBookMove > moveNumber;
	}

	public void setOutBookMove(int outBookMove) {
		this.outBookMove = outBookMove;
	}

	/**
	 * Check if a passed pawn is in the index, useful to trigger extensions
	 */
	public boolean isPassedPawn(int index) {
		int rank = index >> 3;
		int file = 7 - index & 7;
		long square = 0x1L << index;

		if ((whites & square) != 0) {
			return ((BitboardUtils.FILE[file] | BitboardUtils.FILES_ADJACENT[file]) & BitboardUtils.RANKS_UPWARDS[rank] & pawns & blacks) == 0;
		} else if ((blacks & square) != 0) {
			return ((BitboardUtils.FILE[file] | BitboardUtils.FILES_ADJACENT[file]) & BitboardUtils.RANKS_DOWNWARDS[rank] & pawns & whites) == 0;
		}
		return false;
	}

	/**
	 * Generates legal moves for the position when not already generated
	 */
	void generateLegalMoves() {
		if ((key[0] != legalMovesKey[0]) || (key[1] != legalMovesKey[1])) {
			legalMoveCount = legalMoveGenerator.generateMoves(this, legalMoves, 0);
			legalMovesKey[0] = key[0];
			legalMovesKey[1] = key[1];
		}
	}

	public int getLegalMoves(int moves[]) {
		generateLegalMoves();
		System.arraycopy(legalMoves, 0, moves, 0, (legalMoveCount != -1 ? legalMoveCount : 0));
		return legalMoveCount;
	}

	/**
	 * Returns the move with the check flag set if the move is a legal move.
	 * Ignores the check flag in the original move.
	 * Returns Move.NONE if the move is not legal
	 */
	public int getLegalMove(int move) {
		generateLegalMoves();
		for (int i = 0; i < legalMoveCount; i++) {
			if ((move & ~Move.CHECK_MASK) == (legalMoves[i] & ~Move.CHECK_MASK)) {
				return legalMoves[i];
			}
		}
		return Move.NONE;
	}

	public String getSanMove(int moveNumber) {
		return movesSan.get(moveNumber);
	}

	public boolean getMoveTurn(int moveNumber) {
		return (flagsHistory[moveNumber] & FLAG_TURN) == 0;
	}

	public String getInitialFen() {
		return initialFen;
	}

	public String getMoves() {
		StringBuilder oSB = new StringBuilder();
		for (int i = initialMoveNumber; i < moveNumber; i++) {
			if (oSB.length() > 0) {
				oSB.append(" ");
			}
			oSB.append(Move.toString(moveHistory[i]));
		}
		return oSB.toString();
	}

	public String getMovesSan() {
		StringBuilder oSB = new StringBuilder();
		for (int i = initialMoveNumber; i < moveNumber; i++) {
			if (oSB.length() > 0) {
				oSB.append(" ");
			}
			oSB.append(movesSan.get(i));
		}
		return oSB.toString();
	}

	public String toSanNextMoves(String moves) {
		if (moves == null || "".equals(moves.trim())) {
			return "";
		}

		StringBuilder oSB = new StringBuilder();
		String movesArray[] = moves.split(" ");
		int savedMoveNumber = moveNumber;

		for (String moveString : movesArray) {
			int move = Move.getFromString(this, moveString, true);

			if (!doMove(move)) {
				undoMove(savedMoveNumber);
				return "";
			}

			if (oSB.length() > 0) {
				oSB.append(" ");
			}
			oSB.append(getLastMoveSan());
		}
		undoMove(savedMoveNumber);
		return oSB.toString();
	}

	public int getLastMove() {
		if (moveNumber == 0) {
			return Move.NONE;
		}
		return moveHistory[moveNumber - 1];
	}

	public String getLastMoveSan() {
		if (moveNumber == 0) {
			return null;
		}
		return movesSan.get(moveNumber - 1);
	}

	/**
	 * Convenience method to apply all the moves in a string separated by spaces
	 */
	public void doMoves(String moves) {
		if (moves == null || "".equals(moves.trim())) {
			return;
		}
		String movesArray[] = moves.split(" ");
		for (String moveString : movesArray) {
			int move = Move.getFromString(this, moveString, true);
			doMove(move);
		}
	}

} public class Config {

	// Default values are static fields used also from UCIEngine
	public static final int DEFAULT_TRANSPOSITION_TABLE_SIZE = 64;
	public static final boolean DEFAULT_PONDER = true;
	public static final boolean DEFAULT_USE_BOOK = true;
	public static final int DEFAULT_BOOK_KNOWGLEDGE = 100;
	public static final String DEFAULT_EVALUATOR = "complete";

	// >0 refuses draw <0 looks for draw
	public static final int DEFAULT_CONTEMPT_FACTOR = 90;

	public static final int DEFAULT_RAND = 0;
	public static final int DEFAULT_ELO = 2100;
	public static final boolean DEFAULT_UCI_CHESS960 = false;

	public int transpositionTableSize = DEFAULT_TRANSPOSITION_TABLE_SIZE;
	public boolean ponder = DEFAULT_PONDER;
	public boolean useBook = DEFAULT_USE_BOOK;
	public Book book;
	public int bookKnowledge = DEFAULT_BOOK_KNOWGLEDGE;
	public String evaluator = DEFAULT_EVALUATOR;
	public int contemptFactor = DEFAULT_CONTEMPT_FACTOR;

	private int rand = DEFAULT_RAND;
	private boolean uciChess960 = DEFAULT_UCI_CHESS960;

	public boolean getPonder() {
		return ponder;
	}

	public void setPonder(boolean ponder) {
		this.ponder = ponder;
	}

	public boolean getUseBook() {
		return useBook;
	}

	public void setUseBook(boolean useBook) {
		this.useBook = useBook;
	}

	public Book getBook() {
		return book;
	}

	public void setBook(Book book) {
		this.book = book;
	}

	public int getBookKnowledge() {
		return bookKnowledge;
	}

	public void setBookKnowledge(int bookKnowledge) {
		this.bookKnowledge = bookKnowledge;
	}

	public String getEvaluator() {
		return evaluator;
	}

	public void setEvaluator(String evaluator) {
		this.evaluator = evaluator;
	}

	public int getTranspositionTableSize() {
		return transpositionTableSize;
	}

	public void setTranspositionTableSize(int transpositionTableSize) {
		this.transpositionTableSize = transpositionTableSize;
	}

	public int getContemptFactor() {
		return contemptFactor;
	}

	public void setContemptFactor(int contemptFactor) {
		this.contemptFactor = contemptFactor;
	}

	public int getRand() {
		return rand;
	}

	public void setRand(int rand) {
		this.rand = rand;
	}

	public boolean isUciChess960() {
		return uciChess960;
	}

	public void setUciChess960(boolean uciChess960) {
		this.uciChess960 = uciChess960;
	}

	/**
	 * 2100 is the max, 500 the min
	 *
	 * @param engineElo
	 */
	public void setElo(int engineElo) {
		int errorsPerMil = 900 - ((engineElo - 500) * 900) / 1600;
		setRand(errorsPerMil);

		int kPercentage = ((engineElo - 500) * 100) / 1600; // knowledge percentage
		setBookKnowledge(kPercentage);
	}
} public class Move {
	// Predefined moves
	public static final int NONE = 0;
	public static final int NULL = -1;

	public static final String NONE_STRING = "none";
	public static final String NULL_STRING = "null";

	public static final String PIECE_LETTERS_LOWERCASE = " pnbrqk";
	public static final String PIECE_LETTERS_UPPERCASE = " PNBRQK";

	// Move Types
	public static final int TYPE_KINGSIDE_CASTLING = 1;
	public static final int TYPE_QUEENSIDE_CASTLING = 2;
	public static final int TYPE_PASSANT = 3;
	// Promotions must be always >= TYPE_PROMOTION_QUEEN
	public static final int TYPE_PROMOTION_QUEEN = 4;
	public static final int TYPE_PROMOTION_KNIGHT = 5;
	public static final int TYPE_PROMOTION_BISHOP = 6;
	public static final int TYPE_PROMOTION_ROOK = 7;

	public static final int CHECK_MASK = 0x1 << 16;
	public static final int CAPTURE_MASK = 0x1 << 15;

	public static int genMove(int fromIndex, int toIndex, int pieceMoved, boolean capture, boolean check, int moveType) {
		return toIndex | fromIndex << 6 | pieceMoved << 12 | (capture ? CAPTURE_MASK : 0) | (check ? CHECK_MASK : 0) | moveType << 17;
	}

	public static int genMove(int fromIndex, int toIndex, int pieceMoved, boolean capture, int moveType) {
		return toIndex | fromIndex << 6 | pieceMoved << 12 | (capture ? CAPTURE_MASK : 0) | moveType << 17;
	}

	public static int getToIndex(int move) {
		return move & 0x3f;
	}

	public static long getToSquare(int move) {
		return 0x1L << (move & 0x3f);
	}

	public static int getFromIndex(int move) {
		return ((move >>> 6) & 0x3f);
	}

	public static long getFromSquare(int move) {
		return 0x1L << ((move >>> 6) & 0x3f);
	}

	public static int getPieceMoved(int move) {
		return ((move >>> 12) & 0x7);
	}

	public static int getPieceCaptured(Board board, int move) {
		if (getMoveType(move) == TYPE_PASSANT) {
			return Piece.PAWN;
		}
		long toSquare = getToSquare(move);
		if ((toSquare & board.pawns) != 0) {
			return Piece.PAWN;
		} else if ((toSquare & board.knights) != 0) {
			return Piece.KNIGHT;
		} else if ((toSquare & board.bishops) != 0) {
			return Piece.BISHOP;
		} else if ((toSquare & board.rooks) != 0) {
			return Piece.ROOK;
		} else if ((toSquare & board.queens) != 0) {
			return Piece.QUEEN;
		}
		return 0;
	}

	public static boolean isCapture(int move) {
		return (move & CAPTURE_MASK) != 0;
	}

	public static boolean isCheck(int move) {
		return (move & CHECK_MASK) != 0;
	}

	public static boolean isCaptureOrCheck(int move) {
		return (move & (CHECK_MASK | CAPTURE_MASK)) != 0;
	}

	public static int getMoveType(int move) {
		return ((move >>> 17) & 0x7);
	}

	// Pawn push to 7 or 8th rank
	public static boolean isPawnPush(int move) {
		return Move.getPieceMoved(move) == Piece.PAWN && (Move.getToIndex(move) < 16 || Move.getToIndex(move) > 47);
	}

	// Pawn push to 6, 7 or 8th rank
	public static boolean isPawnPush678(int move) {
		return Move.getPieceMoved(move) == Piece.PAWN && (Move.getFromIndex(move) < Move.getToIndex(move) ? Move.getToIndex(move) >= 40 : Move.getToIndex(move) < 24);
	}

	// Pawn push to 5, 6, 7 or 8th rank
	public static boolean isPawnPush5678(int move) {
		return Move.getPieceMoved(move) == Piece.PAWN && (Move.getFromIndex(move) < Move.getToIndex(move) ? Move.getToIndex(move) >= 32 : Move.getToIndex(move) < 32);
	}

	/**
	 * Checks if this move is a promotion
	 */
	public static boolean isPromotion(int move) {
		return Move.getMoveType(move) >= TYPE_PROMOTION_QUEEN;
	}

	public static int getPiecePromoted(int move) {
		switch (getMoveType(move)) {
			case TYPE_PROMOTION_QUEEN:
				return Piece.QUEEN;
			case TYPE_PROMOTION_ROOK:
				return Piece.ROOK;
			case TYPE_PROMOTION_KNIGHT:
				return Piece.KNIGHT;
			case TYPE_PROMOTION_BISHOP:
				return Piece.BISHOP;
		}
		return 0;
	}

	/**
	 * Is capture or promotion
	 *
	 * @param move
	 * @return
	 */
	public static boolean isTactical(int move) {
		return (Move.isCapture(move) || Move.isPromotion(move));
	}

	public static boolean isCastling(int move) {
		return Move.getMoveType(move) == TYPE_KINGSIDE_CASTLING || Move.getMoveType(move) == TYPE_QUEENSIDE_CASTLING;
	}

	/**
	 * Given a board creates a move from a String in uci format or short
	 * algebraic form. verifyValidMove true is mandatory if using sort algebraic
	 *
	 * @param board
	 * @param move
	 */
	public static int getFromString(Board board, String move, boolean verifyValidMove) {
		if (NULL_STRING.equals(move)) {
			return Move.NULL;
		} else if ("".equals(move) || NONE_STRING.equals(move)) {
			return Move.NONE;
		}

		int fromIndex;
		int toIndex;
		int moveType = 0;
		int pieceMoved = 0;
		boolean check = move.indexOf("+") > 0 || move.indexOf("#") > 0;
		long mines = board.getMines();
		boolean turn = board.getTurn();

		// Ignore checks, captures indicators...
		move = move.replace("+", "").replace("x", "").replace("-", "").replace("=", "").replace("#", "").replace("?", "").replace("!", "").replace(" ", "").replace("0", "o").replace("O", "o");

		if ("oo".equals(move)) {
			move = BitboardUtils.SQUARE_NAMES[BitboardUtils.square2Index(board.kings & mines)] + //
					BitboardUtils.SQUARE_NAMES[BitboardUtils.square2Index(board.chess960 ? board.castlingRooks[turn ? 0 : 2] : Board.CASTLING_KING_DESTINY_SQUARE[turn ? 0 : 2])];
		} else if ("ooo".equals(move)) {
			move = BitboardUtils.SQUARE_NAMES[BitboardUtils.square2Index(board.kings & mines)] + //
					BitboardUtils.SQUARE_NAMES[BitboardUtils.square2Index(board.chess960 ? board.castlingRooks[turn ? 1 : 3] : Board.CASTLING_KING_DESTINY_SQUARE[turn ? 1 : 3])];
		} else {
			char promo = move.charAt(move.length() - 1);
			switch (Character.toLowerCase(promo)) {
				case 'q':
					moveType = TYPE_PROMOTION_QUEEN;
					break;
				case 'n':
					moveType = TYPE_PROMOTION_KNIGHT;
					break;
				case 'b':
					moveType = TYPE_PROMOTION_BISHOP;
					break;
				case 'r':
					moveType = TYPE_PROMOTION_ROOK;
					break;
			}
			// If promotion, remove the last char
			if (moveType != 0) {
				move = move.substring(0, move.length() - 1);
			}
		}

		// To is always the last 2 characters
		toIndex = BitboardUtils.algebraic2Index(move.substring(move.length() - 2, move.length()));
		long to = 0x1L << toIndex;
		long from = 0;

		BitboardAttacks bbAttacks = BitboardAttacks.getInstance();

		// Fills from with a mask of possible from values
		switch (move.charAt(0)) {
			case 'N':
				from = board.knights & mines & bbAttacks.knight[toIndex];
				break;
			case 'K':
				from = board.kings & mines & bbAttacks.king[toIndex];
				break;
			case 'R':
				from = board.rooks & mines & bbAttacks.getRookAttacks(toIndex, board.getAll());
				break;
			case 'B':
				from = board.bishops & mines & bbAttacks.getBishopAttacks(toIndex, board.getAll());
				break;
			case 'Q':
				from = board.queens & mines & (bbAttacks.getRookAttacks(toIndex, board.getAll()) | bbAttacks.getBishopAttacks(toIndex, board.getAll()));
				break;
		}
		if (from != 0) { // remove the piece char
			move = move.substring(1);
		} else { // Pawn moves
			if (move.length() == 2) {
				if (turn) {
					from = board.pawns & mines & ((to >>> 8) | (((to >>> 8) & board.getAll()) == 0 ? (to >>> 16) : 0));
				} else {
					from = board.pawns & mines & ((to << 8) | (((to << 8) & board.getAll()) == 0 ? (to << 16) : 0));
				}
			}
			if (move.length() == 3) { // Pawn capture
				from = board.pawns & mines &  bbAttacks.pawn[turn ? Color.B : Color.W][toIndex];
			}
		}
		if (move.length() == 3) { // now disambiaguate
			char disambiguate = move.charAt(0);
			int i = "abcdefgh".indexOf(disambiguate);
			if (i >= 0) {
				from &= BitboardUtils.FILE[i];
			}
			int j = "12345678".indexOf(disambiguate);
			if (j >= 0) {
				from &= BitboardUtils.RANK[j];
			}
		}
		if (move.length() == 4) { // was algebraic complete e2e4 (=UCI!)
			from = BitboardUtils.algebraic2Square(move.substring(0, 2));
		}
		if (from == 0 || (from & board.getMines()) == 0) {
			return NONE;
		}

		// Treats multiple froms, choosing the first Legal Move
		while (from != 0) {
			long myFrom = BitboardUtils.lsb(from);
			from ^= myFrom;
			fromIndex = BitboardUtils.square2Index(myFrom);

			boolean capture = false;
			if ((myFrom & board.pawns) != 0) {

				pieceMoved = Piece.PAWN;
				// for passant captures
				if ((toIndex != (fromIndex - 8)) && (toIndex != (fromIndex + 8)) && (toIndex != (fromIndex - 16)) && (toIndex != (fromIndex + 16))) {
					if ((to & board.getAll()) == 0) {
						moveType = TYPE_PASSANT;
						capture = true; // later is changed if it was not a pawn
					}
				}
				// Default promotion to queen if not specified
				if ((to & (BitboardUtils.b_u | BitboardUtils.b_d)) != 0 && (moveType < TYPE_PROMOTION_QUEEN)) {
					moveType = TYPE_PROMOTION_QUEEN;
				}
			}
			if ((myFrom & board.bishops) != 0) {
				pieceMoved = Piece.BISHOP;
			} else if ((myFrom & board.knights) != 0) {
				pieceMoved = Piece.KNIGHT;
			} else if ((myFrom & board.rooks) != 0) {
				pieceMoved = Piece.ROOK;
			} else if ((myFrom & board.queens) != 0) {
				pieceMoved = Piece.QUEEN;
			} else if ((myFrom & board.kings) != 0) {
				pieceMoved = Piece.KING;
				if ((turn ? board.getWhiteKingsideCastling() : board.getBlackKingsideCastling()) && //
						(toIndex == (fromIndex - 2) || to == board.castlingRooks[turn ? 0 : 2])) {
					moveType = TYPE_KINGSIDE_CASTLING;
				}
				if ((turn ? board.getWhiteQueensideCastling() : board.getBlackQueensideCastling()) && //
						(toIndex == (fromIndex + 2) || to == board.castlingRooks[turn ? 1 : 3])) {
					moveType = TYPE_QUEENSIDE_CASTLING;
				}
			}

			// Now set captured piece flag
			if ((to & (turn ? board.blacks : board.whites)) != 0) {
				capture = true;
			}
			int moveInt = Move.genMove(fromIndex, toIndex, pieceMoved, capture, check, moveType);
			if (verifyValidMove) {
				moveInt = board.getLegalMove(moveInt);
				if (moveInt != NONE) {
					return moveInt;
				}
			} else {
				return moveInt;
			}
		}
		return NONE;
	}

	/**
	 * Gets an UCI-String representation of the move
	 *
	 * @param move
	 * @return
	 */
	public static String toString(int move) {
		if (move == Move.NONE) {
			return NONE_STRING;
		} else if (move == Move.NULL) {
			return NULL_STRING;
		}
		StringBuilder sb = new StringBuilder();
		sb.append(BitboardUtils.index2Algebraic(Move.getFromIndex(move)));
		sb.append(BitboardUtils.index2Algebraic(Move.getToIndex(move)));
		if (isPromotion(move)) {
			sb.append(PIECE_LETTERS_LOWERCASE.charAt(getPiecePromoted(move)));
		}
		return sb.toString();
	}

	public static String toStringExt(int move) {
		if (move == Move.NONE) {
			return NONE_STRING;
		} else if (move == Move.NULL) {
			return NULL_STRING;
		} else if (Move.getMoveType(move) == TYPE_KINGSIDE_CASTLING) {
			return Move.isCheck(move) ? "O-O+" : "O-O";
		} else if (Move.getMoveType(move) == TYPE_QUEENSIDE_CASTLING) {
			return Move.isCheck(move) ? "O-O-O+" : "O-O-O";
		}

		StringBuilder sb = new StringBuilder();
		if (getPieceMoved(move) != Piece.PAWN) {
			sb.append(PIECE_LETTERS_UPPERCASE.charAt(getPieceMoved(move)));
		}
		sb.append(BitboardUtils.index2Algebraic(Move.getFromIndex(move)));
		sb.append(isCapture(move) ? 'x' : '-');
		sb.append(BitboardUtils.index2Algebraic(Move.getToIndex(move)));
		if (isPromotion(move)) {
			sb.append(PIECE_LETTERS_LOWERCASE.charAt(getPiecePromoted(move)));
		}
		if (isCheck(move)) {
			sb.append("+");
		}
		return sb.toString();
	}

	/**
	 * It does not append + or #
	 *
	 * @param board
	 * @param move
	 * @return
	 */
	public static String toSan(Board board, int move) {
		if (move == Move.NONE) {
			return NONE_STRING;
		} else if (move == Move.NULL) {
			return NULL_STRING;
		}
		board.generateLegalMoves();

		boolean isLegal = false;
		boolean disambiguate = false;
		boolean fileEqual = false;
		boolean rankEqual = false;
		for (int i = 0; i < board.legalMoveCount; i++) {
			int move2 = board.legalMoves[i];
			if (move == move2) {
				isLegal = true;
			} else if (getToIndex(move) == getToIndex(move2) && (getPieceMoved(move) == getPieceMoved(move2))) {
				disambiguate = true;
				if ((getFromIndex(move) % 8) == (getFromIndex(move2) % 8)) {
					fileEqual = true;
				}
				if ((getFromIndex(move) / 8) == (getFromIndex(move2) / 8)) {
					rankEqual = true;
				}
			}
		}
		if (!isLegal) {
			return Move.NONE_STRING;
		} else if (Move.getMoveType(move) == TYPE_KINGSIDE_CASTLING) {
			return Move.isCheck(move) ? "O-O+" : "O-O";
		} else if (Move.getMoveType(move) == TYPE_QUEENSIDE_CASTLING) {
			return Move.isCheck(move) ? "O-O-O+" : "O-O-O";
		}

		StringBuilder sb = new StringBuilder();
		if (getPieceMoved(move) != Piece.PAWN) {
			sb.append(PIECE_LETTERS_UPPERCASE.charAt(getPieceMoved(move)));
		}
		String fromSq = BitboardUtils.index2Algebraic(Move.getFromIndex(move));

		if (isCapture(move) && getPieceMoved(move) == Piece.PAWN) {
			disambiguate = true;
		}

		if (disambiguate) {
			if (fileEqual && rankEqual) {
				sb.append(fromSq);
			} else if (fileEqual) {
				sb.append(fromSq.charAt(1));
			} else {
				sb.append(fromSq.charAt(0));
			}
		}

		if (isCapture(move)) {
			sb.append("x");
		}
		sb.append(BitboardUtils.index2Algebraic(Move.getToIndex(move)));
		if (isPromotion(move)) {
			sb.append(PIECE_LETTERS_UPPERCASE.charAt(getPiecePromoted(move)));
		}
		if (isCheck(move)) {
			sb.append("+");
		}
		return sb.toString();
	}

	public static void printMoves(int moves[], int from, int to) {
		for (int i = from; i < to; i++) {
			System.out.print(Move.toStringExt(moves[i]));
			System.out.print(" ");
		}
		System.out.println();
	}

	public static String sanToFigurines(String in) {
		return in == null ? null : in.replace("N", "♘").replace("B", "♗").replace("R", "♖").replace("Q", "♕").replace("K", "♔");
	}
} public class BitboardUtils {

	// Board borders
	public static final long b_d = 0x00000000000000ffL; // down
	public static final long b_u = 0xff00000000000000L; // up
	public static final long b_r = 0x0101010101010101L; // right
	public static final long b_l = 0x8080808080808080L; // left

	// Board borders (2 squares),for the knight
	public static final long b2_d = 0x000000000000ffffL; // down
	public static final long b2_u = 0xffff000000000000L; // up
	public static final long b2_r = 0x0303030303030303L; // right
	public static final long b2_l = 0xC0C0C0C0C0C0C0C0L; // left

	public static final long A = b_l;
	public static final long B = b_r << 6;
	public static final long C = b_r << 5;
	public static final long D = b_r << 4;
	public static final long E = b_r << 3;
	public static final long F = b_r << 2;
	public static final long G = b_r << 1;
	public static final long H = b_r;

	// 0 is a, 7 is g
	public static final long[] FILE = {A, B, C, D, E, F, G, H};
	public static final long[] FILES_ADJACENT = { //
			FILE[1], //
			FILE[0] | FILE[2], //
			FILE[1] | FILE[3], //
			FILE[2] | FILE[4], //
			FILE[3] | FILE[5], //
			FILE[4] | FILE[6], //
			FILE[5] | FILE[7], //
			FILE[6] //
	};

	public static final long[] FILES_LEFT = { //
			0, //
			FILE[0], //
			FILE[0] | FILE[1], //
			FILE[0] | FILE[1] | FILE[2], //
			FILE[0] | FILE[1] | FILE[2] | FILE[3], //
			FILE[0] | FILE[1] | FILE[2] | FILE[3] | FILE[4], //
			FILE[0] | FILE[1] | FILE[2] | FILE[3] | FILE[4] | FILE[5], //
			FILE[0] | FILE[1] | FILE[2] | FILE[3] | FILE[4] | FILE[5] | FILE[6] //
	};

	public static final long[] FILES_RIGHT = { //
			FILE[1] | FILE[2] | FILE[3] | FILE[4] | FILE[5] | FILE[6] | FILE[7], //
			FILE[2] | FILE[3] | FILE[4] | FILE[5] | FILE[6] | FILE[7], //
			FILE[3] | FILE[4] | FILE[5] | FILE[6] | FILE[7], //
			FILE[4] | FILE[5] | FILE[6] | FILE[7], //
			FILE[5] | FILE[6] | FILE[7], //
			FILE[6] | FILE[7], //
			FILE[7], //
			0 //
	};

	public static final long R1 = b_d;
	public static final long R2 = b_d << 8;
	public static final long R3 = b_d << 16;
	public static final long R4 = b_d << 24;
	public static final long R5 = b_d << 32;
	public static final long R6 = b_d << 40;
	public static final long R7 = b_d << 48;
	public static final long R8 = b_d << 56;

	public static final long[] RANK = {R1, R2, R3, R4, R5, R6, R7, R8}; // 0 is 1, 7 is 8
	public static final long[] RANKS_UPWARDS = { //
			RANK[1] | RANK[2] | RANK[3] | RANK[4] | RANK[5] | RANK[6] | RANK[7], //
			RANK[2] | RANK[3] | RANK[4] | RANK[5] | RANK[6] | RANK[7], //
			RANK[3] | RANK[4] | RANK[5] | RANK[6] | RANK[7], //
			RANK[4] | RANK[5] | RANK[6] | RANK[7], //
			RANK[5] | RANK[6] | RANK[7], //
			RANK[6] | RANK[7], //
			RANK[7], //
			0 //
	};
	public static final long[] RANK_AND_UPWARDS = { //
			RANK[0] | RANK[1] | RANK[2] | RANK[3] | RANK[4] | RANK[5] | RANK[6] | RANK[7], //
			RANK[1] | RANK[2] | RANK[3] | RANK[4] | RANK[5] | RANK[6] | RANK[7], //
			RANK[2] | RANK[3] | RANK[4] | RANK[5] | RANK[6] | RANK[7], //
			RANK[3] | RANK[4] | RANK[5] | RANK[6] | RANK[7], //
			RANK[4] | RANK[5] | RANK[6] | RANK[7], //
			RANK[5] | RANK[6] | RANK[7], //
			RANK[6] | RANK[7], //
			RANK[7], //
	};
	public static final long[] RANKS_DOWNWARDS = { //
			0, //
			RANK[0], //
			RANK[0] | RANK[1], //
			RANK[0] | RANK[1] | RANK[2], //
			RANK[0] | RANK[1] | RANK[2] | RANK[3], //
			RANK[0] | RANK[1] | RANK[2] | RANK[3] | RANK[4], //
			RANK[0] | RANK[1] | RANK[2] | RANK[3] | RANK[4] | RANK[5], //
			RANK[0] | RANK[1] | RANK[2] | RANK[3] | RANK[4] | RANK[5] | RANK[6] //
	};
	public static final long[] RANK_AND_DOWNWARDS = { //
			RANK[0], //
			RANK[0] | RANK[1], //
			RANK[0] | RANK[1] | RANK[2], //
			RANK[0] | RANK[1] | RANK[2] | RANK[3], //
			RANK[0] | RANK[1] | RANK[2] | RANK[3] | RANK[4], //
			RANK[0] | RANK[1] | RANK[2] | RANK[3] | RANK[4] | RANK[5], //
			RANK[0] | RANK[1] | RANK[2] | RANK[3] | RANK[4] | RANK[5] | RANK[6], //
			RANK[0] | RANK[1] | RANK[2] | RANK[3] | RANK[4] | RANK[5] | RANK[6] | RANK[7] //
	};

	// Ranks forward in pawn direction W, B
	public static final long[][] RANKS_FORWARD = {RANKS_UPWARDS, RANKS_DOWNWARDS};
	public static final long[][] RANKS_BACKWARD = {RANKS_DOWNWARDS, RANKS_UPWARDS};
	public static final long[][] RANK_AND_FORWARD = {RANK_AND_UPWARDS, RANK_AND_DOWNWARDS};
	public static final long[][] RANK_AND_BACKWARD = {RANK_AND_DOWNWARDS, RANK_AND_UPWARDS};

	public static final String[] SQUARE_NAMES = changeEndianArray64(new String[] //
			{"a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", //
					"a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7", //
					"a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6", //
					"a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5", //
					"a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4", //
					"a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3", //
					"a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2", //
					"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1"});

	// To use with square2Index
	public static final byte[] BIT_TABLE = {63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2, 51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57,
			0, 35, 62, 31, 40, 4, 49, 5, 52, 26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38, 28, 58, 20, 37, 17, 36, 8};

	/**
	 * Converts a square to its index 0=H1, 63=A8
	 */
	public static byte square2Index(long square) {
		long b = square ^ (square - 1);
		int fold = (int) (b ^ (b >>> 32));
		return BIT_TABLE[(fold * 0x783a9b23) >>> 26];
	}

	/**
	 * And viceversa
	 */
	public static long index2Square(int index) {
		return Square.H1 << index;
	}

	/**
	 * Changes element 0 with 63 and consecuvely: this way array constants are
	 * more legible
	 */
	public static String[] changeEndianArray64(String sarray[]) {
		String out[] = new String[64];
		for (int i = 0; i < 64; i++) {
			out[i] = sarray[63 - i];
		}
		return out;
	}

	public static int[] changeEndianArray64(int sarray[]) {
		int out[] = new int[64];
		for (int i = 0; i < 64; i++) {
			out[i] = sarray[63 - i];
		}
		return out;
	}

	/**
	 * Prints a BitBoard to standard output
	 */
	public static String toString(long b) {
		StringBuilder sb = new StringBuilder();
		long i = Square.A8;
		while (i != 0) {
			sb.append(((b & i) != 0 ? "1 " : "0 "));
			if ((i & b_r) != 0) {
				sb.append("\n");
			}
			i >>>= 1;
		}
		return sb.toString();
	}

	/**
	 * Flips board vertically
	 * https://chessprogramming.wikispaces.com/Flipping+Mirroring+and+Rotating
	 *
	 * @param in
	 * @return
	 */
	public static long flipVertical(long in) {
		final long k1 = 0x00FF00FF00FF00FFL;
		final long k2 = 0x0000FFFF0000FFFFL;
		in = ((in >>> 8) & k1) | ((in & k1) << 8);
		in = ((in >>> 16) & k2) | ((in & k2) << 16);
		in = (in >>> 32) | (in << 32);
		return in;
	}

	public static int flipHorizontalIndex(int index) {
		return (index & 0xF8) | (7 - (index & 7));
	}

	/**
	 * Counts the number of bits of one long
	 * http://chessprogramming.wikispaces.com/Population+Count
	 *
	 * @param x
	 * @return
	 */
	public static int popCount(long x) {
		if (x == 0) {
			return 0;
		}
		final long k1 = 0x5555555555555555L;
		final long k2 = 0x3333333333333333L;
		final long k4 = 0x0f0f0f0f0f0f0f0fL;
		final long kf = 0x0101010101010101L;
		x = x - ((x >> 1) & k1); // put count of each 2 bits into those 2 bits
		x = (x & k2) + ((x >> 2) & k2); // put count of each 4 bits into those 4
		// bits
		x = (x + (x >> 4)) & k4; // put count of each 8 bits into those 8 bits
		x = (x * kf) >> 56; // returns 8 most significant bits of x + (x<<8) +
		// (x<<16) + (x<<24) + ...
		return (int) x;
	}

	/**
	 * Convert a bitboard square to algebraic notation Number depends of rotated
	 * board.
	 *
	 * @param square
	 * @return
	 */
	public static String square2Algebraic(long square) {
		return SQUARE_NAMES[square2Index(square)];
	}

	public static String index2Algebraic(int index) {
		return SQUARE_NAMES[index];
	}

	public static int algebraic2Index(String name) {
		for (int i = 0; i < 64; i++) {
			if (name.equals(SQUARE_NAMES[i])) {
				return i;
			}
		}
		return -1;
	}

	public static long algebraic2Square(String name) {
		long aux = Square.H1;
		for (int i = 0; i < 64; i++) {
			if (name.equals(SQUARE_NAMES[i])) {
				return aux;
			}
			aux <<= 1;
		}
		return 0;
	}

	/**
	 * Gets the file (0..7) for (a..h) of the square
	 *
	 * @param square
	 * @return
	 */
	public static int getFile(long square) {
		for (int file = 0; file < 8; file++) {
			if ((FILE[file] & square) != 0) {
				return file;
			}
		}
		return 0;
	}

	public static int getRankLsb(long square) {
		for (int rank = 0; rank <= 7; rank++) {
			if ((RANK[rank] & square) != 0) {
				return rank;
			}
		}
		return 0;
	}

	public static int getRankMsb(long square) {
		for (int rank = 7; rank >= 0; rank--) {
			if ((RANK[rank] & square) != 0) {
				return rank;
			}
		}
		return 0;
	}

	public static int getFileOfIndex(int index) {
		return 7 - index & 7;
	}

	public static int getRankOfIndex(int index) {
		return index >> 3;
	}

	/**
	 * Gets a long with the less significant bit of the board
	 */
	public static long lsb(long board) {
		return board & (-board);
	}

	public static long msb(long board) {
		board |= board >>> 32;
		board |= board >>> 16;
		board |= board >>> 8;
		board |= board >>> 4;
		board |= board >>> 2;
		board |= board >>> 1;
		return board == 0 ? 0 : (board >>> 1) + 1;
	}

	/**
	 * Distance between two indexes
	 */
	public static int distance(int index1, int index2) {
		return Math.max(Math.abs((index1 & 7) - (index2 & 7)), Math.abs((index1 >> 3) - (index2 >> 3)));
	}

	/**
	 * Gets the horizontal line between two squares (including the origin and destiny squares)
	 * square1 must be to the left of square2 (square1 must be a higher bit)
	 */
	public static long getHorizontalLine(long square1, long square2) {
		return (square1 | (square1 - 1)) & ~(square2 - 1);
	}

	public static boolean isWhiteSquare(long square) {
		return (square & Square.WHITES) != 0;
	}

	public static boolean isBlackSquare(long square) {
		return (square & Square.BLACKS) != 0;
	}

	public static long getSameColorSquares(long square) {
		return (square & Square.WHITES) != 0 ? Square.WHITES : Square.BLACKS;
	}

	public static long frontPawnSpan(long pawn, int color) {
		int index = square2Index(pawn);
		int rank = index >> 3;
		int file = 7 - index & 7;

		return RANKS_FORWARD[color][rank] &
				(FILE[file] | FILES_ADJACENT[file]);
	}

	public static long frontFile(long square, int color) {
		int index = square2Index(square);
		int rank = index >> 3;
		int file = 7 - index & 7;

		return RANKS_FORWARD[color][rank] & FILE[file];
	}

	public static boolean sameRankOrFile(int index1, int index2) {
		return ((index1 >> 3) == (index2 >> 3)) ||
				((index1 & 7) == (index2 & 7));
	}
} public class CompleteEvaluator extends Evaluator {
	private static final Logger logger = Logger.getLogger("CompleteEvaluator");

	// Mobility units: this value is added for the number of destination square not occupied by one of our pieces or attacked by opposite pawns
	private static final int[][] MOBILITY = {
			{}, {},
			{oe(-12, -16), oe(2, 2), oe(5, 7), oe(7, 9), oe(8, 11), oe(10, 13), oe(11, 14), oe(11, 15), oe(12, 16)},
			{oe(-16, -16), oe(-1, -1), oe(3, 3), oe(6, 6), oe(8, 8), oe(9, 9), oe(11, 11), oe(12, 12), oe(13, 13), oe(13, 13), oe(14, 14), oe(15, 15), oe(15, 15), oe(16, 16)},
			{oe(-14, -21), oe(-1, -2), oe(3, 4), oe(5, 7), oe(7, 10), oe(8, 12), oe(9, 13), oe(10, 15), oe(11, 16), oe(11, 17), oe(12, 18), oe(13, 19), oe(13, 20), oe(14, 20), oe(14, 21)},
			{oe(-27, -27), oe(-9, -9), oe(-2, -2), oe(2, 2), oe(5, 5), oe(8, 8), oe(10, 10), oe(12, 12), oe(13, 13), oe(14, 14), oe(16, 16), oe(17, 17), oe(18, 18), oe(19, 19), oe(19, 19), oe(20, 20), oe(21, 21), oe(22, 22), oe(22, 22), oe(23, 23), oe(24, 24), oe(24, 24), oe(25, 25), oe(25, 25), oe(26, 26), oe(26, 26), oe(27, 27), oe(27, 27)}
	};

	// Space
	private static final long WHITE_SPACE_ZONE = (BitboardUtils.C | BitboardUtils.D | BitboardUtils.E | BitboardUtils.F) &
			(BitboardUtils.R2 | BitboardUtils.R3 | BitboardUtils.R4);
	private static final long BLACK_SPACE_ZONE = (BitboardUtils.C | BitboardUtils.D | BitboardUtils.E | BitboardUtils.F) &
			(BitboardUtils.R5 | BitboardUtils.R6 | BitboardUtils.R7);
	private static final int SPACE = oe(2, 0);

	// Attacks
	private static final int[] PAWN_ATTACKS = {0, 0, oe(11, 15), oe(12, 16), oe(17, 23), oe(19, 25), 0};
	private static final int[] MINOR_ATTACKS = {0, oe(3, 5), oe(7, 9), oe(7, 9), oe(10, 14), oe(11, 15), 0}; // Minor piece attacks to pawn undefended pieces
	private static final int[] MAJOR_ATTACKS = {0, oe(2, 2), oe(3, 4), oe(3, 4), oe(5, 6), oe(5, 7), 0}; // Major piece attacks to pawn undefended pieces

	private static final int HUNG_PIECES = oe(16, 25); // Two or more pieces of the other side attacked by inferior pieces
	private static final int PINNED_PIECE = oe(7, 15);

	// Pawns
	// Those are all penalties. Array is {not opposed, opposed}: If not opposed, backwards and isolated pawns can be easily attacked
	private static final int[] PAWN_BACKWARDS = {oe(20, 15), oe(10, 15)}; // Not opposed is worse in the opening
	private static final int[] PAWN_ISOLATED = {oe(20, 20), oe(10, 20)}; // Not opposed is worse in the opening
	private static final int[] PAWN_DOUBLED = {oe(8, 16), oe(10, 20)}; // Not opposed is better, opening is better
	private static final int PAWN_UNSUPPORTED = oe(2, 4); // Not backwards or isolated

	// And now the bonuses. Array by relative rank
	private static final int[] PAWN_CANDIDATE = {0, oe(10, 13), oe(10, 13), oe(14, 18), oe(22, 28), oe(34, 43), oe(50, 63), 0};
	private static final int[] PAWN_PASSER = {0, oe(20, 25), oe(20, 25), oe(28, 35), oe(44, 55), oe(68, 85), oe(100, 125), 0};
	private static final int[] PAWN_PASSER_OUTSIDE = {0, 0, 0, oe(2, 3), oe(7, 9), oe(14, 18), oe(24, 30), 0};
	private static final int[] PAWN_PASSER_CONNECTED = {0, 0, 0, oe(3, 3), oe(8, 8), oe(15, 15), oe(25, 25), 0};
	private static final int[] PAWN_PASSER_SUPPORTED = {0, 0, 0, oe(6, 6), oe(17, 17), oe(33, 33), oe(55, 55), 0};
	private static final int[] PAWN_PASSER_MOBILE = {0, 0, 0, oe(2, 2), oe(6, 6), oe(12, 12), oe(20, 20), 0};
	private static final int[] PAWN_PASSER_RUNNER = {0, 0, 0, oe(6, 6), oe(18, 18), oe(36, 36), oe(60, 60), 0};

	private static final int[] PAWN_PASSER_OTHER_KING_DISTANCE = {0, 0, 0, oe(0, 2), oe(0, 6), oe(0, 12), oe(0, 20), 0};
	private static final int[] PAWN_PASSER_MY_KING_DISTANCE = {0, 0, 0, oe(0, 1), oe(0, 3), oe(0, 6), oe(0, 10), 0};

	private static final int[] PAWN_SHIELD_CENTER = {0, oe(55, 0), oe(41, 0), oe(28, 0), oe(14, 0), 0, 0, 0};
	private static final int[] PAWN_SHIELD = {0, oe(35, 0), oe(26, 0), oe(18, 0), oe(9, 0), 0, 0, 0};
	private static final int[] PAWN_STORM_CENTER = {0, 0, 0, oe(8, 0), oe(15, 0), oe(30, 0), 0, 0};
	private static final int[] PAWN_STORM = {0, 0, 0, oe(5, 0), oe(10, 0), oe(20, 0), 0, 0};

	private static final int PAWN_BLOCKADE = oe(5, 0); // Penalty for pawns in [D,E] in the initial square blocked by our own pieces

	// Knights
	private static final int[] KNIGHT_OUTPOST = {oe(15, 10), oe(22, 15)}; // Array is Not defended by pawn, defended by pawn

	// Bishops
	private static final int[] BISHOP_OUTPOST = {oe(7, 4), oe(10, 7)};
	private static final int BISHOP_MY_PAWNS_IN_COLOR_PENALTY = oe(2, 4); // Penalty for each of my pawns in the bishop color (Capablanca rule)
	private static final int[] BISHOP_TRAPPED_PENALTY = {oe(40, 40), oe(80, 80)}; // By pawn not guarded / guarded
	private static final long[] BISHOP_TRAPPING = { // Indexed by bishop position, contains the square where a pawn can trap the bishop
			0, Square.F2, 0, 0, 0, 0, Square.C2, 0,
			Square.G3, 0, 0, 0, 0, 0, 0, Square.B3,
			Square.G4, 0, 0, 0, 0, 0, 0, Square.B4,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			Square.G5, 0, 0, 0, 0, 0, 0, Square.B5,
			Square.G6, 0, 0, 0, 0, 0, 0, Square.B6,
			0, Square.F7, 0, 0, 0, 0, Square.C7, 0
	};
	private static final long[] BISHOP_TRAPPING_GUARD = { // Indexed by bishop position, contains the square where other pawn defends the trapping pawn
			0, 0, 0, 0, 0, 0, 0, 0,
			Square.F2, 0, 0, 0, 0, 0, 0, Square.C2,
			Square.F3, 0, 0, 0, 0, 0, 0, Square.C3,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			Square.F6, 0, 0, 0, 0, 0, 0, Square.C6,
			Square.F7, 0, 0, 0, 0, 0, 0, Square.C7,
			0, 0, 0, 0, 0, 0, 0, 0
	};

	// Rooks
	private static final int[] ROOK_OUTPOST = {oe(2, 1), oe(3, 2)}; // Array is Not defended by pawn, defended by pawn
	private static final int[] ROOK_FILE = {oe(15, 10), oe(7, 5)}; // Open / Semi open
	private static final int ROOK_7 = oe(7, 10); // Rook 5, 6 or 7th rank attacking a pawn in the same rank not defended by pawn
	private static final int[] ROOK_TRAPPED_PENALTY = {oe(40, 0), oe(30, 0), oe(20, 0), oe(10, 0)}; // Penalty by number of mobility squares
	private static final long[] ROOK_TRAPPING = { // Indexed by own king position, contains the squares where a rook may be traped by the king
			0, Square.H1 | Square.H2, Square.H1 | Square.H2 | Square.G1 | Square.G2, 0,
			0, Square.A1 | Square.A2 | Square.B1 | Square.B2, Square.A1 | Square.A2, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, Square.H7 | Square.H8, Square.H7 | Square.H8 | Square.G7 | Square.G8, 0,
			0, Square.A7 | Square.A8 | Square.B7 | Square.B8, Square.A7 | Square.A8, 0,
	};

	// King
	// Sums for each piece attacking an square near the king
	private static final int PIECE_ATTACKS_KING[] = {0, 0, oe(30, 0), oe(20, 0), oe(40, 0), oe(80, 0)};
	// Ponder kings attacks by the number of attackers (not pawns)
	private static final int[] KING_SAFETY_PONDER = {0, 0, 32, 48, 56, 60, 62, 63, 64, 64, 64, 64, 64, 64, 64, 64};

	// Tempo
	public static final int TEMPO = oe(15, 5); // Add to moving side score

	private static final long[] OUTPOST_MASK = {0x00007e7e7e000000L, 0x0000007e7e7e0000L};

	private static final int pawnPcsq[] = {
			oe(-18, 4), oe(-6, 2), oe(0, 0), oe(6, -2), oe(6, -2), oe(0, 0), oe(-6, 2), oe(-18, 4),
			oe(-21, 1), oe(-9, -1), oe(-3, -3), oe(3, -5), oe(3, -5), oe(-3, -3), oe(-9, -1), oe(-21, 1),
			oe(-20, 1), oe(-8, -1), oe(-2, -3), oe(4, -5), oe(4, -5), oe(-2, -3), oe(-8, -1), oe(-20, 1),
			oe(-19, 2), oe(-7, 0), oe(-1, -2), oe(12, -4), oe(12, -4), oe(-1, -2), oe(-7, 0), oe(-19, 2),
			oe(-17, 3), oe(-5, 1), oe(1, -1), oe(10, -3), oe(10, -3), oe(1, -1), oe(-5, 1), oe(-17, 3),
			oe(-16, 4), oe(-4, 2), oe(2, 0), oe(8, -2), oe(8, -2), oe(2, 0), oe(-4, 2), oe(-16, 4),
			oe(-15, 6), oe(-3, 4), oe(3, 2), oe(9, 0), oe(9, 0), oe(3, 2), oe(-3, 4), oe(-15, 6),
			oe(-18, 4), oe(-6, 2), oe(0, 0), oe(6, -2), oe(6, -2), oe(0, 0), oe(-6, 2), oe(-18, 4)
	};
	private static final int knightPcsq[] = {
			oe(-27, -22), oe(-17, -17), oe(-9, -12), oe(-4, -9), oe(-4, -9), oe(-9, -12), oe(-17, -17), oe(-27, -22),
			oe(-21, -15), oe(-11, -8), oe(-3, -4), oe(2, -2), oe(2, -2), oe(-3, -4), oe(-11, -8), oe(-21, -15),
			oe(-15, -10), oe(-5, -4), oe(3, 1), oe(8, 3), oe(8, 3), oe(3, 1), oe(-5, -4), oe(-15, -10),
			oe(-9, -6), oe(1, -1), oe(9, 4), oe(14, 8), oe(14, 8), oe(9, 4), oe(1, -1), oe(-9, -6),
			oe(-5, -4), oe(5, 1), oe(13, 6), oe(18, 10), oe(18, 10), oe(13, 6), oe(5, 1), oe(-5, -4),
			oe(-6, -4), oe(4, 2), oe(12, 7), oe(17, 9), oe(17, 9), oe(12, 7), oe(4, 2), oe(-6, -4),
			oe(-10, -8), oe(0, -1), oe(8, 3), oe(13, 5), oe(13, 5), oe(8, 3), oe(0, -1), oe(-10, -8),
			oe(-20, -15), oe(-10, -10), oe(-2, -5), oe(3, -2), oe(3, -2), oe(-2, -5), oe(-10, -10), oe(-20, -15)
	};
	private static final int bishopPcsq[] = {
			oe(-7, 0), oe(-8, -1), oe(-11, -2), oe(-13, -2), oe(-13, -2), oe(-11, -2), oe(-8, -1), oe(-7, 0),
			oe(-3, -1), oe(3, 1), oe(0, 0), oe(-2, 0), oe(-2, 0), oe(0, 0), oe(3, 1), oe(-3, -1),
			oe(-6, -2), oe(0, 0), oe(7, 3), oe(6, 2), oe(6, 2), oe(7, 3), oe(0, 0), oe(-6, -2),
			oe(-8, -2), oe(-2, 0), oe(6, 2), oe(15, 5), oe(15, 5), oe(6, 2), oe(-2, 0), oe(-8, -2),
			oe(-8, -2), oe(-2, 0), oe(6, 2), oe(15, 5), oe(15, 5), oe(6, 2), oe(-2, 0), oe(-8, -2),
			oe(-6, -2), oe(0, 0), oe(7, 3), oe(6, 2), oe(6, 2), oe(7, 3), oe(0, 0), oe(-6, -2),
			oe(-3, -1), oe(3, 1), oe(0, 0), oe(-2, 0), oe(-2, 0), oe(0, 0), oe(3, 1), oe(-3, -1),
			oe(-2, 0), oe(-3, -1), oe(-6, -2), oe(-8, -2), oe(-8, -2), oe(-6, -2), oe(-3, -1), oe(-2, 0)
	};
	private static final int rookPcsq[] = {
			oe(-4, 0), oe(0, 0), oe(4, 0), oe(8, 0), oe(8, 0), oe(4, 0), oe(0, 0), oe(-4, 0),
			oe(-4, 0), oe(0, 0), oe(4, 0), oe(8, 0), oe(8, 0), oe(4, 0), oe(0, 0), oe(-4, 0),
			oe(-4, 0), oe(0, 0), oe(4, 0), oe(8, 0), oe(8, 0), oe(4, 0), oe(0, 0), oe(-4, 0),
			oe(-4, 0), oe(0, 0), oe(4, 0), oe(8, 0), oe(8, 0), oe(4, 0), oe(0, 0), oe(-4, 0),
			oe(-4, 1), oe(0, 1), oe(4, 1), oe(8, 1), oe(8, 1), oe(4, 1), oe(0, 1), oe(-4, 1),
			oe(-4, 3), oe(0, 3), oe(4, 3), oe(8, 3), oe(8, 3), oe(4, 3), oe(0, 3), oe(-4, 3),
			oe(-4, 5), oe(0, 5), oe(4, 5), oe(8, 5), oe(8, 5), oe(4, 5), oe(0, 5), oe(-4, 5),
			oe(-4, -2), oe(0, -2), oe(4, -2), oe(8, -2), oe(8, -2), oe(4, -2), oe(0, -2), oe(-4, -2)
	};
	private static final int queenPcsq[] = {
			oe(-9, -15), oe(-6, -10), oe(-4, -8), oe(-2, -7), oe(-2, -7), oe(-4, -8), oe(-6, -10), oe(-9, -15),
			oe(-6, -10), oe(-1, -5), oe(1, -3), oe(3, -2), oe(3, -2), oe(1, -3), oe(-1, -5), oe(-6, -10),
			oe(-4, -8), oe(1, -3), oe(5, 0), oe(6, 2), oe(6, 2), oe(5, 0), oe(1, -3), oe(-4, -8),
			oe(-2, -7), oe(3, -2), oe(6, 2), oe(9, 5), oe(9, 5), oe(6, 2), oe(3, -2), oe(-2, -7),
			oe(-2, -7), oe(3, -2), oe(6, 2), oe(9, 5), oe(9, 5), oe(6, 2), oe(3, -2), oe(-2, -7),
			oe(-4, -8), oe(1, -3), oe(5, 0), oe(6, 2), oe(6, 2), oe(5, 0), oe(1, -3), oe(-4, -8),
			oe(-6, -10), oe(-1, -5), oe(1, -3), oe(3, -2), oe(3, -2), oe(1, -3), oe(-1, -5), oe(-6, -10),
			oe(-9, -15), oe(-6, -10), oe(-4, -8), oe(-2, -7), oe(-2, -7), oe(-4, -8), oe(-6, -10), oe(-9, -15)
	};
	private static final int kingPcsq[] = {
			oe(34, -58), oe(39, -35), oe(14, -19), oe(-6, -13), oe(-6, -13), oe(14, -19), oe(39, -35), oe(34, -58),
			oe(31, -35), oe(36, -10), oe(11, 2), oe(-9, 8), oe(-9, 8), oe(11, 2), oe(36, -10), oe(31, -35),
			oe(28, -19), oe(33, 2), oe(8, 17), oe(-12, 23), oe(-12, 23), oe(8, 17), oe(33, 2), oe(28, -19),
			oe(25, -13), oe(30, 8), oe(5, 23), oe(-15, 32), oe(-15, 32), oe(5, 23), oe(30, 8), oe(25, -13),
			oe(20, -13), oe(25, 8), oe(0, 23), oe(-20, 32), oe(-20, 32), oe(0, 23), oe(25, 8), oe(20, -13),
			oe(15, -19), oe(20, 2), oe(-5, 17), oe(-25, 23), oe(-25, 23), oe(-5, 17), oe(20, 2), oe(15, -19),
			oe(5, -35), oe(10, -10), oe(-15, 2), oe(-35, 8), oe(-35, 8), oe(-15, 2), oe(10, -10), oe(5, -35),
			oe(-5, -58), oe(0, -35), oe(-25, -19), oe(-45, -13), oe(-45, -13), oe(-25, -19), oe(0, -35), oe(-5, -58)
	};

	public boolean debug = false;
	public boolean debugPawns = false;
	public StringBuffer debugSB;

	private int[] scaleFactor = {0};

	private int[] pawnMaterial = {0, 0};
	private int[] nonPawnMaterial = {0, 0};
	private int[] pcsq = {0, 0};
	private int[] space = {0, 0};
	private int[] positional = {0, 0};
	private int[] mobility = {0, 0};
	private int[] attacks = {0, 0};
	private int[] kingAttackersCount = {0, 0};
	private int[] kingSafety = {0, 0};
	private int[] pawnStructure = {0, 0};
	private int[] passedPawns = {0, 0};
	private long[] pawnCanAttack = {0, 0};
	private long[] mobilitySquares = {0, 0};
	private long[] kingZone = {0, 0}; // Squares surrounding King

	public int evaluate(Board board, AttacksInfo ai) {
		if (debug) {
			debugSB = new StringBuffer();
			debugSB.append("\n");
			debugSB.append(board.toString());
			debugSB.append("\n");
		}

		int whitePawns = BitboardUtils.popCount(board.pawns & board.whites);
		int blackPawns = BitboardUtils.popCount(board.pawns & board.blacks);
		int whiteKnights = BitboardUtils.popCount(board.knights & board.whites);
		int blackKnights = BitboardUtils.popCount(board.knights & board.blacks);
		int whiteBishops = BitboardUtils.popCount(board.bishops & board.whites);
		int blackBishops = BitboardUtils.popCount(board.bishops & board.blacks);
		int whiteRooks = BitboardUtils.popCount(board.rooks & board.whites);
		int blackRooks = BitboardUtils.popCount(board.rooks & board.blacks);
		int whiteQueens = BitboardUtils.popCount(board.queens & board.whites);
		int blackQueens = BitboardUtils.popCount(board.queens & board.blacks);

		int endgameValue = Endgame.evaluateEndgame(board, scaleFactor, whitePawns, blackPawns, whiteKnights, blackKnights, whiteBishops, blackBishops, whiteRooks, blackRooks, whiteQueens, blackQueens);
		if (endgameValue != NO_VALUE) {
			return endgameValue;
		}

		pawnMaterial[W] = whitePawns * PIECE_VALUES_OE[Piece.PAWN];
		nonPawnMaterial[W] = whiteKnights * PIECE_VALUES_OE[Piece.KNIGHT] +
				whiteBishops * PIECE_VALUES_OE[Piece.BISHOP] +
				whiteRooks * PIECE_VALUES_OE[Piece.ROOK] +
				whiteQueens * PIECE_VALUES_OE[Piece.QUEEN] +
				((board.whites & board.bishops & Square.WHITES) != 0
						&& (board.whites & board.bishops & Square.BLACKS) != 0 ? BISHOP_PAIR : 0);
		pawnMaterial[B] = blackPawns * PIECE_VALUES_OE[Piece.PAWN];
		nonPawnMaterial[B] = blackKnights * PIECE_VALUES_OE[Piece.KNIGHT] +
				blackBishops * PIECE_VALUES_OE[Piece.BISHOP] +
				blackRooks * PIECE_VALUES_OE[Piece.ROOK] +
				blackQueens * PIECE_VALUES_OE[Piece.QUEEN] +
				((board.blacks & board.bishops & Square.WHITES) != 0
						&& (board.blacks & board.bishops & Square.BLACKS) != 0 ? BISHOP_PAIR : 0);

		int nonPawnMat = e(nonPawnMaterial[W] + nonPawnMaterial[B]);
		int gamePhase = nonPawnMat >= NON_PAWN_MATERIAL_MIDGAME_MAX ? GAME_PHASE_MIDGAME :
				nonPawnMat <= NON_PAWN_MATERIAL_ENDGAME_MIN ? GAME_PHASE_ENDGAME :
						((nonPawnMat - NON_PAWN_MATERIAL_ENDGAME_MIN) * GAME_PHASE_MIDGAME) / (NON_PAWN_MATERIAL_MIDGAME_MAX - NON_PAWN_MATERIAL_ENDGAME_MIN);

		pcsq[W] = 0;
		pcsq[B] = 0;
		positional[W] = 0;
		positional[B] = 0;
		mobility[W] = 0;
		mobility[B] = 0;
		kingAttackersCount[W] = 0;
		kingAttackersCount[B] = 0;
		kingSafety[W] = 0;
		kingSafety[B] = 0;
		pawnStructure[W] = 0;
		pawnStructure[B] = 0;
		passedPawns[W] = 0;
		passedPawns[B] = 0;

		mobilitySquares[W] = ~board.whites;
		mobilitySquares[B] = ~board.blacks;

		ai.build(board);

		long whitePawnsAux = board.pawns & board.whites;
		long blackPawnsAux = board.pawns & board.blacks;

		// Space evaluation
		if (gamePhase > 0) {
			long whiteSafe = WHITE_SPACE_ZONE & ~ai.pawnAttacks[B] & (~ai.attackedSquares[B] | ai.attackedSquares[W]);
			long blackSafe = BLACK_SPACE_ZONE & ~ai.pawnAttacks[W] & (~ai.attackedSquares[W] | ai.attackedSquares[B]);

			long whiteBehindPawn = ((whitePawnsAux >>> 8) | (whitePawnsAux >>> 16) | (whitePawnsAux >>> 24));
			long blackBehindPawn = ((blackPawnsAux << 8) | (blackPawnsAux << 16) | (blackPawnsAux << 24));

			space[W] = SPACE * (((BitboardUtils.popCount(whiteSafe) + BitboardUtils.popCount(whiteSafe & whiteBehindPawn)) *
					(whiteKnights + whiteBishops)) / 4);
			space[B] = SPACE * (((BitboardUtils.popCount(blackSafe) + BitboardUtils.popCount(blackSafe & blackBehindPawn)) *
					(blackKnights + blackBishops)) / 4);
		} else {
			space[W] = 0;
			space[B] = 0;
		}

		// Squares that pawns attack or can attack by advancing
		pawnCanAttack[W] = ai.pawnAttacks[W];
		pawnCanAttack[B] = ai.pawnAttacks[B];
		for (int i = 0; i < 5; i++) {
			whitePawnsAux = whitePawnsAux << 8;
			whitePawnsAux &= ~((board.pawns & board.blacks) | ai.pawnAttacks[B]); // Cannot advance because of a blocking pawn or a opposite pawn attack
			blackPawnsAux = blackPawnsAux >>> 8;
			blackPawnsAux &= ~((board.pawns & board.whites) | ai.pawnAttacks[W]); // Cannot advance because of a blocking pawn or a opposite pawn attack

			if (whitePawnsAux == 0 && blackPawnsAux == 0) {
				break;
			}
			pawnCanAttack[W] |= ((whitePawnsAux & ~BitboardUtils.b_l) << 9) | ((whitePawnsAux & ~BitboardUtils.b_r) << 7);
			pawnCanAttack[B] |= ((blackPawnsAux & ~BitboardUtils.b_r) >>> 9) | ((blackPawnsAux & ~BitboardUtils.b_l) >>> 7);
		}

		// Calculate attacks
		attacks[W] = evalAttacks(board, ai, W, board.blacks);
		attacks[B] = evalAttacks(board, ai, B, board.whites);

		// Squares surrounding King and three squares towards thew other side
		kingZone[W] = bbAttacks.king[ai.kingIndex[W]];
		kingZone[W] |= (kingZone[W] << 8);
		kingZone[B] = bbAttacks.king[ai.kingIndex[B]];
		kingZone[B] |= (kingZone[B] >>> 8);

		long all = board.getAll();
		long pieceAttacks, safeAttacks, kingAttacks;

		long square = 1;
		for (int index = 0; index < 64; index++) {
			if ((square & all) != 0) {
				boolean isWhite = ((board.whites & square) != 0);
				int us = (isWhite ? W : B);
				int them = (isWhite ? B : W);
				long mines = (isWhite ? board.whites : board.blacks);
				long others = (isWhite ? board.blacks : board.whites);
				int pcsqIndex = (isWhite ? index : 63 - index);
				int rank = index >> 3;
				int relativeRank = isWhite ? rank : 7 - rank;
				int file = 7 - index & 7;

				pieceAttacks = ai.attacksFromSquare[index];

				if ((square & board.pawns) != 0) {
					pcsq[us] += pawnPcsq[pcsqIndex];

					long myPawns = board.pawns & mines;
					long otherPawns = board.pawns & others;
					long adjacentFiles = BitboardUtils.FILES_ADJACENT[file];
					long ranksForward = BitboardUtils.RANKS_FORWARD[us][rank];
					long pawnFile = BitboardUtils.FILE[file];
					long routeToPromotion = pawnFile & ranksForward;
					long otherPawnsAheadAdjacent = ranksForward & adjacentFiles & otherPawns;
					long pushSquare = isWhite ? square << 8 : square >>> 8;

					boolean supported = (square & ai.pawnAttacks[us]) != 0;
					boolean doubled = (myPawns & routeToPromotion) != 0;
					boolean opposed = (otherPawns & routeToPromotion) != 0;
					boolean passed = !doubled
							&& !opposed
							&& otherPawnsAheadAdjacent == 0;

					if (!passed) {
						long myPawnsAheadAdjacent = ranksForward & adjacentFiles & myPawns;
						long myPawnsBesideAndBehindAdjacent = BitboardUtils.RANK_AND_BACKWARD[us][rank] & adjacentFiles & myPawns;
						boolean isolated = (myPawns & adjacentFiles) == 0;
						boolean candidate = !doubled
								&& !opposed
								&& (((otherPawnsAheadAdjacent & ~pieceAttacks) == 0) || // Can become passer advancing
								(BitboardUtils.popCount(myPawnsBesideAndBehindAdjacent) >= BitboardUtils.popCount(otherPawnsAheadAdjacent & ~pieceAttacks))); // Has more friend pawns beside and behind than opposed pawns controlling his route to promotion
						boolean backward = !isolated
								&& !candidate
								&& myPawnsBesideAndBehindAdjacent == 0
								&& (pieceAttacks & otherPawns) == 0 // No backwards if it can capture
								&& (BitboardUtils.RANK_AND_BACKWARD[us][isWhite ? BitboardUtils.getRankLsb(myPawnsAheadAdjacent) : BitboardUtils.getRankMsb(myPawnsAheadAdjacent)] &
								routeToPromotion & (board.pawns | ai.pawnAttacks[them])) != 0; // Other pawns stopping it from advance, opposing or capturing it before reaching my pawns

						if (debugPawns) {
							boolean connected = ((bbAttacks.king[index] & adjacentFiles & myPawns) != 0);
							debugSB.append("PAWN " + BitboardUtils.SQUARE_NAMES[index] +
									(isWhite ? " WHITE " : " BLACK ") +
									(isolated ? "isolated " : "") +
									(supported ? "supported " : "") +
									(connected ? "connected " : "") +
									(doubled ? "doubled " : "") +
									(opposed ? "opposed " : "") +
									(candidate ? "candidate " : "") +
									(backward ? "backward " : "") +
									"\n"
							);
						}

						if (backward) {
							pawnStructure[us] -= PAWN_BACKWARDS[opposed ? 1 : 0];
						}
						if (isolated) {
							pawnStructure[us] -= PAWN_ISOLATED[opposed ? 1 : 0];
						}
						if (doubled) {
							pawnStructure[us] -= PAWN_DOUBLED[opposed ? 1 : 0];
						}
						if (!supported
								&& !isolated
								&& !backward) {
							pawnStructure[us] -= PAWN_UNSUPPORTED;
						}
						if (candidate) {
							passedPawns[us] += PAWN_CANDIDATE[relativeRank];
						}

						if ((square & (BitboardUtils.D | BitboardUtils.E)) != 0
								&& relativeRank == 1
								&& (pushSquare & mines & ~board.pawns) != 0) {
							pawnStructure[us] -= PAWN_BLOCKADE;
						}

						// Pawn Storm: It can open a file near the other king
						if (gamePhase > 0 && relativeRank > 2) {
							// Only if in kingside or queenside
							long stormedPawns = otherPawnsAheadAdjacent & ~BitboardUtils.D & ~BitboardUtils.E;
							if (stormedPawns != 0) {
								// The stormed pawn must be in the other king's adjacent files
								int otherKingFile = 7 - ai.kingIndex[them] & 7;
								if ((stormedPawns & BitboardUtils.FILE[otherKingFile]) != 0) {
									pawnStructure[us] += PAWN_STORM_CENTER[relativeRank];
								} else if ((stormedPawns & BitboardUtils.FILES_ADJACENT[otherKingFile]) != 0) {
									pawnStructure[us] += PAWN_STORM[relativeRank];
								}
							}
						}
					} else {
						//
						// Passed Pawn
						//
						// Backfile only to the first piece found
						long backFile = bbAttacks.getRookAttacks(index, all) & pawnFile & BitboardUtils.RANKS_BACKWARD[us][rank];
						// If it has a rook or queen behind consider all the route to promotion attacked or defended
						long attackedAndNotDefendedRoute =
								((routeToPromotion & ai.attackedSquares[them]) | ((backFile & (board.rooks | board.queens) & others) != 0 ? routeToPromotion : 0)) &
										~((routeToPromotion & ai.attackedSquares[us]) | ((backFile & (board.rooks | board.queens) & mines) != 0 ? routeToPromotion : 0));
						boolean connected = (bbAttacks.king[index] & adjacentFiles & myPawns) != 0;
						boolean outside = otherPawns != 0
								&& (((square & BitboardUtils.FILES_LEFT[3]) != 0 && (board.pawns & BitboardUtils.FILES_LEFT[file]) == 0)
								|| ((square & BitboardUtils.FILES_RIGHT[4]) != 0 && (board.pawns & BitboardUtils.FILES_RIGHT[file]) == 0));
						boolean mobile = (pushSquare & (all | attackedAndNotDefendedRoute)) == 0;
						boolean runner = mobile
								&& (routeToPromotion & all) == 0
								&& attackedAndNotDefendedRoute == 0;

						if (debug) {
							debugSB.append("PAWN " + BitboardUtils.SQUARE_NAMES[index] +
									(isWhite ? " WHITE " : " BLACK ") +
									"passed " +
									(outside ? "outside " : "") +
									(connected ? "connected " : "") +
									(supported ? "supported " : "") +
									(mobile ? "mobile " : "") +
									(runner ? "runner " : "") +
									"\n"
							);
						}

						passedPawns[us] += PAWN_PASSER[relativeRank];

						if (relativeRank >= 2) {
							int pushIndex = isWhite ? index + 8 : index - 8;
							passedPawns[us] += BitboardUtils.distance(pushIndex, ai.kingIndex[them]) * PAWN_PASSER_OTHER_KING_DISTANCE[relativeRank]
									- BitboardUtils.distance(pushIndex, ai.kingIndex[us]) * PAWN_PASSER_MY_KING_DISTANCE[relativeRank];
						}
						if (outside) {
							passedPawns[us] += PAWN_PASSER_OUTSIDE[relativeRank];
						}
						if (supported) {
							passedPawns[us] += PAWN_PASSER_SUPPORTED[relativeRank];
						} else if (connected) {
							passedPawns[us] += PAWN_PASSER_CONNECTED[relativeRank];
						}
						if (runner) {
							passedPawns[us] += PAWN_PASSER_RUNNER[relativeRank];
						} else if (mobile) {
							passedPawns[us] += PAWN_PASSER_MOBILE[relativeRank];
						}
					}
					// Pawn is part of the king shield
					if (gamePhase > 0
							&& (pawnFile & ~ranksForward & kingZone[us] & ~BitboardUtils.D & ~BitboardUtils.E) != 0) { // Pawn in the kingzone
						pawnStructure[us] += (pawnFile & board.kings & mines) != 0 ?
								PAWN_SHIELD_CENTER[relativeRank] :
								PAWN_SHIELD[relativeRank];
					}

				} else if ((square & board.knights) != 0) {
					pcsq[us] += knightPcsq[pcsqIndex];

					safeAttacks = pieceAttacks & ~ai.pawnAttacks[them];

					mobility[us] += MOBILITY[Piece.KNIGHT][BitboardUtils.popCount(safeAttacks & mobilitySquares[us])];

					kingAttacks = safeAttacks & kingZone[them];
					if (kingAttacks != 0) {
						kingSafety[us] += PIECE_ATTACKS_KING[Piece.KNIGHT] * BitboardUtils.popCount(kingAttacks);
						kingAttackersCount[us]++;
					}

					// Knight outpost: no opposite pawns can attack the square
					if ((square & OUTPOST_MASK[us] & ~pawnCanAttack[them]) != 0) {
						positional[us] += KNIGHT_OUTPOST[(square & ai.pawnAttacks[us]) != 0 ? 1 : 0];
					}

				} else if ((square & board.bishops) != 0) {
					pcsq[us] += bishopPcsq[pcsqIndex];

					safeAttacks = pieceAttacks & ~ai.pawnAttacks[them];

					mobility[us] += MOBILITY[Piece.BISHOP][BitboardUtils.popCount(safeAttacks & mobilitySquares[us])];

					kingAttacks = safeAttacks & kingZone[them];
					if (kingAttacks != 0) {
						kingSafety[us] += PIECE_ATTACKS_KING[Piece.BISHOP] * BitboardUtils.popCount(kingAttacks);
						kingAttackersCount[us]++;
					}

					// Bishop Outpost
					if ((square & OUTPOST_MASK[us] & ~pawnCanAttack[them]) != 0) {
						positional[us] += BISHOP_OUTPOST[(square & ai.pawnAttacks[us]) != 0 ? 1 : 0];
					}

					positional[us] -= BISHOP_MY_PAWNS_IN_COLOR_PENALTY * BitboardUtils.popCount(board.pawns & mines & BitboardUtils.getSameColorSquares(square));

					if ((BISHOP_TRAPPING[index] & board.pawns & others) != 0) {
						mobility[us] -= BISHOP_TRAPPED_PENALTY[(BISHOP_TRAPPING_GUARD[index] & board.pawns & others) != 0 ? 1 : 0];
					}

				} else if ((square & board.rooks) != 0) {
					pcsq[us] += rookPcsq[pcsqIndex];

					safeAttacks = pieceAttacks & ~ai.pawnAttacks[them] & ~ai.knightAttacks[them] & ~ai.bishopAttacks[them];

					int mobilityCount = BitboardUtils.popCount(safeAttacks & mobilitySquares[us]);
					mobility[us] += MOBILITY[Piece.ROOK][mobilityCount];

					kingAttacks = safeAttacks & kingZone[them];
					if (kingAttacks != 0) {
						kingSafety[us] += PIECE_ATTACKS_KING[Piece.ROOK] * BitboardUtils.popCount(kingAttacks);
						kingAttackersCount[us]++;
					}

					if ((square & OUTPOST_MASK[us] & ~pawnCanAttack[them]) != 0) {
						positional[us] += ROOK_OUTPOST[(square & ai.pawnAttacks[us]) != 0 ? 1 : 0];
					}

					long rookFile = BitboardUtils.FILE[file];
					if ((rookFile & board.pawns & mines) == 0) {
						positional[us] += ROOK_FILE[(rookFile & board.pawns) == 0 ? 0 : 1];
					}

					if (relativeRank >= 4) {
						long pawnsAligned = BitboardUtils.RANK[rank] & board.pawns & others;
						if (pawnsAligned != 0) {
							positional[us] += ROOK_7 * BitboardUtils.popCount(pawnsAligned);
						}
					}

					// Rook trapped by king
					if ((square & ROOK_TRAPPING[ai.kingIndex[us]]) != 0
							&& mobilityCount < ROOK_TRAPPED_PENALTY.length) {
						positional[us] -= ROOK_TRAPPED_PENALTY[mobilityCount];
					}

				} else if ((square & board.queens) != 0) {
					pcsq[us] += queenPcsq[pcsqIndex];

					safeAttacks = pieceAttacks & ~ai.pawnAttacks[them] & ~ai.knightAttacks[them] & ~ai.bishopAttacks[them] & ~ai.rookAttacks[them];

					mobility[us] += MOBILITY[Piece.QUEEN][BitboardUtils.popCount(safeAttacks & mobilitySquares[us])];

					kingAttacks = safeAttacks & kingZone[them];
					if (kingAttacks != 0) {
						kingSafety[us] += PIECE_ATTACKS_KING[Piece.QUEEN] * BitboardUtils.popCount(kingAttacks);
						kingAttackersCount[us]++;
					}

				} else if ((square & board.kings) != 0) {
					pcsq[us] += kingPcsq[pcsqIndex];
				}
			}
			square <<= 1;
		}

		int oe = (board.getTurn() ? TEMPO : -TEMPO)
				+ pawnMaterial[W] - pawnMaterial[B]
				+ nonPawnMaterial[W] - nonPawnMaterial[B]
				+ pcsq[W] - pcsq[B]
				+ space[W] - space[B]
				+ positional[W] - positional[B]
				+ attacks[W] - attacks[B]
				+ mobility[W] - mobility[B]
				+ pawnStructure[W] - pawnStructure[B]
				+ passedPawns[W] - passedPawns[B]
				+ oeShr(6, KING_SAFETY_PONDER[kingAttackersCount[W]] * kingSafety[W] - KING_SAFETY_PONDER[kingAttackersCount[B]] * kingSafety[B]);

		// Ponder opening and Endgame value depending of the game phase and the scale factor
		int value = (gamePhase * o(oe)
				+ (GAME_PHASE_MIDGAME - gamePhase) * e(oe) * scaleFactor[0] / Endgame.SCALE_FACTOR_DEFAULT) / GAME_PHASE_MIDGAME;

		if (debug) {
			logger.debug(debugSB);
			logger.debug("                    WOpening WEndgame BOpening BEndgame");
			logger.debug("pawnMaterial      = " + formatOE(pawnMaterial[W]) + " " + formatOE(pawnMaterial[B]));
			logger.debug("nonPawnMaterial   = " + formatOE(nonPawnMaterial[W]) + " " + formatOE(nonPawnMaterial[B]));
			logger.debug("pcsq              = " + formatOE(pcsq[W]) + " " + formatOE(pcsq[B]));
			logger.debug("space             = " + formatOE(space[W]) + " " + formatOE(space[B]));
			logger.debug("mobility          = " + formatOE(mobility[W]) + " " + formatOE(mobility[B]));
			logger.debug("positional        = " + formatOE(positional[W]) + " " + formatOE(positional[B]));
			logger.debug("pawnStructure     = " + formatOE(pawnStructure[W]) + " " + formatOE(pawnStructure[B]));
			logger.debug("passedPawns       = " + formatOE(passedPawns[W]) + " " + formatOE(passedPawns[B]));
			logger.debug("attacks           = " + formatOE(attacks[W]) + " " + formatOE(attacks[B]));
			logger.debug("kingSafety        = " + formatOE(oeShr(6, KING_SAFETY_PONDER[kingAttackersCount[W]] * kingSafety[W])) + " " + formatOE(oeShr(6, KING_SAFETY_PONDER[kingAttackersCount[B]] * kingSafety[B])));
			logger.debug("tempo             = " + formatOE(board.getTurn() ? TEMPO : -TEMPO));
			logger.debug("                    -----------------");
			logger.debug("TOTAL:              " + formatOE(oe));
			logger.debug("gamePhase = " + gamePhase + " => value = " + value);
		}
		assert Math.abs(value) < KNOWN_WIN : "Eval is outside limits";
		return value;
	}

	private int evalAttacks(Board board, AttacksInfo ai, int us, long others) {
		int attacks = 0;

		long attackedByPawn = ai.pawnAttacks[us] & others & ~board.pawns;
		while (attackedByPawn != 0) {
			long lsb = BitboardUtils.lsb(attackedByPawn);
			attacks += PAWN_ATTACKS[board.getPieceIntAt(lsb)];
			attackedByPawn &= ~lsb;
		}

		long otherWeak = ai.attackedSquares[us] & others & ~ai.pawnAttacks[1 - us];
		if (otherWeak != 0) {
			long attackedByMinor = (ai.knightAttacks[us] | ai.bishopAttacks[us]) & otherWeak;
			while (attackedByMinor != 0) {
				long lsb = BitboardUtils.lsb(attackedByMinor);
				attacks += MINOR_ATTACKS[board.getPieceIntAt(lsb)];
				attackedByMinor &= ~lsb;
			}
			long attackedByMajor = (ai.rookAttacks[us] | ai.queenAttacks[us]) & otherWeak;
			while (attackedByMajor != 0) {
				long lsb = BitboardUtils.lsb(attackedByMajor);
				attacks += MAJOR_ATTACKS[board.getPieceIntAt(lsb)];
				attackedByMajor &= ~lsb;
			}
		}

		long superiorAttacks = ai.pawnAttacks[us] & others & ~board.pawns
				| (ai.knightAttacks[us] | ai.bishopAttacks[us]) & others & (board.rooks | board.queens)
				| ai.rookAttacks[us] & others & board.queens;
		int superiorAttacksCount = BitboardUtils.popCount(superiorAttacks);
		if (superiorAttacksCount >= 2) {
			attacks += superiorAttacksCount * HUNG_PIECES;
		}

		long pinnedNotPawn = ai.pinnedPieces & ~board.pawns & others;
		if (pinnedNotPawn != 0) {
			attacks += PINNED_PIECE * BitboardUtils.popCount(pinnedNotPawn);
		}
		return attacks;
	}
} public abstract class Evaluator {
	public static final int W = Color.W;
	public static final int B = Color.B;

	public static final int NO_VALUE = Short.MAX_VALUE;
	public static final int MATE = 30000;
	public static final int KNOWN_WIN = 20000;
	public static final int DRAW = 0;

	public static final int PAWN_OPENING = 80;
	public static final int PAWN = 100;
	public static final int KNIGHT = 325;
	public static final int BISHOP = 325;
	public static final int ROOK = 500;
	public static final int QUEEN = 975;
	public static final int[] PIECE_VALUES = {0, PAWN, KNIGHT, BISHOP, ROOK, QUEEN};
	public static final int[] PIECE_VALUES_OE = {0, oe(PAWN_OPENING, PAWN), oe(KNIGHT, KNIGHT), oe(BISHOP, BISHOP), oe(ROOK, ROOK), oe(QUEEN, QUEEN)};
	public static final int BISHOP_PAIR = oe(50, 50); // Bonus by having two bishops in different colors

	public static final int GAME_PHASE_MIDGAME = 1000;
	public static final int GAME_PHASE_ENDGAME = 0;
	public static final int NON_PAWN_MATERIAL_ENDGAME_MIN = QUEEN + ROOK;
	public static final int NON_PAWN_MATERIAL_MIDGAME_MAX = 3 * KNIGHT + 3 * BISHOP + 4 * ROOK + 2 * QUEEN;

	public BitboardAttacks bbAttacks;

	public Evaluator() {
		bbAttacks = BitboardAttacks.getInstance();
	}

	/**
	 * Board evaluator
	 */
	public abstract int evaluate(Board board, AttacksInfo attacksInfo);

	/**
	 * Merges two short Opening - Ending values in one int
	 */
	public static int oe(int opening, int endgame) {
		return (opening << 16) + endgame;
	}

	/**
	 * Get the "Opening" part
	 */
	public static int o(int oe) {
		return (oe + 0x8000) >> 16;
	}

	/**
	 * Get the "Endgame" part
	 */
	public static int e(int oe) {
		return (short) (oe & 0xffff);
	}

	/**
	 * Shift right each part by factor positions
	 */
	public static int oeShr(int factor, int oeValue) {
		return oe(o(oeValue) >> factor, e(oeValue) >> factor);
	}

	String formatOE(int value) {
		return StringUtils.padLeft(String.valueOf(o(value)), 8) + " " + StringUtils.padLeft(String.valueOf(e(value)), 8);
	}
} public class ExperimentalEvaluator extends Evaluator {
	private static final Logger logger = Logger.getLogger("ExperimentalEvaluator");

	// Mobility units: this value is added for the number of destination square not occupied by one of our pieces or attacked by opposite pawns
	private static final int[][] MOBILITY = {
			{}, {},
			{oe(-12, -16), oe(2, 2), oe(5, 7), oe(7, 9), oe(8, 11), oe(10, 13), oe(11, 14), oe(11, 15), oe(12, 16)},
			{oe(-16, -16), oe(-1, -1), oe(3, 3), oe(6, 6), oe(8, 8), oe(9, 9), oe(11, 11), oe(12, 12), oe(13, 13), oe(13, 13), oe(14, 14), oe(15, 15), oe(15, 15), oe(16, 16)},
			{oe(-14, -21), oe(-1, -2), oe(3, 4), oe(5, 7), oe(7, 10), oe(8, 12), oe(9, 13), oe(10, 15), oe(11, 16), oe(11, 17), oe(12, 18), oe(13, 19), oe(13, 20), oe(14, 20), oe(14, 21)},
			{oe(-27, -27), oe(-9, -9), oe(-2, -2), oe(2, 2), oe(5, 5), oe(8, 8), oe(10, 10), oe(12, 12), oe(13, 13), oe(14, 14), oe(16, 16), oe(17, 17), oe(18, 18), oe(19, 19), oe(19, 19), oe(20, 20), oe(21, 21), oe(22, 22), oe(22, 22), oe(23, 23), oe(24, 24), oe(24, 24), oe(25, 25), oe(25, 25), oe(26, 26), oe(26, 26), oe(27, 27), oe(27, 27)}
	};

	// Space
	private static final long WHITE_SPACE_ZONE = (BitboardUtils.C | BitboardUtils.D | BitboardUtils.E | BitboardUtils.F) &
			(BitboardUtils.R2 | BitboardUtils.R3 | BitboardUtils.R4);
	private static final long BLACK_SPACE_ZONE = (BitboardUtils.C | BitboardUtils.D | BitboardUtils.E | BitboardUtils.F) &
			(BitboardUtils.R5 | BitboardUtils.R6 | BitboardUtils.R7);
	private static final int SPACE = oe(2, 0);

	// Attacks
	private static final int[] PAWN_ATTACKS = {0, 0, oe(11, 15), oe(12, 16), oe(17, 23), oe(19, 25), 0};
	private static final int[] MINOR_ATTACKS = {0, oe(3, 5), oe(7, 9), oe(7, 9), oe(10, 14), oe(11, 15), 0}; // Minor piece attacks to pawn undefended pieces
	private static final int[] MAJOR_ATTACKS = {0, oe(2, 2), oe(3, 4), oe(3, 4), oe(5, 6), oe(5, 7), 0}; // Major piece attacks to pawn undefended pieces

	private static final int HUNG_PIECES = oe(16, 25); // Two or more pieces of the other side attacked by inferior pieces
	private static final int PINNED_PIECE = oe(25, 35);

	// Pawns
	// Those are all penalties. Array is {not opposed, opposed}: If not opposed, backwards and isolated pawns can be easily attacked
	private static final int[] PAWN_BACKWARDS = {oe(20, 15), oe(10, 15)}; // Not opposed is worse in the opening
	private static final int[] PAWN_ISOLATED = {oe(20, 20), oe(10, 20)}; // Not opposed is worse in the opening
	private static final int[] PAWN_DOUBLED = {oe(8, 16), oe(10, 20)}; // Not opposed is better, opening is better
	private static final int PAWN_UNSUPPORTED = oe(2, 4); // Not backwards or isolated

	// And now the bonuses. Array by relative rank
	private static final int[] PAWN_CANDIDATE = {0, oe(10, 13), oe(10, 13), oe(14, 18), oe(22, 28), oe(34, 43), oe(50, 63), 0};
	private static final int[] PAWN_PASSER = {0, oe(20, 25), oe(20, 25), oe(28, 35), oe(44, 55), oe(68, 85), oe(100, 125), 0};
	private static final int[] PAWN_PASSER_OUTSIDE = {0, 0, 0, oe(2, 3), oe(7, 9), oe(14, 18), oe(24, 30), 0};
	private static final int[] PAWN_PASSER_CONNECTED = {0, 0, 0, oe(3, 3), oe(8, 8), oe(15, 15), oe(25, 25), 0};
	private static final int[] PAWN_PASSER_SUPPORTED = {0, 0, 0, oe(6, 6), oe(17, 17), oe(33, 33), oe(55, 55), 0};
	private static final int[] PAWN_PASSER_MOBILE = {0, 0, 0, oe(2, 2), oe(6, 6), oe(12, 12), oe(20, 20), 0};
	private static final int[] PAWN_PASSER_RUNNER = {0, 0, 0, oe(6, 6), oe(18, 18), oe(36, 36), oe(60, 60), 0};

	private static final int[] PAWN_PASSER_OTHER_KING_DISTANCE = {0, 0, 0, oe(0, 2), oe(0, 6), oe(0, 12), oe(0, 20), 0};
	private static final int[] PAWN_PASSER_MY_KING_DISTANCE = {0, 0, 0, oe(0, 1), oe(0, 3), oe(0, 6), oe(0, 10), 0};

	private static final int[] PAWN_SHIELD_CENTER = {0, oe(55, 0), oe(41, 0), oe(28, 0), oe(14, 0), 0, 0, 0};
	private static final int[] PAWN_SHIELD = {0, oe(35, 0), oe(26, 0), oe(18, 0), oe(9, 0), 0, 0, 0};
	private static final int[] PAWN_STORM_CENTER = {0, 0, 0, oe(8, 0), oe(15, 0), oe(30, 0), 0, 0};
	private static final int[] PAWN_STORM = {0, 0, 0, oe(5, 0), oe(10, 0), oe(20, 0), 0, 0};

	// Knights
	private static final int KNIGHT_OUTPOST = oe(2, 3); // Adds one time if no opposite can can attack out knight and twice if it is defended by one of our pawns
	private static final int[] KNIGHT_OUTPOST_ATTACKING = { // Knight outpost attacks squares Near King or other opposite pieces Pawn Undefended
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, oe(7, 7), oe(7, 7), oe(10, 10), oe(10, 10), oe(7, 7), oe(7, 7), 0,
			0, oe(5, 5), oe(5, 5), oe(8, 8), oe(8, 8), oe(5, 5), oe(5, 5), 0,
			0, 0, oe(5, 5), oe(8, 8), oe(8, 8), oe(5, 5), 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0
	};

	// Bishops
	private static final int BISHOP_OUTPOST = oe(1, 2); // Only if defended by pawn
	private static final int BISHOP_OUTPOST_ATTACKING = oe(3, 4); // And attacks other piece not defended by pawn or a square in the king zone
	private static final int BISHOP_MY_PAWNS_IN_COLOR_PENALTY = oe(2, 4); // Penalty for each of my pawns in the bishop color (Capablanca rule)
	private static final int[] BISHOP_TRAPPED_PENALTY = {oe(40, 40), oe(80, 80)}; // By pawn not guarded / guarded
	private static final long[] BISHOP_TRAPPING = { // Indexed by bishop position, contains the square where a pawn can trap the bishop
			0, Square.F2, 0, 0, 0, 0, Square.C2, 0,
			Square.G3, 0, 0, 0, 0, 0, 0, Square.B3,
			Square.G4, 0, 0, 0, 0, 0, 0, Square.B4,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			Square.G5, 0, 0, 0, 0, 0, 0, Square.B5,
			Square.G6, 0, 0, 0, 0, 0, 0, Square.B6,
			0, Square.F7, 0, 0, 0, 0, Square.C7, 0
	};
	private static final long[] BISHOP_TRAPPING_GUARD = { // Indexed by bishop position, contains the square where other pawn defends the trapping pawn
			0, 0, 0, 0, 0, 0, 0, 0,
			Square.F2, 0, 0, 0, 0, 0, 0, Square.C2,
			Square.F3, 0, 0, 0, 0, 0, 0, Square.C3,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			Square.F6, 0, 0, 0, 0, 0, 0, Square.C6,
			Square.F7, 0, 0, 0, 0, 0, 0, Square.C7,
			0, 0, 0, 0, 0, 0, 0, 0
	};

	// Rooks
	private static final int ROOK_OUTPOST = oe(1, 2); // Only if defended by pawn
	private static final int ROOK_OUTPOST_ATTACKING = oe(3, 4); // Also attacks other piece not defended by pawn or a square near king
	private static final int ROOK_FILE_OPEN_NO_MG = oe(20, 10); // No pawns in rook file and no minor guarded
	private static final int ROOK_FILE_OPEN_MG_P = oe(15, 5); // No pawns in rook file and minor guarded, my pawns can attack
	private static final int ROOK_FILE_OPEN_MG_NP = oe(10, 0); // No pawns in rook file and minor guarded, my pawns cannot attack
	private static final int ROOK_FILE_SEMIOPEN = oe(3, 6); // No pawns mines in file
	private static final int ROOK_FILE_SEMIOPEN_WEAK_PAWN = oe(15, 5); // And attacks a backward or isolated pawn
	private static final int ROOK_FILE_SEMIOPEN_K = oe(3, 6); // No pawns mines in file and opposite king
	private static final int ROOK_7 = oe(7, 10); // Rook 5, 6 or 7th rank attacking a pawn in the same rank not defended by pawn
	private static final int[] ROOK_TRAPPED_PENALTY = {oe(50, 0), oe(25, 0), oe(12, 0), oe(6, 0)}; // Penalty by number of mobility squares
	private static final long[] ROOK_TRAPPING = { // Indexed by own king position, contains the squares where a rook may be traped by the king
			0, Square.H1 | Square.H2, Square.H1 | Square.H2 | Square.G1 | Square.G2, 0,
			0, Square.A1 | Square.A2 | Square.B1 | Square.B2, Square.A1 | Square.A2, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, 0, 0, 0, 0, 0, 0, 0,
			0, Square.H7 | Square.H8, Square.H7 | Square.H8 | Square.G7 | Square.G8, 0,
			0, Square.A7 | Square.A8 | Square.B7 | Square.B8, Square.A7 | Square.A8, 0,
	};

	// King
	// Sums for each piece attacking an square near the king
	private static final int PIECE_ATTACKS_KING[] = {0, 0, oe(30, 0), oe(20, 0), oe(40, 0), oe(80, 0)};
	// Ponder kings attacks by the number of attackers (not pawns)
	private static final int[] KING_SAFETY_PONDER = {0, 0, 32, 48, 56, 60, 62, 63, 64, 64, 64, 64, 64, 64, 64, 64};

	// Tempo
	public static final int TEMPO = oe(15, 5); // Add to moving side score

	private static final long[] OUTPOST_MASK = {0x00007e7e7e000000L, 0x0000007e7e7e0000L};

	private static final int pawnPcsq[] = {
			oe(-18, 4), oe(-6, 2), oe(0, 0), oe(6, -2), oe(6, -2), oe(0, 0), oe(-6, 2), oe(-18, 4),
			oe(-21, 1), oe(-9, -1), oe(-3, -3), oe(3, -5), oe(3, -5), oe(-3, -3), oe(-9, -1), oe(-21, 1),
			oe(-20, 1), oe(-8, -1), oe(-2, -3), oe(4, -5), oe(4, -5), oe(-2, -3), oe(-8, -1), oe(-20, 1),
			oe(-19, 2), oe(-7, 0), oe(-1, -2), oe(12, -4), oe(12, -4), oe(-1, -2), oe(-7, 0), oe(-19, 2),
			oe(-17, 3), oe(-5, 1), oe(1, -1), oe(10, -3), oe(10, -3), oe(1, -1), oe(-5, 1), oe(-17, 3),
			oe(-16, 4), oe(-4, 2), oe(2, 0), oe(8, -2), oe(8, -2), oe(2, 0), oe(-4, 2), oe(-16, 4),
			oe(-15, 6), oe(-3, 4), oe(3, 2), oe(9, 0), oe(9, 0), oe(3, 2), oe(-3, 4), oe(-15, 6),
			oe(-18, 4), oe(-6, 2), oe(0, 0), oe(6, -2), oe(6, -2), oe(0, 0), oe(-6, 2), oe(-18, 4)
	};
	private static final int knightPcsq[] = {
			oe(-27, -22), oe(-17, -17), oe(-9, -12), oe(-4, -9), oe(-4, -9), oe(-9, -12), oe(-17, -17), oe(-27, -22),
			oe(-21, -15), oe(-11, -8), oe(-3, -4), oe(2, -2), oe(2, -2), oe(-3, -4), oe(-11, -8), oe(-21, -15),
			oe(-15, -10), oe(-5, -4), oe(3, 1), oe(8, 3), oe(8, 3), oe(3, 1), oe(-5, -4), oe(-15, -10),
			oe(-9, -6), oe(1, -1), oe(9, 4), oe(14, 8), oe(14, 8), oe(9, 4), oe(1, -1), oe(-9, -6),
			oe(-5, -4), oe(5, 1), oe(13, 6), oe(18, 10), oe(18, 10), oe(13, 6), oe(5, 1), oe(-5, -4),
			oe(-6, -4), oe(4, 2), oe(12, 7), oe(17, 9), oe(17, 9), oe(12, 7), oe(4, 2), oe(-6, -4),
			oe(-10, -8), oe(0, -1), oe(8, 3), oe(13, 5), oe(13, 5), oe(8, 3), oe(0, -1), oe(-10, -8),
			oe(-20, -15), oe(-10, -10), oe(-2, -5), oe(3, -2), oe(3, -2), oe(-2, -5), oe(-10, -10), oe(-20, -15)
	};
	private static final int bishopPcsq[] = {
			oe(-7, 0), oe(-8, -1), oe(-11, -2), oe(-13, -2), oe(-13, -2), oe(-11, -2), oe(-8, -1), oe(-7, 0),
			oe(-3, -1), oe(3, 1), oe(0, 0), oe(-2, 0), oe(-2, 0), oe(0, 0), oe(3, 1), oe(-3, -1),
			oe(-6, -2), oe(0, 0), oe(7, 3), oe(6, 2), oe(6, 2), oe(7, 3), oe(0, 0), oe(-6, -2),
			oe(-8, -2), oe(-2, 0), oe(6, 2), oe(15, 5), oe(15, 5), oe(6, 2), oe(-2, 0), oe(-8, -2),
			oe(-8, -2), oe(-2, 0), oe(6, 2), oe(15, 5), oe(15, 5), oe(6, 2), oe(-2, 0), oe(-8, -2),
			oe(-6, -2), oe(0, 0), oe(7, 3), oe(6, 2), oe(6, 2), oe(7, 3), oe(0, 0), oe(-6, -2),
			oe(-3, -1), oe(3, 1), oe(0, 0), oe(-2, 0), oe(-2, 0), oe(0, 0), oe(3, 1), oe(-3, -1),
			oe(-2, 0), oe(-3, -1), oe(-6, -2), oe(-8, -2), oe(-8, -2), oe(-6, -2), oe(-3, -1), oe(-2, 0)
	};
	private static final int rookPcsq[] = {
			oe(-4, 0), oe(0, 0), oe(4, 0), oe(8, 0), oe(8, 0), oe(4, 0), oe(0, 0), oe(-4, 0),
			oe(-4, 0), oe(0, 0), oe(4, 0), oe(8, 0), oe(8, 0), oe(4, 0), oe(0, 0), oe(-4, 0),
			oe(-4, 0), oe(0, 0), oe(4, 0), oe(8, 0), oe(8, 0), oe(4, 0), oe(0, 0), oe(-4, 0),
			oe(-4, 0), oe(0, 0), oe(4, 0), oe(8, 0), oe(8, 0), oe(4, 0), oe(0, 0), oe(-4, 0),
			oe(-4, 1), oe(0, 1), oe(4, 1), oe(8, 1), oe(8, 1), oe(4, 1), oe(0, 1), oe(-4, 1),
			oe(-4, 3), oe(0, 3), oe(4, 3), oe(8, 3), oe(8, 3), oe(4, 3), oe(0, 3), oe(-4, 3),
			oe(-4, 5), oe(0, 5), oe(4, 5), oe(8, 5), oe(8, 5), oe(4, 5), oe(0, 5), oe(-4, 5),
			oe(-4, -2), oe(0, -2), oe(4, -2), oe(8, -2), oe(8, -2), oe(4, -2), oe(0, -2), oe(-4, -2)
	};
	private static final int queenPcsq[] = {
			oe(-9, -15), oe(-6, -10), oe(-4, -8), oe(-2, -7), oe(-2, -7), oe(-4, -8), oe(-6, -10), oe(-9, -15),
			oe(-6, -10), oe(-1, -5), oe(1, -3), oe(3, -2), oe(3, -2), oe(1, -3), oe(-1, -5), oe(-6, -10),
			oe(-4, -8), oe(1, -3), oe(5, 0), oe(6, 2), oe(6, 2), oe(5, 0), oe(1, -3), oe(-4, -8),
			oe(-2, -7), oe(3, -2), oe(6, 2), oe(9, 5), oe(9, 5), oe(6, 2), oe(3, -2), oe(-2, -7),
			oe(-2, -7), oe(3, -2), oe(6, 2), oe(9, 5), oe(9, 5), oe(6, 2), oe(3, -2), oe(-2, -7),
			oe(-4, -8), oe(1, -3), oe(5, 0), oe(6, 2), oe(6, 2), oe(5, 0), oe(1, -3), oe(-4, -8),
			oe(-6, -10), oe(-1, -5), oe(1, -3), oe(3, -2), oe(3, -2), oe(1, -3), oe(-1, -5), oe(-6, -10),
			oe(-9, -15), oe(-6, -10), oe(-4, -8), oe(-2, -7), oe(-2, -7), oe(-4, -8), oe(-6, -10), oe(-9, -15)
	};
	private static final int kingPcsq[] = {
			oe(34, -58), oe(39, -35), oe(14, -19), oe(-6, -13), oe(-6, -13), oe(14, -19), oe(39, -35), oe(34, -58),
			oe(31, -35), oe(36, -10), oe(11, 2), oe(-9, 8), oe(-9, 8), oe(11, 2), oe(36, -10), oe(31, -35),
			oe(28, -19), oe(33, 2), oe(8, 17), oe(-12, 23), oe(-12, 23), oe(8, 17), oe(33, 2), oe(28, -19),
			oe(25, -13), oe(30, 8), oe(5, 23), oe(-15, 32), oe(-15, 32), oe(5, 23), oe(30, 8), oe(25, -13),
			oe(20, -13), oe(25, 8), oe(0, 23), oe(-20, 32), oe(-20, 32), oe(0, 23), oe(25, 8), oe(20, -13),
			oe(15, -19), oe(20, 2), oe(-5, 17), oe(-25, 23), oe(-25, 23), oe(-5, 17), oe(20, 2), oe(15, -19),
			oe(5, -35), oe(10, -10), oe(-15, 2), oe(-35, 8), oe(-35, 8), oe(-15, 2), oe(10, -10), oe(5, -35),
			oe(-5, -58), oe(0, -35), oe(-25, -19), oe(-45, -13), oe(-45, -13), oe(-25, -19), oe(0, -35), oe(-5, -58)
	};

	public boolean debug = false;
	public boolean debugPawns = false;
	public StringBuffer debugSB;

	private int[] scaleFactor = {0};

	private int[] pawnMaterial = {0, 0};
	private int[] nonPawnMaterial = {0, 0};
	private int[] pcsq = {0, 0};
	private int[] space = {0, 0};
	private int[] positional = {0, 0};
	private int[] mobility = {0, 0};
	private int[] attacks = {0, 0};
	private int[] kingAttackersCount = {0, 0};
	private int[] kingSafety = {0, 0};
	private int[] pawnStructure = {0, 0};
	private int[] passedPawns = {0, 0};
	private long[] pawnCanAttack = {0, 0};
	private long[] mobilitySquares = {0, 0};
	private long[] kingZone = {0, 0}; // Squares surrounding King

	public int evaluate(Board board, AttacksInfo ai) {
		if (debug) {
			debugSB = new StringBuffer();
			debugSB.append("\n");
			debugSB.append(board.toString());
			debugSB.append("\n");
		}

		int whitePawns = BitboardUtils.popCount(board.pawns & board.whites);
		int blackPawns = BitboardUtils.popCount(board.pawns & board.blacks);
		int whiteKnights = BitboardUtils.popCount(board.knights & board.whites);
		int blackKnights = BitboardUtils.popCount(board.knights & board.blacks);
		int whiteBishops = BitboardUtils.popCount(board.bishops & board.whites);
		int blackBishops = BitboardUtils.popCount(board.bishops & board.blacks);
		int whiteRooks = BitboardUtils.popCount(board.rooks & board.whites);
		int blackRooks = BitboardUtils.popCount(board.rooks & board.blacks);
		int whiteQueens = BitboardUtils.popCount(board.queens & board.whites);
		int blackQueens = BitboardUtils.popCount(board.queens & board.blacks);

		int endgameValue = Endgame.evaluateEndgame(board, scaleFactor, whitePawns, blackPawns, whiteKnights, blackKnights, whiteBishops, blackBishops, whiteRooks, blackRooks, whiteQueens, blackQueens);
		if (endgameValue != NO_VALUE) {
			return endgameValue;
		}

		pawnMaterial[W] = whitePawns * PIECE_VALUES_OE[Piece.PAWN];
		nonPawnMaterial[W] = whiteKnights * PIECE_VALUES_OE[Piece.KNIGHT] +
				whiteBishops * PIECE_VALUES_OE[Piece.BISHOP] +
				whiteRooks * PIECE_VALUES_OE[Piece.ROOK] +
				whiteQueens * PIECE_VALUES_OE[Piece.QUEEN] +
				((board.whites & board.bishops & Square.WHITES) != 0
						&& (board.whites & board.bishops & Square.BLACKS) != 0 ? BISHOP_PAIR : 0);
		pawnMaterial[B] = blackPawns * PIECE_VALUES_OE[Piece.PAWN];
		nonPawnMaterial[B] = blackKnights * PIECE_VALUES_OE[Piece.KNIGHT] +
				blackBishops * PIECE_VALUES_OE[Piece.BISHOP] +
				blackRooks * PIECE_VALUES_OE[Piece.ROOK] +
				blackQueens * PIECE_VALUES_OE[Piece.QUEEN] +
				((board.blacks & board.bishops & Square.WHITES) != 0
						&& (board.blacks & board.bishops & Square.BLACKS) != 0 ? BISHOP_PAIR : 0);

		int nonPawnMat = e(nonPawnMaterial[W] + nonPawnMaterial[B]);
		int gamePhase = nonPawnMat >= NON_PAWN_MATERIAL_MIDGAME_MAX ? GAME_PHASE_MIDGAME :
				nonPawnMat <= NON_PAWN_MATERIAL_ENDGAME_MIN ? GAME_PHASE_ENDGAME :
						((nonPawnMat - NON_PAWN_MATERIAL_ENDGAME_MIN) * GAME_PHASE_MIDGAME) / (NON_PAWN_MATERIAL_MIDGAME_MAX - NON_PAWN_MATERIAL_ENDGAME_MIN);

		pcsq[W] = 0;
		pcsq[B] = 0;
		positional[W] = 0;
		positional[B] = 0;
		mobility[W] = 0;
		mobility[B] = 0;
		kingAttackersCount[W] = 0;
		kingAttackersCount[B] = 0;
		kingSafety[W] = 0;
		kingSafety[B] = 0;
		pawnStructure[W] = 0;
		pawnStructure[B] = 0;
		passedPawns[W] = 0;
		passedPawns[B] = 0;

		mobilitySquares[W] = ~board.whites;
		mobilitySquares[B] = ~board.blacks;

		ai.build(board);

		long whitePawnsAux = board.pawns & board.whites;
		long blackPawnsAux = board.pawns & board.blacks;

		// Space evaluation
		if (gamePhase > 0) {
			long whiteSafe = WHITE_SPACE_ZONE & ~ai.pawnAttacks[B] & (~ai.attackedSquares[B] | ai.attackedSquares[W]);
			long blackSafe = BLACK_SPACE_ZONE & ~ai.pawnAttacks[W] & (~ai.attackedSquares[W] | ai.attackedSquares[B]);

			long whiteBehindPawn = ((whitePawnsAux >>> 8) | (whitePawnsAux >>> 16) | (whitePawnsAux >>> 24));
			long blackBehindPawn = ((blackPawnsAux << 8) | (blackPawnsAux << 16) | (blackPawnsAux << 24));

			space[W] = SPACE * (((BitboardUtils.popCount(whiteSafe) + BitboardUtils.popCount(whiteSafe & whiteBehindPawn)) *
					(whiteKnights + whiteBishops)) / 4);
			space[B] = SPACE * (((BitboardUtils.popCount(blackSafe) + BitboardUtils.popCount(blackSafe & blackBehindPawn)) *
					(blackKnights + blackBishops)) / 4);
		} else {
			space[W] = 0;
			space[B] = 0;
		}

		// Squares that pawns attack or can attack by advancing
		pawnCanAttack[W] = ai.pawnAttacks[W];
		pawnCanAttack[B] = ai.pawnAttacks[B];
		for (int i = 0; i < 5; i++) {
			whitePawnsAux = whitePawnsAux << 8;
			whitePawnsAux &= ~((board.pawns & board.blacks) | ai.pawnAttacks[B]); // Cannot advance because of a blocking pawn or a opposite pawn attack
			blackPawnsAux = blackPawnsAux >>> 8;
			blackPawnsAux &= ~((board.pawns & board.whites) | ai.pawnAttacks[W]); // Cannot advance because of a blocking pawn or a opposite pawn attack

			if (whitePawnsAux == 0 && blackPawnsAux == 0) {
				break;
			}
			pawnCanAttack[W] |= ((whitePawnsAux & ~BitboardUtils.b_l) << 9) | ((whitePawnsAux & ~BitboardUtils.b_r) << 7);
			pawnCanAttack[B] |= ((blackPawnsAux & ~BitboardUtils.b_r) >>> 9) | ((blackPawnsAux & ~BitboardUtils.b_l) >>> 7);
		}

		// Calculate attacks
		attacks[W] = evalAttacks(board, ai, W, board.blacks);
		attacks[B] = evalAttacks(board, ai, B, board.whites);

		// Squares surrounding King and three squares towards thew other side
		kingZone[W] = bbAttacks.king[ai.kingIndex[W]];
		kingZone[W] |= (kingZone[W] << 8);
		kingZone[B] = bbAttacks.king[ai.kingIndex[B]];
		kingZone[B] |= (kingZone[B] >>> 8);

		long all = board.getAll();
		long pieceAttacks, pieceAttackedXray, safeAttacks, kingAttacks;

		long square = 1;
		for (int index = 0; index < 64; index++) {
			if ((square & all) != 0) {
				boolean isWhite = ((board.whites & square) != 0);
				int us = (isWhite ? W : B);
				int them = (isWhite ? B : W);
				long mines = (isWhite ? board.whites : board.blacks);
				long others = (isWhite ? board.blacks : board.whites);
				int pcsqIndex = (isWhite ? index : 63 - index);
				int rank = index >> 3;
				int relativeRank = isWhite ? rank : 7 - rank;
				int file = 7 - index & 7;

				pieceAttacks = ai.attacksFromSquare[index];

				if ((square & board.pawns) != 0) {
					pcsq[us] += pawnPcsq[pcsqIndex];

					long myPawns = board.pawns & mines;
					long otherPawns = board.pawns & others;
					long adjacentFiles = BitboardUtils.FILES_ADJACENT[file];
					long ranksForward = BitboardUtils.RANKS_FORWARD[us][rank];
					long pawnFile = BitboardUtils.FILE[file];
					long routeToPromotion = pawnFile & ranksForward;
					long otherPawnsAheadAdjacent = ranksForward & adjacentFiles & otherPawns;

					boolean supported = (square & ai.pawnAttacks[us]) != 0;
					boolean doubled = (myPawns & routeToPromotion) != 0;
					boolean opposed = (otherPawns & routeToPromotion) != 0;
					boolean passed = !doubled
							&& !opposed
							&& otherPawnsAheadAdjacent == 0;

					if (!passed) {
						long myPawnsAheadAdjacent = ranksForward & adjacentFiles & myPawns;
						long myPawnsBesideAndBehindAdjacent = BitboardUtils.RANK_AND_BACKWARD[us][rank] & adjacentFiles & myPawns;
						boolean isolated = (myPawns & adjacentFiles) == 0;
						boolean candidate = !doubled
								&& !opposed
								&& (((otherPawnsAheadAdjacent & ~pieceAttacks) == 0) || // Can become passer advancing
								(BitboardUtils.popCount(myPawnsBesideAndBehindAdjacent) >= BitboardUtils.popCount(otherPawnsAheadAdjacent & ~pieceAttacks))); // Has more friend pawns beside and behind than opposed pawns controlling his route to promotion
						boolean backward = !isolated
								&& !candidate
								&& myPawnsBesideAndBehindAdjacent == 0
								&& (pieceAttacks & otherPawns) == 0 // No backwards if it can capture
								&& (BitboardUtils.RANK_AND_BACKWARD[us][isWhite ? BitboardUtils.getRankLsb(myPawnsAheadAdjacent) : BitboardUtils.getRankMsb(myPawnsAheadAdjacent)] &
								routeToPromotion & (board.pawns | ai.pawnAttacks[them])) != 0; // Other pawns stopping it from advance, opposing or capturing it before reaching my pawns

						if (debugPawns) {
							boolean connected = ((bbAttacks.king[index] & adjacentFiles & myPawns) != 0);
							debugSB.append("PAWN " + BitboardUtils.SQUARE_NAMES[index] +
									(isWhite ? " WHITE " : " BLACK ") +
									(isolated ? "isolated " : "") +
									(supported ? "supported " : "") +
									(connected ? "connected " : "") +
									(doubled ? "doubled " : "") +
									(opposed ? "opposed " : "") +
									(candidate ? "candidate " : "") +
									(backward ? "backward " : "") +
									"\n"
							);
						}

						if (backward) {
							pawnStructure[us] -= PAWN_BACKWARDS[opposed ? 1 : 0];
						}
						if (isolated) {
							pawnStructure[us] -= PAWN_ISOLATED[opposed ? 1 : 0];
						}
						if (doubled) {
							pawnStructure[us] -= PAWN_DOUBLED[opposed ? 1 : 0];
						}
						if (!supported
								&& !isolated
								&& !backward) {
							pawnStructure[us] -= PAWN_UNSUPPORTED;
						}
						if (candidate) {
							passedPawns[us] += PAWN_CANDIDATE[relativeRank];
						}
						// Pawn Storm: It can open a file near the other king
						if (gamePhase > 0 && relativeRank > 2) {
							// Only if in kingside or queenside
							long stormedPawns = otherPawnsAheadAdjacent & ~BitboardUtils.D & ~BitboardUtils.E;
							if (stormedPawns != 0) {
								// The stormed pawn must be in the other king's adjacent files
								int otherKingFile = 7 - ai.kingIndex[them] & 7;
								if ((stormedPawns & BitboardUtils.FILE[otherKingFile]) != 0) {
									pawnStructure[us] += PAWN_STORM_CENTER[relativeRank];
								} else if ((stormedPawns & BitboardUtils.FILES_ADJACENT[otherKingFile]) != 0) {
									pawnStructure[us] += PAWN_STORM[relativeRank];
								}
							}
						}
					} else {
						//
						// Passed Pawn
						//
						// Backfile only to the first piece found
						long backFile = bbAttacks.getRookAttacks(index, all) & pawnFile & BitboardUtils.RANKS_BACKWARD[us][rank];
						// If it has a rook or queen behind consider all the route to promotion attacked or defended
						long attackedAndNotDefendedRoute =
								((routeToPromotion & ai.attackedSquares[them]) | ((backFile & (board.rooks | board.queens) & others) != 0 ? routeToPromotion : 0)) &
										~((routeToPromotion & ai.attackedSquares[us]) | ((backFile & (board.rooks | board.queens) & mines) != 0 ? routeToPromotion : 0));
						long pushSquare = isWhite ? square << 8 : square >>> 8;
						boolean connected = (bbAttacks.king[index] & adjacentFiles & myPawns) != 0;
						boolean outside = otherPawns != 0
								&& (((square & BitboardUtils.FILES_LEFT[3]) != 0 && (board.pawns & BitboardUtils.FILES_LEFT[file]) == 0)
								|| ((square & BitboardUtils.FILES_RIGHT[4]) != 0 && (board.pawns & BitboardUtils.FILES_RIGHT[file]) == 0));
						boolean mobile = (pushSquare & (all | attackedAndNotDefendedRoute)) == 0;
						boolean runner = mobile
								&& (routeToPromotion & all) == 0
								&& attackedAndNotDefendedRoute == 0;

						if (debug) {
							debugSB.append("PAWN " + BitboardUtils.SQUARE_NAMES[index] +
									(isWhite ? " WHITE " : " BLACK ") +
									"passed " +
									(outside ? "outside " : "") +
									(connected ? "connected " : "") +
									(supported ? "supported " : "") +
									(mobile ? "mobile " : "") +
									(runner ? "runner " : "") +
									"\n"
							);
						}

						passedPawns[us] += PAWN_PASSER[relativeRank];

						if (relativeRank >= 2) {
							int pushIndex = isWhite ? index + 8 : index - 8;
							passedPawns[us] += BitboardUtils.distance(pushIndex, ai.kingIndex[them]) * PAWN_PASSER_OTHER_KING_DISTANCE[relativeRank]
									- BitboardUtils.distance(pushIndex, ai.kingIndex[us]) * PAWN_PASSER_MY_KING_DISTANCE[relativeRank];
						}
						if (outside) {
							passedPawns[us] += PAWN_PASSER_OUTSIDE[relativeRank];
						}
						if (supported) {
							passedPawns[us] += PAWN_PASSER_SUPPORTED[relativeRank];
						} else if (connected) {
							passedPawns[us] += PAWN_PASSER_CONNECTED[relativeRank];
						}
						if (runner) {
							passedPawns[us] += PAWN_PASSER_RUNNER[relativeRank];
						} else if (mobile) {
							passedPawns[us] += PAWN_PASSER_MOBILE[relativeRank];
						}
					}
					// Pawn is part of the king shield
					if (gamePhase > 0
							&& (pawnFile & ~ranksForward & kingZone[us] & ~BitboardUtils.D & ~BitboardUtils.E) != 0) { // Pawn in the kingzone
						pawnStructure[us] += (pawnFile & board.kings & mines) != 0 ?
								PAWN_SHIELD_CENTER[relativeRank] :
								PAWN_SHIELD[relativeRank];
					}

				} else if ((square & board.knights) != 0) {
					pcsq[us] += knightPcsq[pcsqIndex];

					safeAttacks = pieceAttacks & ~ai.pawnAttacks[them];

					mobility[us] += MOBILITY[Piece.KNIGHT][BitboardUtils.popCount(safeAttacks & mobilitySquares[us])];

					kingAttacks = safeAttacks & kingZone[them];
					if (kingAttacks != 0) {
						kingSafety[us] += PIECE_ATTACKS_KING[Piece.KNIGHT] * BitboardUtils.popCount(kingAttacks);
						kingAttackersCount[us]++;
					}

					// Knight outpost: no opposite pawns can attack the square
					if ((square & OUTPOST_MASK[us] & ~pawnCanAttack[them]) != 0) {
						positional[us] += KNIGHT_OUTPOST;
						// Defended by one of our pawns
						if ((square & ai.pawnAttacks[us]) != 0) {
							positional[us] += KNIGHT_OUTPOST;
							// Attacks squares near king or other pieces pawn undefended
							if ((safeAttacks & (kingZone[them] | others)) != 0) {
								positional[us] += KNIGHT_OUTPOST_ATTACKING[pcsqIndex];
							}
						}
					}

				} else if ((square & board.bishops) != 0) {
					pcsq[us] += bishopPcsq[pcsqIndex];

					safeAttacks = pieceAttacks & ~ai.pawnAttacks[them];

					mobility[us] += MOBILITY[Piece.BISHOP][BitboardUtils.popCount(safeAttacks & mobilitySquares[us])];

					kingAttacks = safeAttacks & kingZone[them];
					if (kingAttacks != 0) {
						kingSafety[us] += PIECE_ATTACKS_KING[Piece.BISHOP] * BitboardUtils.popCount(kingAttacks);
						kingAttackersCount[us]++;
					}

					pieceAttackedXray = bbAttacks.getBishopAttacks(index, all & ~(pieceAttacks & others & ~board.pawns))
							& ~pieceAttacks
							& (board.rooks | board.queens) & others;
					if (pieceAttackedXray != 0) {
						attacks[us] += PINNED_PIECE * BitboardUtils.popCount(pieceAttackedXray);
					}

					// Bishop Outpost: no opposite pawns can attack the square and defended by one of our pawns
					if ((square & OUTPOST_MASK[us] & ~pawnCanAttack[them] & ai.pawnAttacks[us]) != 0) {
						positional[us] += BISHOP_OUTPOST;
						// Attacks squares near king or other pieces pawn undefended
						if ((safeAttacks & (kingZone[them] | others)) != 0) {
							positional[us] += BISHOP_OUTPOST_ATTACKING;
						}
					}

					positional[us] -= BISHOP_MY_PAWNS_IN_COLOR_PENALTY * BitboardUtils.popCount(board.pawns & mines & BitboardUtils.getSameColorSquares(square));

					if ((BISHOP_TRAPPING[index] & board.pawns & others) != 0) {
						mobility[us] -= BISHOP_TRAPPED_PENALTY[(BISHOP_TRAPPING_GUARD[index] & board.pawns & others) != 0 ? 1 : 0];
					}

				} else if ((square & board.rooks) != 0) {
					pcsq[us] += rookPcsq[pcsqIndex];

					safeAttacks = pieceAttacks & ~ai.pawnAttacks[them] & ~ai.knightAttacks[them] & ~ai.bishopAttacks[them];

					int mobilityCount = BitboardUtils.popCount(safeAttacks & mobilitySquares[us]);
					mobility[us] += MOBILITY[Piece.ROOK][mobilityCount];

					kingAttacks = safeAttacks & kingZone[them];
					if (kingAttacks != 0) {
						kingSafety[us] += PIECE_ATTACKS_KING[Piece.ROOK] * BitboardUtils.popCount(kingAttacks);
						kingAttackersCount[us]++;
					}

					pieceAttackedXray = bbAttacks.getRookAttacks(index, all & ~(pieceAttacks & others & ~board.pawns))
							& ~pieceAttacks
							& board.queens & others;
					if ((pieceAttackedXray) != 0) {
						attacks[us] += PINNED_PIECE * BitboardUtils.popCount(pieceAttackedXray);
					}

					// Rook Outpost: no opposite pawns can attack the square and defended by one of our pawns
					if ((square & OUTPOST_MASK[us] & ~pawnCanAttack[them] & ai.pawnAttacks[us]) != 0) {
						positional[us] += ROOK_OUTPOST;
						// Attacks squares near king or other pieces pawn undefended
						if ((safeAttacks & (kingZone[them] | others)) != 0) {
							positional[us] += ROOK_OUTPOST_ATTACKING;
						}
					}

					if (relativeRank >= 4) {
						long pawnsAligned = BitboardUtils.RANK[rank] & board.pawns & others;
						if (pawnsAligned != 0) {
							positional[us] += ROOK_7 * BitboardUtils.popCount(pawnsAligned);
						}
					}

					long rookFile = BitboardUtils.FILE[file] & BitboardUtils.RANKS_FORWARD[us][rank];
					if ((rookFile & board.pawns & mines) == 0) {
						positional[us] += ROOK_FILE_SEMIOPEN;
						if ((rookFile & board.pawns) == 0) {
							long minorPiecesDefendedByPawns = others & (board.bishops | board.knights) & ai.pawnAttacks[them];
							if ((rookFile & minorPiecesDefendedByPawns) == 0) {
								positional[us] += ROOK_FILE_OPEN_NO_MG;
							} else {
								if ((rookFile & minorPiecesDefendedByPawns & pawnCanAttack[us]) == 0) {
									positional[us] += ROOK_FILE_OPEN_MG_NP;
								} else {
									positional[us] += ROOK_FILE_OPEN_MG_P;
								}
							}
						}

						if ((rookFile & board.kings & others) != 0) {
							positional[us] += ROOK_FILE_SEMIOPEN_K;
						}
					}

					// Rook trapped by king
					if ((square & ROOK_TRAPPING[ai.kingIndex[us]]) != 0
							&& mobilityCount < ROOK_TRAPPED_PENALTY.length) {
						positional[us] -= ROOK_TRAPPED_PENALTY[mobilityCount];
					}

				} else if ((square & board.queens) != 0) {
					pcsq[us] += queenPcsq[pcsqIndex];

					safeAttacks = pieceAttacks & ~ai.pawnAttacks[them] & ~ai.knightAttacks[them] & ~ai.bishopAttacks[them] & ~ai.rookAttacks[them];

					mobility[us] += MOBILITY[Piece.QUEEN][BitboardUtils.popCount(safeAttacks & mobilitySquares[us])];

					kingAttacks = safeAttacks & kingZone[them];
					if (kingAttacks != 0) {
						kingSafety[us] += PIECE_ATTACKS_KING[Piece.QUEEN] * BitboardUtils.popCount(kingAttacks);
						kingAttackersCount[us]++;
					}

				} else if ((square & board.kings) != 0) {
					pcsq[us] += kingPcsq[pcsqIndex];
				}
			}
			square <<= 1;
		}

		int oe = (board.getTurn() ? TEMPO : -TEMPO)
				+ pawnMaterial[W] - pawnMaterial[B]
				+ nonPawnMaterial[W] - nonPawnMaterial[B]
				+ pcsq[W] - pcsq[B]
				+ space[W] - space[B]
				+ positional[W] - positional[B]
				+ attacks[W] - attacks[B]
				+ mobility[W] - mobility[B]
				+ pawnStructure[W] - pawnStructure[B]
				+ passedPawns[W] - passedPawns[B]
				+ oeShr(6, KING_SAFETY_PONDER[kingAttackersCount[W]] * kingSafety[W] - KING_SAFETY_PONDER[kingAttackersCount[B]] * kingSafety[B]);

		// Ponder opening and Endgame value depending of the game phase and the scale factor
		int value = (gamePhase * o(oe)
				+ (GAME_PHASE_MIDGAME - gamePhase) * e(oe) * scaleFactor[0] / Endgame.SCALE_FACTOR_DEFAULT) / GAME_PHASE_MIDGAME;

		if (debug) {
			logger.debug(debugSB);
			logger.debug("                    WOpening WEndgame BOpening BEndgame");
			logger.debug("pawnMaterial      = " + formatOE(pawnMaterial[W]) + " " + formatOE(pawnMaterial[B]));
			logger.debug("nonPawnMaterial   = " + formatOE(nonPawnMaterial[W]) + " " + formatOE(nonPawnMaterial[B]));
			logger.debug("pcsq              = " + formatOE(pcsq[W]) + " " + formatOE(pcsq[B]));
			logger.debug("space             = " + formatOE(space[W]) + " " + formatOE(space[B]));
			logger.debug("mobility          = " + formatOE(mobility[W]) + " " + formatOE(mobility[B]));
			logger.debug("positional        = " + formatOE(positional[W]) + " " + formatOE(positional[B]));
			logger.debug("pawnStructure     = " + formatOE(pawnStructure[W]) + " " + formatOE(pawnStructure[B]));
			logger.debug("passedPawns       = " + formatOE(passedPawns[W]) + " " + formatOE(passedPawns[B]));
			logger.debug("attacks           = " + formatOE(attacks[W]) + " " + formatOE(attacks[B]));
			logger.debug("kingSafety        = " + formatOE(oeShr(6, KING_SAFETY_PONDER[kingAttackersCount[W]] * kingSafety[W])) + " " + formatOE(oeShr(6, KING_SAFETY_PONDER[kingAttackersCount[B]] * kingSafety[B])));
			logger.debug("tempo             = " + formatOE(board.getTurn() ? TEMPO : -TEMPO));
			logger.debug("                    -----------------");
			logger.debug("TOTAL:              " + formatOE(oe));
			logger.debug("gamePhase = " + gamePhase + " => value = " + value);
		}
		assert Math.abs(value) < KNOWN_WIN : "Eval is outside limits";
		return value;
	}

	private int evalAttacks(Board board, AttacksInfo ai, int us, long others) {
		int attacks = 0;

		long attackedByPawn = ai.pawnAttacks[us] & others & ~board.pawns;
		while (attackedByPawn != 0) {
			long lsb = BitboardUtils.lsb(attackedByPawn);
			attacks += PAWN_ATTACKS[board.getPieceIntAt(lsb)];
			attackedByPawn &= ~lsb;
		}

		long otherWeak = ai.attackedSquares[us] & others & ~ai.pawnAttacks[1 - us];
		if (otherWeak != 0) {
			long attackedByMinor = (ai.knightAttacks[us] | ai.bishopAttacks[us]) & otherWeak;
			while (attackedByMinor != 0) {
				long lsb = BitboardUtils.lsb(attackedByMinor);
				attacks += MINOR_ATTACKS[board.getPieceIntAt(lsb)];
				attackedByMinor &= ~lsb;
			}
			long attackedByMajor = (ai.rookAttacks[us] | ai.queenAttacks[us]) & otherWeak;
			while (attackedByMajor != 0) {
				long lsb = BitboardUtils.lsb(attackedByMajor);
				attacks += MAJOR_ATTACKS[board.getPieceIntAt(lsb)];
				attackedByMajor &= ~lsb;
			}
		}

		long superiorAttacks = ai.pawnAttacks[us] & others & ~board.pawns
				| (ai.knightAttacks[us] | ai.bishopAttacks[us]) & others & (board.rooks | board.queens)
				| ai.rookAttacks[us] & others & board.queens;
		int superiorAttacksCount = BitboardUtils.popCount(superiorAttacks);
		if (superiorAttacksCount >= 2) {
			attacks += superiorAttacksCount * HUNG_PIECES;
		}

		long pinnedNotPawn = ai.pinnedPieces & ~board.pawns & others;
		if (pinnedNotPawn != 0) {
			attacks += PINNED_PIECE * BitboardUtils.popCount(pinnedNotPawn);
		}
		return attacks;
	}
} public class SimplifiedEvaluator extends Evaluator {

	static final int PAWN = 100;
	static final int KNIGHT = 320;
	static final int BISHOP = 330;
	static final int ROOK = 500;
	static final int QUEEN = 900;

	// Values are rotated for whites, so when white is playing is like shown in the code TODO at the moment must be symmetric
	public static final int[] pawnSquare = { //
			0, 0, 0, 0, 0, 0, 0, 0, //
			50, 50, 50, 50, 50, 50, 50, 50, //
			10, 10, 20, 30, 30, 20, 10, 10, //
			5, 5, 10, 25, 25, 10, 5, 5, //
			0, 0, 0, 20, 20, 0, 0, 0, //
			5, -5, -10, 0, 0, -10, -5, 5, //
			5, 10, 10, -20, -20, 10, 10, 5, //
			0, 0, 0, 0, 0, 0, 0, 0 //
	};

	public static final int[] knightSquare = { //
			-50, -40, -30, -30, -30, -30, -40, -50, //
			-40, -20, 0, 0, 0, 0, -20, -40, //
			-30, 0, 10, 15, 15, 10, 0, -30, //
			-30, 5, 15, 20, 20, 15, 5, -30, //
			-30, 0, 15, 20, 20, 15, 0, -30, //
			-30, 5, 10, 15, 15, 10, 5, -30, //
			-40, -20, 0, 5, 5, 0, -20, -40, //
			-50, -40, -30, -30, -30, -30, -40, -50, //
	};

	public static final int[] bishopSquare = { //
			-20, -10, -10, -10, -10, -10, -10, -20, //
			-10, 0, 0, 0, 0, 0, 0, -10, //
			-10, 0, 5, 10, 10, 5, 0, -10, //
			-10, 5, 5, 10, 10, 5, 5, -10, //
			-10, 0, 10, 10, 10, 10, 0, -10, //
			-10, 10, 10, 10, 10, 10, 10, -10, //
			-10, 5, 0, 0, 0, 0, 5, -10, //
			-20, -10, -10, -10, -10, -10, -10, -20, //
	};

	public static final int[] rookSquare = { //
			0, 0, 0, 0, 0, 0, 0, 0, //
			5, 10, 10, 10, 10, 10, 10, 5, //
			-5, 0, 0, 0, 0, 0, 0, -5, //
			-5, 0, 0, 0, 0, 0, 0, -5, //
			-5, 0, 0, 0, 0, 0, 0, -5, //
			-5, 0, 0, 0, 0, 0, 0, -5, //
			-5, 0, 0, 0, 0, 0, 0, -5, //
			0, 0, 0, 5, 5, 0, 0, 0 //
	};

	public static final int[] queenSquare = { //
			-20, -10, -10, -5, -5, -10, -10, -20, //
			-10, 0, 0, 0, 0, 0, 0, -10, //
			-10, 0, 5, 5, 5, 5, 0, -10, //
			-5, 0, 5, 5, 5, 5, 0, -5, //
			0, 0, 5, 5, 5, 5, 0, -5, //
			-10, 5, 5, 5, 5, 5, 0, -10, //
			-10, 0, 5, 0, 0, 0, 0, -10, //
			-20, -10, -10, -5, -5, -10, -10, -20 //
	};

	public static final int[] kingSquareOpening = { //
			-30, -40, -40, -50, -50, -40, -40, -30, //
			-30, -40, -40, -50, -50, -40, -40, -30, //
			-30, -40, -40, -50, -50, -40, -40, -30, //
			-30, -40, -40, -50, -50, -40, -40, -30, //
			-20, -30, -30, -40, -40, -30, -30, -20, //
			-10, -20, -20, -20, -20, -20, -20, -10, //
			20, 20, 0, 0, 0, 0, 20, 20, //
			20, 30, 10, 0, 0, 10, 30, 20 //
	};

	public static final int[] kingSquareEndGame = { //
			-50, -40, -30, -20, -20, -30, -40, -50, //
			-30, -20, -10, 0, 0, -10, -20, -30, //
			-30, -10, 20, 30, 30, 20, -10, -30, //
			-30, -10, 30, 40, 40, 30, -10, -30, //
			-30, -10, 30, 40, 40, 30, -10, -30, //
			-30, -10, 20, 30, 30, 20, -10, -30, //
			-30, -30, 0, 0, 0, 0, -30, -30, //
			-50, -30, -30, -30, -30, -30, -30, -50 //
	};


	public int evaluate(Board board, AttacksInfo attacksInfo) {
		long all = board.getAll();

		int materialValue[] = {0, 0};
		int pawnMaterialValue[] = {0, 0};
		int pcsqValue[] = {0, 0};
		int pcsqOpeningValue[] = {0, 0};
		int pcsqEndgameValue[] = {0, 0};

		boolean noQueen[] = {true, true};

		long square = 1;
		byte index = 0;
		while (square != 0) {
			boolean isWhite = ((board.whites & square) != 0);
			int color = (isWhite ? 0 : 1);
			int pcsqIndex = (isWhite ? 63 - index : index);

			if ((square & all) != 0) {

				if ((square & board.pawns) != 0) {
					pawnMaterialValue[color] += PAWN;
					pcsqValue[color] += pawnSquare[pcsqIndex];

				} else if ((square & board.knights) != 0) {
					materialValue[color] += KNIGHT;
					pcsqValue[color] += knightSquare[pcsqIndex];

				} else if ((square & board.bishops) != 0) {
					materialValue[color] += BISHOP;
					pcsqValue[color] += bishopSquare[pcsqIndex];

				} else if ((square & board.rooks) != 0) {
					materialValue[color] += ROOK;
					pcsqValue[color] += rookSquare[pcsqIndex];

				} else if ((square & board.queens) != 0) {
					pcsqValue[color] += queenSquare[pcsqIndex];
					materialValue[color] += QUEEN;
					noQueen[color] = false;

				} else if ((square & board.kings) != 0) {
					pcsqOpeningValue[color] += kingSquareOpening[pcsqIndex];
					pcsqEndgameValue[color] += kingSquareEndGame[pcsqIndex];
				}
			}
			square <<= 1;
			index++;
		}

		int value = 0;
		value += pawnMaterialValue[0] - pawnMaterialValue[1];
		value += materialValue[0] - materialValue[1];
		value += pcsqValue[0] - pcsqValue[1];

		// Endgame
		// 1. Both sides have no queens or
		// 2. Every side which has a queen has additionally no other pieces or one minorpiece maximum.
		if ((noQueen[0] || materialValue[0] <= QUEEN + BISHOP) && (noQueen[1] || materialValue[1] <= QUEEN + BISHOP)) {
			value += pcsqEndgameValue[0] - pcsqEndgameValue[1];
		} else {
			value += pcsqOpeningValue[0] - pcsqOpeningValue[1];
		}

		return value;
	}
} public class Logger {
	public static boolean noLog = false;
	String prefix;

	private Logger(String prefix) {
		this.prefix = prefix;
	}

	public static Logger getLogger(String prefix) {
		return new Logger(prefix);
	}

	public void info(Object in) {
		if (noLog) return;
		System.out.print("INFO ");
		System.out.print(prefix);
		System.out.print(" - ");
		System.out.println(in.toString());
	}

	public void debug(Object in) {
		if (noLog) return;
		System.out.print("DEBUG ");
		System.out.print(prefix);
		System.out.print(" - ");
		System.out.println(in.toString());
	}

	public void error(Object in) {
		if (noLog) return;
		System.out.print("ERROR ");
		System.out.print(prefix);
		System.out.print(" - ");
		System.out.println(in.toString());
	}

} public class TranspositionTable {
	private static final Logger logger = Logger.getLogger("MultiprobeTranspositionTable");

	public static final int DEPTH_QS_CHECKS = 0;
	public static final int DEPTH_QS_NO_CHECKS = -1;

	public static final int TYPE_EVAL = 0;
	public static final int TYPE_EXACT_SCORE = 1;
	public static final int TYPE_FAIL_LOW = 2;
	public static final int TYPE_FAIL_HIGH = 3;

	private static final int MAX_PROBES = 4;

	public long[] keys;
	public long[] infos;
	public short[] evals;

	private int size;
	private long info;
	private short eval;
	private int generation;
	private int entriesOccupied;

	private int score;
	private int sizeBits;

	public TranspositionTable(int sizeMb) {
		sizeBits = BitboardUtils.square2Index(sizeMb) + 16;
		size = 1 << sizeBits;
		keys = new long[size];
		infos = new long[size];
		evals = new short[size];
		entriesOccupied = 0;

		generation = 0;
		logger.debug("Created transposition table, size = " + size + " slots " + size * 18.0 / (1024 * 1024) + " MBytes");
	}

	public void clear() {
		entriesOccupied = 0;
		Arrays.fill(keys, 0);
	}

	public boolean search(Board board, int distanceToInitialPly, boolean exclusion) {
		info = 0;
		score = 0;
		int startIndex = (int) ((exclusion ? board.getExclusionKey() : board.getKey()) >>> (64 - sizeBits));
		// Verifies that it is really this board
		for (int i = startIndex; i < startIndex + MAX_PROBES && i < size; i++) {
			if (keys[i] == board.getKey2()) {
				info = infos[i];
				eval = evals[i];
				score = (short) ((info >>> 48) & 0xffff);

				// Fix mate score with the real distance to the initial PLY
				if (score >= SearchEngine.VALUE_IS_MATE) {
					score -= distanceToInitialPly;
				} else if (score <= -SearchEngine.VALUE_IS_MATE) {
					score += distanceToInitialPly;
				}
				return true;
			}
		}
		return false;
	}

	public int getBestMove() {
		return (int) (info & 0x1fffff);
	}

	public int getNodeType() {
		return (int) ((info >>> 21) & 0xf);
	}

	public int getGeneration() {
		return (int) ((info >>> 32) & 0xff);
	}

	public int getDepthAnalyzed() {
		int depthAnalyzed = (int) (info >>> 40) & 0xff;
		return depthAnalyzed == 0xff ? -1 : depthAnalyzed;
	}

	public int getScore() {
		return score;
	}

	public int getEval() {
		return eval;
	}

	public void newGeneration() {
		generation = (generation + 1) & 0xff;
	}

	public boolean isMyGeneration() {
		return getGeneration() == generation;
	}

	public void set(Board board, int nodeType, int distanceToInitialPly, int depthAnalyzed, int bestMove, int score, int eval, boolean exclusion) {
		long key2 = board.getKey2();
		int startIndex = (int) ((exclusion ? board.getExclusionKey() : board.getKey()) >>> (64 - sizeBits));
		int replaceIndex = startIndex;
		int replaceImportance = Integer.MAX_VALUE; // A higher value, so the first entry will be the default

		// Fix mate score with the real distance to mate from the current PLY, not from the initial PLY
		if (score >= SearchEngine.VALUE_IS_MATE) {
			score += distanceToInitialPly;
		} else if (score <= -SearchEngine.VALUE_IS_MATE) {
			score -= distanceToInitialPly;
		}

		assert score >= -Evaluator.MATE && score <= Evaluator.MATE : "Fixed TT score is outside limits";
		assert Math.abs(eval) < SearchEngine.VALUE_IS_MATE || Math.abs(eval) == Evaluator.MATE || eval == Evaluator.NO_VALUE : "Storing a eval value in the TT outside limits";

		for (int i = startIndex; i < startIndex + MAX_PROBES && i < size; i++) {
			info = infos[i];

			if (keys[i] == 0) { // Replace an empty TT position
				entriesOccupied++;
				replaceIndex = i;
				break;
			} else if (keys[i] == key2) { // Replace the same position
				replaceIndex = i;
				if (bestMove == Move.NONE) { // Keep previous best move
					bestMove = getBestMove();
				}
				break;
			}

			// Calculates a value with this TT entry importance
			int entryImportance = (getNodeType() == TYPE_EXACT_SCORE ? 10 : 0) // Bonus for the PV entries
					- getGenerationDelta() // The older the generation, the less importance
					+ getDepthAnalyzed(); // The more depth, the more importance

			// We will replace the less important entry
			if (entryImportance < replaceImportance) {
				replaceImportance = entryImportance;
				replaceIndex = i;
			}
		}

		keys[replaceIndex] = key2;
		info = (bestMove & 0x1fffff) | ((nodeType & 0xf) << 21) | (((long) (generation & 0xff)) << 32) | (((long) (depthAnalyzed & 0xff)) << 40)
				| (((long) (score & 0xffff)) << 48);

		infos[replaceIndex] = info;
		evals[replaceIndex] = (short) eval;
	}

	/**
	 * Returns the difference between the current generation and the entry generation (max 255)
	 */
	private int getGenerationDelta() {
		byte entryGeneration = (byte) ((info >>> 32) & 0xff);
		return (generation >= entryGeneration ? generation - entryGeneration : 256 + generation - entryGeneration);
	}

	public int getHashFull() {
		return (int) (1000L * entriesOccupied / size);
	}
} public class Random implements Serializable {

  private static final double multiplierHi = 0x5de;
  private static final double multiplierLo = 0xece66d;
  private static final double twoToThe24 = 16777216.0;
  private static final double twoToThe31 = 2147483648.0;
  private static final double twoToThe32 = 4294967296.0;
  private static final double twoToTheMinus24 =
    5.9604644775390625e-8;
  private static final double twoToTheMinus26 =
    1.490116119384765625e-8;
  private static final double twoToTheMinus31 =
    4.656612873077392578125e-10;
  private static final double twoToTheMinus53 =
    1.1102230246251565404236316680908203125e-16;
  private static final double[] twoToTheXMinus24 = new double[25];
  private static final double[] twoToTheXMinus48 = new double[33];

  /**
   * A value used to avoid two random number generators produced at the same
   * time having the same seed.
   */
  private static int uniqueSeed = 0;

  // Initialize power-of-two tables
  static {
    double twoToTheXMinus48Tmp = 1.52587890625e-5; // 1.0 / (1 << (48 - 32));
    for (int i = 32; i >= 0; i--) {
      twoToTheXMinus48[i] = twoToTheXMinus48Tmp;
      twoToTheXMinus48Tmp *= 0.5;
    }
    
    double twoToTheXMinus24Tmp = 1.0;
    for (int i = 24; i >= 0; i--) {
      twoToTheXMinus24[i] = twoToTheXMinus24Tmp;
      twoToTheXMinus24Tmp *= 0.5;
    }
  }

  /**
   * Return the number of milliseconds since 1/1/1970 as a double
   * in order to avoid the use os LongLib.
   */
  private static native double currentTimeMillis() /*-{
    return (new Date()).getTime();
  }-*/;

  /**
   * The boolean value indicating if the second Gaussian number is available.
   * 
   * @serial
   */
  private boolean haveNextNextGaussian = false;

  /**
   * The second Gaussian generated number.
   * 
   * @serial
   */
  private double nextNextGaussian;
  
  /**
   * The high 24 bits of the 48=bit seed value.
   * 
   * @serial It is associated with the internal state of this generator.
   */
  private double seedhi;

  /**
   * The low 24 bits of the 48=bit seed value.
   * 
   * @serial It is associated with the internal state of this generator.
   */
  private double seedlo;
  
  /**
   * Construct a random generator with the current time of day in milliseconds
   * plus a unique counter value as the initial state.
   * 
   * @see #setSeed
   */
  public Random() {
    double seed = uniqueSeed++ + currentTimeMillis();
    int hi = (int) Math.floor(seed * twoToTheMinus24) & 0xffffff;
    int lo = (int) (seed - (hi * twoToThe24));
    setSeed(hi, lo);
  }

  /**
   * Construct a random generator with the given {@code seed} as the initial
   * state.
   * 
   * @param seed the seed that will determine the initial state of this random
   *          number generator.
   * @see #setSeed
   */
  public Random(long seed) {
    setSeed(seed);
  }

  /**
   * Returns the next pseudo-random, uniformly distributed {@code boolean} value
   * generated by this generator.
   * 
   * @return a pseudo-random, uniformly distributed boolean value.
   */
  public boolean nextBoolean() {
    return nextInternal(1) != 0;
  }

  /**
   * Modifies the {@code byte} array by a random sequence of {@code byte}s
   * generated by this random number generator.
   * 
   * @param buf non-null array to contain the new random {@code byte}s.
   * @see #next
   */
  public void nextBytes(byte[] buf) {
    if (buf == null) {
      throw new NullPointerException();
    }

    int rand = 0, count = 0, loop = 0;
    while (count < buf.length) {
      if (loop == 0) {
        rand = (int) nextInternal(32);
        loop = 3;
      } else {
        loop--;
      }
      buf[count++] = (byte) rand;
      rand >>= 8;
    }
  }

  /**
   * Generates a normally distributed random {@code double} number between 0.0
   * inclusively and 1.0 exclusively.
   * 
   * @return a random {@code double} in the range [0.0 - 1.0)
   * @see #nextFloat
   */
  public double nextDouble() {
    return nextInternal(26) * twoToTheMinus26 +
      nextInternal(27) * twoToTheMinus53;
  }

  /**
   * Generates a normally distributed random {@code float} number between 0.0
   * inclusively and 1.0 exclusively.
   * 
   * @return float a random {@code float} number between [0.0 and 1.0)
   * @see #nextDouble
   */
  public float nextFloat() {
    return (float) (nextInternal(24) * twoToTheMinus24);
  }

  /**
   * Pseudo-randomly generates (approximately) a normally distributed {@code
   * double} value with mean 0.0 and a standard deviation value of {@code 1.0}
   * using the <i>polar method<i> of G. E. P. Box, M. E. Muller, and G.
   * Marsaglia, as described by Donald E. Knuth in <i>The Art of Computer
   * Programming, Volume 2: Seminumerical Algorithms</i>, section 3.4.1,
   * subsection C, algorithm P.
   * 
   * @return a random {@code double}
   * @see #nextDouble
   */
  public synchronized double nextGaussian() {
    if (haveNextNextGaussian) {
      // if X1 has been returned, return the second Gaussian
      haveNextNextGaussian = false;
      return nextNextGaussian;
    }
    
    double v1, v2, s;
    do {
      // Generates two independent random variables U1, U2
      v1 = 2 * nextDouble() - 1;
      v2 = 2 * nextDouble() - 1;
      s = v1 * v1 + v2 * v2;
    } while (s >= 1);

    // See errata for TAOCP vol. 2, 3rd ed. for proper handling of s == 0 case
    // (page 5 of http://www-cs-faculty.stanford.edu/~uno/err2.ps.gz)
    double norm = (s == 0) ? 0.0 : Math.sqrt(-2.0 * Math.log(s) / s);
    nextNextGaussian = v2 * norm;
    haveNextNextGaussian = true;
    return v1 * norm;
  }

  /**
   * Generates a uniformly distributed 32-bit {@code int} value from the random
   * number sequence.
   * 
   * @return a uniformly distributed {@code int} value.
   * @see java.lang.Integer#MAX_VALUE
   * @see java.lang.Integer#MIN_VALUE
   * @see #next
   * @see #nextLong
   */
  public int nextInt() {
    return (int) nextInternal(32);
  }

  /**
   * Returns a new pseudo-random {@code int} value which is uniformly
   * distributed between 0 (inclusively) and the value of {@code n}
   * (exclusively).
   * 
   * @param n the exclusive upper border of the range [0 - n).
   * @return a random {@code int}.
   */
  public int nextInt(int n) {
    if (n > 0) {
      if ((n & -n) == n) {
        return (int) ((n * nextInternal(31)) * twoToTheMinus31);
      }
      double bits, val;
      do {
        bits = nextInternal(31);
        val = bits % n;
      } while (bits - val + (n - 1) < 0);
      return (int) val;
    }
    
    throw new IllegalArgumentException();
  }
  
  /**
   * Generates a uniformly distributed 64-bit integer value from the random
   * number sequence.
   * 
   * @return 64-bit random integer.
   * @see java.lang.Integer#MAX_VALUE
   * @see java.lang.Integer#MIN_VALUE
   * @see #next
   * @see #nextInt()
   * @see #nextInt(int)
   */
  public long nextLong() {
    return ((long) nextInternal(32) << 32) + (long) nextInternal(32);
  }

  /**
   * Modifies the seed a using linear congruential formula presented in <i>The
   * Art of Computer Programming, Volume 2</i>, Section 3.2.1.
   * 
   * @param seed the seed that alters the state of the random number generator.
   * @see #next
   * @see #Random()
   * @see #Random(long)
   */
  public synchronized void setSeed(long seed) {
    setSeed((int) ((seed >> 24) & 0xffffff), (int) (seed & 0xffffff));
  }
  
  /**
   * Returns a pseudo-random uniformly distributed {@code int} value of the
   * number of bits specified by the argument {@code bits} as described by
   * Donald E. Knuth in <i>The Art of Computer Programming, Volume 2:
   * Seminumerical Algorithms</i>, section 3.2.1.
   * 
   * @param bits number of bits of the returned value.
   * @return a pseudo-random generated int number.
   * @see #nextBytes
   * @see #nextDouble
   * @see #nextFloat
   * @see #nextInt()
   * @see #nextInt(int)
   * @see #nextGaussian
   * @see #nextLong
   */
  protected synchronized int next(int bits) {
    return (int) nextInternal(bits);
  }
  
  private synchronized double nextInternal(int bits) {
    double hi = seedhi * multiplierLo + seedlo * multiplierHi;
    double lo = seedlo * multiplierLo + 0xb;
    double carry = Math.floor(lo * twoToTheMinus24);
    hi += carry;
    lo -= carry * twoToThe24;
    hi %= twoToThe24;
    
    seedhi = hi;
    seedlo = lo;
    
    if (bits <= 24) {
      // High bits of seedhi, shifted right
      return Math.floor(seedhi * twoToTheXMinus24[bits]);
    } else {
      // All bits of seedhi, shifted left
      double h = seedhi * (1 << (bits - 24));
      // High bits of seedlo, shifted right
      double l = Math.floor(seedlo * twoToTheXMinus48[bits]);
      // Sum of high and low bits
      double dval = h + l;
      // Force sign based on bit 31
      if (dval >= twoToThe31) {
        dval -= twoToThe32;
      }
      return dval;
    }
  }
  
  // seedhi and seedlo should be integers from 0 to 2^24 - 1
  private synchronized void setSeed(int seedhi, int seedlo) {
    this.seedhi = seedhi ^ 0x5de;
    this.seedlo = seedlo ^ 0xece66d;
    haveNextNextGaussian = false;
  }
}
import com.alonsoruibal.chess.Board;
import com.alonsoruibal.chess.Config;
import com.alonsoruibal.chess.Move;
import com.alonsoruibal.chess.bitboard.BitboardUtils;
import com.alonsoruibal.chess.evaluation.CompleteEvaluator;
import com.alonsoruibal.chess.evaluation.Evaluator;
import com.alonsoruibal.chess.evaluation.ExperimentalEvaluator;
import com.alonsoruibal.chess.evaluation.SimplifiedEvaluator;
import com.alonsoruibal.chess.log.Logger;
import com.alonsoruibal.chess.tt.TranspositionTable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
				int futilityValue = futilityBase + Evaluator.PIECE_VALUES[Move.getPieceCaptured(board, node.move)];
				if (futilityValue <= alpha) {
					bestScore = Math.max(bestScore, futilityValue);
					continue;
				}
				if (futilityBase <= alpha && node.moveIterator.getLastMoveSee() <= 0) {
					bestScore = Math.max(bestScore, futilityBase);
					continue;
				}
			}

			board.doMove(node.move, false, false);
			assert board.getCheck() == Move.isCheck(node.move) : "Check flag not generated properly";

			int score = -quiescentSearch(qsdepth + 1, -beta, -bestScore);
			board.undoMove();
			if (score > bestScore) {
				bestMove = node.move;
				bestScore = score;
				if (score >= beta) {
					break;
				}
			}
		}

		if (checkEvasion && moveCount == 0) {
			return valueMatedIn(distanceToInitialPly);
		}
		tt.set(board,
				bestScore <= alpha ? TranspositionTable.TYPE_FAIL_LOW
						: bestScore >= beta ? TranspositionTable.TYPE_FAIL_HIGH
						: TranspositionTable.TYPE_EXACT_SCORE,
				distanceToInitialPly, ttDepth,
				bestMove, bestScore, node.staticEval, false);

		return bestScore;
	}

	/**
	 * Search Root, PV and null window
	 */
	public int search(int nodeType, int depthRemaining, int alpha, int beta, boolean allowPrePruning, int excludedMove) throws SearchFinishedException {
		assert depthRemaining > 0 : "Wrong depthRemaining";

		if (nodeType != NODE_ROOT && globalBestMove != Move.NONE && (System.currentTimeMillis() > thinkToTime || nodeCount > thinkToNodes)) {
			throw new SearchFinishedException();
		}

		int distanceToInitialPly = board.getMoveNumber() - initialPly;

		if (nodeType == NODE_PV || nodeType == NODE_ROOT) {
			if (distanceToInitialPly > selDepth) {
				selDepth = distanceToInitialPly;
			}
		}

		// It checks draw by three fold repetition, fifty moves rule and no material to mate
		if (board.isDraw()) {
			return evaluateDraw(distanceToInitialPly);
		}

		// Mate distance pruning
		alpha = Math.max(valueMatedIn(distanceToInitialPly), alpha);
		beta = Math.min(valueMateIn(distanceToInitialPly + 1), beta);
		if (alpha >= beta) {
			return alpha;
		}

		Node node = nodes[distanceToInitialPly];

		int ttScore = 0;
		int ttNodeType = 0;
		int ttDepthAnalyzed = 0;
		int score = 0;

		ttProbe++;
		boolean foundTT = tt.search(board, distanceToInitialPly, excludedMove != Move.NONE);
		if (foundTT) {
			if (nodeType != NODE_ROOT && canUseTT(depthRemaining, alpha, beta)) {
				if (distanceToInitialPly + tt.getDepthAnalyzed() > selDepth) {
					selDepth = distanceToInitialPly + tt.getDepthAnalyzed();
				}

				historyGood(node, tt.getBestMove(), depthRemaining);
				return tt.getScore();
			}
			node.ttMove = tt.getBestMove();
			ttScore = tt.getScore();
			ttNodeType = tt.getNodeType();
			ttDepthAnalyzed = tt.getDepthAnalyzed();
		} else {
			node.ttMove = Move.NONE;
		}

		boolean checkEvasion = board.getCheck();
		boolean mateThreat = false;
		int eval = -Evaluator.MATE;
		node.staticEval = -Evaluator.MATE;

		if (!checkEvasion) {
			// Do a static eval, in case of exclusion and not found in the TT, search again with the normal key
			boolean evalTT = excludedMove == Move.NONE || foundTT ? foundTT : tt.search(board, distanceToInitialPly, false);
			evaluate(node, evalTT);
			eval = refineEval(node, foundTT);
		}

		// If we have more depths than possible...
		if (distanceToInitialPly >= MAX_DEPTH - 1) {
			return checkEvasion ? evaluateDraw(distanceToInitialPly) : eval; // Return a drawish score if we are in check
		}

		if (!checkEvasion
				&& allowPrePruning) {
			// Hyatt's Razoring http://chessprogramming.wikispaces.com/Razoring
			if (nodeType == NODE_NULL
					&& node.ttMove == Move.NONE
					&& depthRemaining < RAZORING_MARGIN.length
					&& Math.abs(beta) < VALUE_IS_MATE
					&& Math.abs(eval) < Evaluator.KNOWN_WIN
					&& eval + RAZORING_MARGIN[depthRemaining] < beta
					&& (board.pawns & ((board.whites & BitboardUtils.R7) | (board.blacks & BitboardUtils.R2))) == 0) { // No pawns on 7TH