package org.myrobotlab.chess; // // Board.java // ChessApp // // Created by Peter Hunter on Sun Dec 30 2001. // Java version copyright (c) 2001 Peter Hunter. All rights reserved. // This code is heavily based on Tom Kerrigan's tscp, for which he // owns the copyright, and is used with his permission. All rights are // reserved by the owners of the respective copyrights. import java.util.ArrayList; import java.util.Collection; import java.util.List; final public class Board implements Constants { final static int DOUBLED_PAWN_PENALTY = 10; final static int ISOLATED_PAWN_PENALTY = 20; final static int BACKWARDS_PAWN_PENALTY = 8; final static int PASSED_PAWN_BONUS = 20; final static int ROOK_SEMI_OPEN_FILE_BONUS = 10; final static int ROOK_OPEN_FILE_BONUS = 15; final static int ROOK_ON_SEVENTH_BONUS = 20; final static int HIST_STACK = 400; public int side = LIGHT; public int xside = DARK; private int castle = 15; private int ep = -1; public int fifty = 0; private int hply = 0; int history[][] = new int[64][64]; private HistoryData histDat[] = new HistoryData[HIST_STACK]; private int pawnRank[][] = new int[2][10]; private int pieceMat[] = { 3100, 3100 }; private int pawnMat[] = new int[2]; private int color[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private int piece[] = { 3, 1, 2, 4, 5, 2, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 2, 4, 5, 2, 1, 3 }; long pawnBits[] = { 0x00ff000000000000L, 0xff00L }; long pieceBits[] = { 0xffff000000000000L, 0xffffL }; long oldPawnBits = 0; long oldPieceBits = 0; int kingSquare[] = { 60, 4 }; final private static char pieceChar[] = { 'P', 'N', 'B', 'R', 'Q', 'K' }; final private static boolean slide[] = { false, false, true, true, true, false }; final private static int offsets[] = { 0, 8, 4, 4, 8, 8 }; final private static int offset[][] = { { 0, 0, 0, 0, 0, 0, 0, 0 }, { -21, -19, -12, -8, 8, 12, 19, 21 }, { -11, -9, 9, 11, 0, 0, 0, 0 }, { -10, -1, 1, 10, 0, 0, 0, 0 }, { -11, -10, -9, -1, 1, 9, 10, 11 }, { -11, -10, -9, -1, 1, 9, 10, 11 } }; final private static int mailbox[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, -1, -1, 8, 9, 10, 11, 12, 13, 14, 15, -1, -1, 16, 17, 18, 19, 20, 21, 22, 23, -1, -1, 24, 25, 26, 27, 28, 29, 30, 31, -1, -1, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, 40, 41, 42, 43, 44, 45, 46, 47, -1, -1, 48, 49, 50, 51, 52, 53, 54, 55, -1, -1, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; private final static int mailbox64[] = { 21, 22, 23, 24, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 38, 41, 42, 43, 44, 45, 46, 47, 48, 51, 52, 53, 54, 55, 56, 57, 58, 61, 62, 63, 64, 65, 66, 67, 68, 71, 72, 73, 74, 75, 76, 77, 78, 81, 82, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98 }; private final static int castleMask[] = { 7, 15, 15, 15, 3, 15, 15, 11, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 13, 15, 15, 15, 12, 15, 15, 14 }; /* the values of the pieces */ private final static int pieceValue[] = { 100, 300, 300, 500, 900, 0 }; /* * The "pcsq" arrays are piece/square tables. They're values added to the * material value of the piece based on the location of the piece. */ private final static int pawnPcsq[] = { 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 20, 20, 15, 10, 5, 4, 8, 12, 16, 16, 12, 8, 4, 3, 6, 9, 12, 12, 9, 6, 3, 2, 4, 6, 8, 8, 6, 4, 2, 1, 2, 3, -10, -10, 3, 2, 1, 0, 0, 0, -40, -40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; private final static int knightPcsq[] = { -10, -10, -10, -10, -10, -10, -10, -10, -10, 0, 0, 0, 0, 0, 0, -10, -10, 0, 5, 5, 5, 5, 0, -10, -10, 0, 5, 10, 10, 5, 0, -10, -10, 0, 5, 10, 10, 5, 0, -10, -10, 0, 5, 5, 5, 5, 0, -10, -10, 0, 0, 0, 0, 0, 0, -10, -10, -30, -10, -10, -10, -10, -30, -10 }; private final static int bishopPcsq[] = { -10, -10, -10, -10, -10, -10, -10, -10, -10, 0, 0, 0, 0, 0, 0, -10, -10, 0, 5, 5, 5, 5, 0, -10, -10, 0, 5, 10, 10, 5, 0, -10, -10, 0, 5, 10, 10, 5, 0, -10, -10, 0, 5, 5, 5, 5, 0, -10, -10, 0, 0, 0, 0, 0, 0, -10, -10, -10, -20, -10, -10, -20, -10, -10 }; private final static int kingPcsq[] = { -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -20, -20, -20, -20, -20, -20, -20, -20, 0, 20, 40, -20, 0, -20, 40, 20 }; private final static int kingEndgamePcsq[] = { 0, 10, 20, 30, 30, 20, 10, 0, 10, 20, 30, 40, 40, 30, 20, 10, 20, 30, 40, 50, 50, 40, 30, 20, 30, 40, 50, 60, 60, 50, 40, 30, 30, 40, 50, 60, 60, 50, 40, 30, 20, 30, 40, 50, 50, 40, 30, 20, 10, 20, 30, 40, 40, 30, 20, 10, 0, 10, 20, 30, 30, 20, 10, 0 }; /* * The flip array is used to calculate the piece/square values for DARK * pieces. The piece/square value of a LIGHT pawn is pawnPcsq[sq] and the * value of a DARK pawn is pawnPcsq[flip[sq]] */ private final static int flip[] = { 56, 57, 58, 59, 60, 61, 62, 63, 48, 49, 50, 51, 52, 53, 54, 55, 40, 41, 42, 43, 44, 45, 46, 47, 32, 33, 34, 35, 36, 37, 38, 39, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 }; private static final int m1 = 0x55555555; private static final int m2 = 0x33333333; static int COL(int x) { return (x & 7); } static int ROW(int x) { return (x >> 3); } public Board() { } boolean attack(int sq, int s) { long attackSq = (1L << sq); if (s == LIGHT) { long moves = ((pawnBits[LIGHT] & 0x00fefefefefefefeL) >> 9) & attackSq; if (moves != 0) return true; moves = ((pawnBits[LIGHT] & 0x007f7f7f7f7f7f7fL) >> 7) & attackSq; if (moves != 0) return true; } else { long moves = ((pawnBits[DARK] & 0x00fefefefefefefeL) << 7) & attackSq; if (moves != 0) return true; moves = ((pawnBits[DARK] & 0x007f7f7f7f7f7f7fL) << 9) & attackSq; if (moves != 0) return true; } long pieces = pieceBits[s] ^ pawnBits[s]; while (pieces != 0) { int i = getLBit(pieces); int p = piece[i]; for (int j = 0; j < offsets[p]; ++j) for (int n = i;;) { n = mailbox[mailbox64[n] + offset[p][j]]; if (n == -1) break; if (n == sq) return true; if (color[n] != EMPTY) break; if (!slide[p]) break; } pieces &= (pieces - 1); } return false; } /* * inCheck() returns true if side s is in check and false otherwise. It just * scans the board to find side s's king and calls attack() to see if it's * being attacked. */ int eval() { int score[] = new int[2]; /* each side's score */ /* this is the first pass: set up pawnRank, and pawnMat. */ if (oldPawnBits != (pawnBits[LIGHT] | pawnBits[DARK])) { for (int i = 0; i < 10; ++i) { pawnRank[LIGHT][i] = 0; pawnRank[DARK][i] = 7; } pawnMat[LIGHT] = 0; pawnMat[DARK] = 0; long pieces = pawnBits[LIGHT]; while (pieces != 0) { int i = getLBit(pieces); pawnMat[LIGHT] += pieceValue[PAWN]; int f = COL(i) + 1; /* * add 1 because of the extra file in the array */ if (pawnRank[LIGHT][f] < ROW(i)) pawnRank[LIGHT][f] = ROW(i); pieces &= (pieces - 1); } pieces = pawnBits[DARK]; while (pieces != 0) { int i = getLBit(pieces); pawnMat[DARK] += pieceValue[PAWN]; int f = COL(i) + 1; /* * add 1 because of the extra file in the array */ if (pawnRank[DARK][f] > ROW(i)) pawnRank[DARK][f] = ROW(i); pieces &= (pieces - 1); } oldPawnBits = pawnBits[LIGHT] | pawnBits[DARK]; } /* this is the second pass: evaluate each piece */ score[LIGHT] = pieceMat[LIGHT] + pawnMat[LIGHT]; score[DARK] = pieceMat[DARK] + pawnMat[DARK]; for (int i = 0; i < 64; ++i) { if (color[i] == EMPTY) continue; if (color[i] == LIGHT) { switch (piece[i]) { case PAWN: score[LIGHT] += evalLightPawn(i); break; case KNIGHT: score[LIGHT] += knightPcsq[i]; break; case BISHOP: score[LIGHT] += bishopPcsq[i]; break; case ROOK: if (pawnRank[LIGHT][COL(i) + 1] == 0) { if (pawnRank[DARK][COL(i) + 1] == 7) score[LIGHT] += ROOK_OPEN_FILE_BONUS; else score[LIGHT] += ROOK_SEMI_OPEN_FILE_BONUS; } if (ROW(i) == 1) score[LIGHT] += ROOK_ON_SEVENTH_BONUS; break; case KING: if (pieceMat[DARK] <= 1200) score[LIGHT] += kingEndgamePcsq[i]; else score[LIGHT] += evalLightKing(i); break; } } else { switch (piece[i]) { case PAWN: score[DARK] += evalDarkPawn(i); break; case KNIGHT: score[DARK] += knightPcsq[flip[i]]; break; case BISHOP: score[DARK] += bishopPcsq[flip[i]]; break; case ROOK: if (pawnRank[DARK][COL(i) + 1] == 7) { if (pawnRank[LIGHT][COL(i) + 1] == 0) score[DARK] += ROOK_OPEN_FILE_BONUS; else score[DARK] += ROOK_SEMI_OPEN_FILE_BONUS; } if (ROW(i) == 6) score[DARK] += ROOK_ON_SEVENTH_BONUS; break; case KING: if (pieceMat[LIGHT] <= 1200) score[DARK] += kingEndgamePcsq[flip[i]]; else score[DARK] += evalDarkKing(i); break; } } } /* * the score[] array is set, now return the score relative to the side to * move */ if (side == LIGHT) return score[LIGHT] - score[DARK]; return score[DARK] - score[LIGHT]; } /* * attack() returns true if square sq is being attacked by side s and false * otherwise. */ int evalDarkKing(int sq) { int r; int i; r = kingPcsq[flip[sq]]; if (COL(sq) < 3) { r += evalDkp(1); r += evalDkp(2); r += evalDkp(3) / 2; } else if (COL(sq) > 4) { r += evalDkp(8); r += evalDkp(7); r += evalDkp(6) / 2; } else { for (i = COL(sq); i <= COL(sq) + 2; ++i) if ((pawnRank[LIGHT][i] == 0) && (pawnRank[DARK][i] == 7)) r -= 10; } r *= pieceMat[LIGHT]; r /= 3100; return r; } /* * gen() generates pseudo-legal moves for the current position. It scans the * board to find friendly pieces and then determines what squares they attack. * When it finds a piece/square combination, it calls genPush to put the move * on the "move stack." */ int evalDarkPawn(int sq) { int r = 0; /* the value to return */ int f = COL(sq) + 1; /* the pawn's file */ r += pawnPcsq[flip[sq]]; /* if there's a pawn behind this one, it's doubled */ if (pawnRank[DARK][f] < ROW(sq)) r -= DOUBLED_PAWN_PENALTY; /* * if there aren't any friendly pawns on either side of this one, it's * isolated */ if ((pawnRank[DARK][f - 1] == 7) && (pawnRank[DARK][f + 1] == 7)) r -= ISOLATED_PAWN_PENALTY; /* if it's not isolated, it might be backwards */ else if ((pawnRank[DARK][f - 1] > ROW(sq)) && (pawnRank[DARK][f + 1] > ROW(sq))) r -= BACKWARDS_PAWN_PENALTY; /* add a bonus if the pawn is passed */ if ((pawnRank[LIGHT][f - 1] <= ROW(sq)) && (pawnRank[LIGHT][f] <= ROW(sq)) && (pawnRank[LIGHT][f + 1] <= ROW(sq))) r += ROW(sq) * PASSED_PAWN_BONUS; return r; } /* * genCaps() is basically a copy of gen() that's modified to only generate * capture and promote moves. It's used by the quiescence search. */ int evalDkp(int f) { int r = 0; if (pawnRank[DARK][f] == 1) ; else if (pawnRank[DARK][f] == 2) r -= 10; else if (pawnRank[DARK][f] != 7) r -= 20; else r -= 25; if (pawnRank[LIGHT][f] == 0) r -= 15; else if (pawnRank[LIGHT][f] == 2) r -= 10; else if (pawnRank[LIGHT][f] == 3) r -= 5; return r; } /* * genPush() puts a move on the move stack, unless it's a pawn promotion that * needs to be handled by genPromote(). It also assigns a score to the move * for alpha-beta move ordering. If the move is a capture, it uses MVV/LVA * (Most Valuable Victim/Least Valuable Attacker). Otherwise, it uses the * move's history heuristic value. Note that 1,000,000 is added to a capture * move's score, so it always gets ordered above a "normal" move. */ int evalLightKing(int sq) { int r = kingPcsq[sq]; /* return value */ /* * if the king is castled, use a special function to evaluate the pawns on * the appropriate side */ if (COL(sq) < 3) { r += evalLkp(1); r += evalLkp(2); r += evalLkp(3) / 2; /* * problems with pawns on the c & f files are not as severe */ } else if (COL(sq) > 4) { r += evalLkp(8); r += evalLkp(7); r += evalLkp(6) / 2; } /* * otherwise, just assess a penalty if there are open files near the king */ else { for (int i = COL(sq); i <= COL(sq) + 2; ++i) if ((pawnRank[LIGHT][i] == 0) && (pawnRank[DARK][i] == 7)) r -= 10; } /* * scale the king safety value according to the opponent's material; the * premise is that your king safety can only be bad if the opponent has * enough pieces to attack you */ r *= pieceMat[DARK]; r /= 3100; return r; } /* * genPromote() is just like genPush(), only it puts 4 moves on the move * stack, one for each possible promotion piece */ int evalLightPawn(int sq) { int r = 0; /* return value */ int f = COL(sq) + 1; /* pawn's file */ r += pawnPcsq[sq]; /* if there's a pawn behind this one, it's doubled */ if (pawnRank[LIGHT][f] > ROW(sq)) r -= DOUBLED_PAWN_PENALTY; /* * if there aren't any friendly pawns on either side of this one, it's * isolated */ if ((pawnRank[LIGHT][f - 1] == 0) && (pawnRank[LIGHT][f + 1] == 0)) r -= ISOLATED_PAWN_PENALTY; /* if it's not isolated, it might be backwards */ else if ((pawnRank[LIGHT][f - 1] < ROW(sq)) && (pawnRank[LIGHT][f + 1] < ROW(sq))) r -= BACKWARDS_PAWN_PENALTY; /* add a bonus if the pawn is passed */ if ((pawnRank[DARK][f - 1] >= ROW(sq)) && (pawnRank[DARK][f] >= ROW(sq)) && (pawnRank[DARK][f + 1] >= ROW(sq))) r += (7 - ROW(sq)) * PASSED_PAWN_BONUS; return r; } /* * makemove() makes a move. If the move is illegal, it undoes whatever it did * and returns false. Otherwise, it returns true. */ int evalLkp(int f) { int r = 0; if (pawnRank[LIGHT][f] == 6) ; /* pawn hasn't moved */ else if (pawnRank[LIGHT][f] == 5) r -= 10; /* pawn moved one square */ else if (pawnRank[LIGHT][f] != 0) r -= 20; /* pawn moved more than one square */ else r -= 25; /* no pawn on this file */ if (pawnRank[DARK][f] == 7) r -= 15; /* no enemy pawn */ else if (pawnRank[DARK][f] == 5) r -= 10; /* enemy pawn on the 3rd rank */ else if (pawnRank[DARK][f] == 4) r -= 5; /* enemy pawn on the 4th rank */ return r; } /* takeBack() is very similar to makeMove(), only backwards :) */ public List<HMove> gen() { List<HMove> ret = new ArrayList<HMove>(); long emptySlots = ~(pieceBits[LIGHT] | pieceBits[DARK]); if (side == LIGHT) { long moves = (pawnBits[LIGHT] >> 8) & emptySlots; long keep = moves; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove + 8, theMove, 16); moves &= (moves - 1); } moves = ((keep & 0x0000ff0000000000L) >> 8) & emptySlots; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove + 16, theMove, 24); moves &= (moves - 1); } moves = ((pawnBits[LIGHT] & 0x00fefefefefefefeL) >> 9) & pieceBits[DARK]; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove + 9, theMove, 17); moves &= (moves - 1); } moves = ((pawnBits[LIGHT] & 0x007f7f7f7f7f7f7fL) >> 7) & pieceBits[DARK]; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove + 7, theMove, 17); moves &= (moves - 1); } } else { long moves = (pawnBits[DARK] << 8) & emptySlots; long keep = moves; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove - 8, theMove, 16); moves &= (moves - 1); } moves = ((keep & 0xff0000L) << 8) & emptySlots; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove - 16, theMove, 24); moves &= (moves - 1); } moves = ((pawnBits[DARK] & 0x00fefefefefefefeL) << 7) & pieceBits[LIGHT]; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove - 7, theMove, 17); moves &= (moves - 1); } moves = ((pawnBits[DARK] & 0x007f7f7f7f7f7f7fL) << 9) & pieceBits[LIGHT]; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove - 9, theMove, 17); moves &= (moves - 1); } } long pieces = pieceBits[side] ^ pawnBits[side]; while (pieces != 0) { int i = getLBit(pieces); int p = piece[i]; for (int j = 0; j < offsets[p]; ++j) for (int n = i;;) { n = mailbox[mailbox64[n] + offset[p][j]]; if (n == -1) break; if (color[n] != EMPTY) { if (color[n] == xside) genPush(ret, i, n, 1); break; } genPush(ret, i, n, 0); if (!slide[p]) break; } pieces &= (pieces - 1); } /* generate castle moves */ if (side == LIGHT) { if (((castle & 1) != 0) && (piece[F1] == EMPTY) && (piece[G1] == EMPTY)) genPush(ret, E1, G1, 2); if (((castle & 2) != 0) && (piece[D1] == EMPTY) && (piece[C1] == EMPTY) && (piece[B1] == EMPTY)) genPush(ret, E1, C1, 2); } else { if (((castle & 4) != 0) && (piece[F8] == EMPTY) && (piece[G8] == EMPTY)) genPush(ret, E8, G8, 2); if (((castle & 8) != 0) && (piece[D8] == EMPTY) && (piece[C8] == EMPTY) && (piece[B8] == EMPTY)) genPush(ret, E8, C8, 2); } /* generate en passant moves */ if (ep != -1) { if (side == LIGHT) { if (COL(ep) != 0 && color[ep + 7] == LIGHT && piece[ep + 7] == PAWN) genPush(ret, ep + 7, ep, 21); if (COL(ep) != 7 && color[ep + 9] == LIGHT && piece[ep + 9] == PAWN) genPush(ret, ep + 9, ep, 21); } else { if (COL(ep) != 0 && color[ep - 9] == DARK && piece[ep - 9] == PAWN) genPush(ret, ep - 9, ep, 21); if (COL(ep) != 7 && color[ep - 7] == DARK && piece[ep - 7] == PAWN) genPush(ret, ep - 7, ep, 21); } } return ret; } List<HMove> genCaps() { List<HMove> ret = new ArrayList<HMove>(); if (side == LIGHT) { long moves = ((pawnBits[LIGHT] & 0x00fefefefefefefeL) >> 9) & pieceBits[DARK]; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove + 9, theMove, 17); moves &= (moves - 1); } moves = ((pawnBits[LIGHT] & 0x007f7f7f7f7f7f7fL) >> 7) & pieceBits[DARK]; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove + 7, theMove, 17); moves &= (moves - 1); } } else { long moves = ((pawnBits[DARK] & 0x00fefefefefefefeL) << 7) & pieceBits[LIGHT]; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove - 7, theMove, 17); moves &= (moves - 1); } moves = ((pawnBits[DARK] & 0x007f7f7f7f7f7f7fL) << 9) & pieceBits[LIGHT]; while (moves != 0) { int theMove = getLBit(moves); genPush(ret, theMove - 9, theMove, 17); moves &= (moves - 1); } } long pieces = pieceBits[side] ^ pawnBits[side]; while (pieces != 0) { int p = getLBit(pieces); for (int j = 0; j < offsets[piece[p]]; ++j) for (int n = p;;) { n = mailbox[mailbox64[n] + offset[piece[p]][j]]; if (n == -1) break; if (color[n] != EMPTY) { if (color[n] == xside) genPush(ret, p, n, 1); break; } if (!slide[piece[p]]) break; } pieces &= (pieces - 1); } if (ep != -1) { if (side == LIGHT) { if (COL(ep) != 0 && color[ep + 7] == LIGHT && piece[ep + 7] == PAWN) genPush(ret, ep + 7, ep, 21); if (COL(ep) != 7 && color[ep + 9] == LIGHT && piece[ep + 9] == PAWN) genPush(ret, ep + 9, ep, 21); } else { if (COL(ep) != 0 && color[ep - 9] == DARK && piece[ep - 9] == PAWN) genPush(ret, ep - 9, ep, 21); if (COL(ep) != 7 && color[ep - 7] == DARK && piece[ep - 7] == PAWN) genPush(ret, ep - 7, ep, 21); } } return ret; } /* * reps() returns the number of times that the current position has been * repeated. Thanks to John Stanback for this clever algorithm. */ void genPromote(Collection<HMove> ret, int from, int to, int bits) { for (char i = KNIGHT; i <= QUEEN; ++i) { HMove g = new HMove(from, to, i, (bits | 32), 'P'); g.setScore(1000000 + (i * 10)); ret.add(g); } } void genPush(Collection<HMove> ret, int from, int to, int bits) { if ((bits & 16) != 0) { if (side == LIGHT) { if (to <= H8) { genPromote(ret, from, to, bits); return; } } else { if (to >= A1) { genPromote(ret, from, to, bits); return; } } } HMove g = new HMove(from, to, 0, bits, pieceChar[piece[from]]); if (color[to] != EMPTY) g.setScore(1000000 + (piece[to] * 10) - piece[from]); else g.setScore(history[from][to]); ret.add(g); } public int getColor(int i) { return color[i]; } public int getColor(int i, int j) { return color[(i << 3) + j]; } private int getLBit(long y) { int x, shift; if ((y & 0xffffffffL) == 0) { x = (int) (y >> 32); shift = 32; } else { x = (int) y; shift = 0; } x = ~(x | -x); int a = x - ((x >> 1) & m1); int c = (a & m2) + ((a >> 2) & m2); c = (c & 0x0f0f0f0f) + ((c >> 4) & 0x0f0f0f0f); c = (c & 0xffff) + (c >> 16); c = (c & 0xff) + (c >> 8); return c + shift; } /* evalLkp(f) evaluates the Light King Pawn on file f */ public int getPiece(int i) { return piece[i]; } public int getPiece(int i, int j) { return piece[(i << 3) + j]; } public boolean inCheck(int s) { return attack(kingSquare[s], s ^ 1); } public boolean isWhiteToMove() { return (side == LIGHT); } public boolean makeMove(HMove m) { long oldBits[] = { pieceBits[LIGHT], pieceBits[DARK] }; int from, to; /* * test to see if a castle move is legal and move the rook (the king is * moved with the usual move code later) */ if ((m.bits & 2) != 0) { if (inCheck(side)) return false; switch (m.getTo()) { case 62: if (color[F1] != EMPTY || color[G1] != EMPTY || attack(F1, xside) || attack(G1, xside)) return false; from = H1; to = F1; break; case 58: if (color[B1] != EMPTY || color[C1] != EMPTY || color[D1] != EMPTY || attack(C1, xside) || attack(D1, xside)) return false; from = A1; to = D1; break; case 6: if (color[F8] != EMPTY || color[G8] != EMPTY || attack(F8, xside) || attack(G8, xside)) return false; from = H8; to = F8; break; case 2: if (color[B8] != EMPTY || color[C8] != EMPTY || color[D8] != EMPTY || attack(C8, xside) || attack(D8, xside)) return false; from = A8; to = D8; break; default: /* shouldn't get here */ from = -1; to = -1; break; } color[to] = color[from]; piece[to] = piece[from]; color[from] = EMPTY; piece[from] = EMPTY; pieceBits[side] ^= (1L << from) | (1L << to); } /* back up information so we can take the move back later. */ HistoryData h = new HistoryData(); h.m = m; to = m.getTo(); from = m.getFrom(); h.capture = piece[to]; h.castle = castle; h.ep = ep; h.fifty = fifty; h.pawnBits = new long[] { pawnBits[LIGHT], pawnBits[DARK] }; h.pieceBits = oldBits; histDat[hply++] = h; /* * update the castle, en passant, and fifty-move-draw variables */ castle &= castleMask[from] & castleMask[to]; if ((m.bits & 8) != 0) { if (side == LIGHT) ep = to + 8; else ep = to - 8; } else ep = -1; if ((m.bits & 17) != 0) fifty = 0; else ++fifty; /* move the piece */ int thePiece = piece[from]; if (thePiece == KING) kingSquare[side] = to; color[to] = side; if ((m.bits & 32) != 0) { piece[to] = m.promote; pieceMat[side] += pieceValue[m.promote]; } else piece[to] = thePiece; color[from] = EMPTY; piece[from] = EMPTY; long fromBits = 1L << from; long toBits = 1L << to; pieceBits[side] ^= fromBits | toBits; if ((m.bits & 16) != 0) { pawnBits[side] ^= fromBits; if ((m.bits & 32) == 0) pawnBits[side] |= toBits; } int capture = h.capture; if (capture != EMPTY) { pieceBits[xside] ^= toBits; if (capture == PAWN) pawnBits[xside] ^= toBits; else pieceMat[xside] -= pieceValue[capture]; } /* erase the pawn if this is an en passant move */ if ((m.bits & 4) != 0) { if (side == LIGHT) { color[to + 8] = EMPTY; piece[to + 8] = EMPTY; pieceBits[DARK] ^= (1L << (to + 8)); pawnBits[DARK] ^= (1L << (to + 8)); } else { color[to - 8] = EMPTY; piece[to - 8] = EMPTY; pieceBits[LIGHT] ^= (1L << (to - 8)); pawnBits[LIGHT] ^= (1L << (to - 8)); } } /* * switch sides and test for legality (if we can capture the other guy's * king, it's an illegal position and we need to take the move back) */ side ^= 1; xside ^= 1; if (inCheck(xside)) { takeBack(); return false; } return true; } public int reps() { int b[] = new int[64]; int c = 0; /* * count of squares that are different from the current position */ int r = 0; /* number of repetitions */ /* is a repetition impossible? */ if (fifty <= 3) return 0; /* loop through the reversible moves */ for (int i = hply - 1; i >= hply - fifty - 1; --i) { if (++b[histDat[i].m.getFrom()] == 0) --c; else ++c; if (--b[histDat[i].m.getTo()] == 0) --c; else ++c; if (c == 0) ++r; } return r; } public void takeBack() { side ^= 1; xside ^= 1; HistoryData h = histDat[--hply]; pawnBits = h.pawnBits; pieceBits = h.pieceBits; HMove m = h.m; castle = h.castle; ep = h.ep; fifty = h.fifty; int from = m.getFrom(); int to = m.getTo(); color[from] = side; if ((m.bits & 32) != 0) { piece[from] = PAWN; pieceMat[side] -= pieceValue[h.m.promote]; } else { int thePiece = piece[to]; if (thePiece == KING) kingSquare[side] = from; piece[from] = thePiece; } if (h.capture == EMPTY) { color[to] = EMPTY; piece[to] = EMPTY; } else { color[to] = xside; piece[to] = h.capture; if (h.capture != PAWN) pieceMat[xside] += pieceValue[h.capture]; } if ((m.bits & 2) != 0) { int cfrom, cto; switch (to) { case 62: cfrom = F1; cto = H1; break; case 58: cfrom = D1; cto = A1; break; case 6: cfrom = F8; cto = H8; break; case 2: cfrom = D8; cto = A8; break; default: /* shouldn't get here */ cfrom = -1; cto = -1; break; } color[cto] = side; piece[cto] = ROOK; color[cfrom] = EMPTY; piece[cfrom] = EMPTY; } if ((m.bits & 4) != 0) { if (side == LIGHT) { color[to + 8] = xside; piece[to + 8] = PAWN; } else { color[to - 8] = xside; piece[to - 8] = PAWN; } } } @Override public String toString() { int i; StringBuffer sb = new StringBuffer("\n8 "); for (i = 0; i < 64; ++i) { switch (color[i]) { case EMPTY: sb.append(" ."); break; case LIGHT: sb.append(" "); sb.append(pieceChar[piece[i]]); break; case DARK: sb.append(" "); sb.append((char) (pieceChar[piece[i]] + ('a' - 'A'))); break; default: throw new IllegalStateException("Square not EMPTY, LIGHT or DARK: " + i); } if ((i + 1) % 8 == 0 && i != 63) { sb.append("\n"); sb.append(Integer.toString(7 - ROW(i))); sb.append(" "); } } sb.append("\n\n a b c d e f g h\n\n"); return sb.toString(); } }