content: package com.chess.engine.pieces;
  
  import com.chess.engine.Alliance;
  import com.chess.engine.board.Board;
  import com.chess.engine.board.BoardUtils;
  import com.chess.engine.board.Move;
  import com.chess.engine.board.Move.MajorAttackMove;
  import com.chess.engine.board.Move.MajorMove;
  
  import java.util.*;
  
  public final class King extends Piece {
  
      private final static int[] CANDIDATE_MOVE_COORDINATES = { -9, -8, -7, -1, 1, 7, 8, 9 };
      private final static Map<Integer, int[]> PRECOMPUTED_CANDIDATES = computeCandidates();
      private final boolean isCastled;
      private final boolean kingSideCastleCapable;
      private final boolean queenSideCastleCapable;
  
      public King(final Alliance alliance,
                  final int piecePosition,
                  final boolean kingSideCastleCapable,
                  final boolean queenSideCastleCapable) {
          super(PieceType.KING, alliance, piecePosition, true);
          this.isCastled = false;
          this.kingSideCastleCapable = kingSideCastleCapable;
          this.queenSideCastleCapable = queenSideCastleCapable;
      }
  
      public King(final Alliance alliance,
                  final int piecePosition,
                  final boolean isFirstMove,
                  final boolean isCastled,
                  final boolean kingSideCastleCapable,
                  final boolean queenSideCastleCapable) {
          super(PieceType.KING, alliance, piecePosition, isFirstMove);
          this.isCastled = isCastled;
          this.kingSideCastleCapable = kingSideCastleCapable;
          this.queenSideCastleCapable = queenSideCastleCapable;
      }
  
      private static Map<Integer, int[]> computeCandidates() {
          final Map<Integer, int[]> candidates = new HashMap<>();
          for (int position = 0; position < BoardUtils.NUM_TILES; position++) {
              final int[] legalOffsets = new int[CANDIDATE_MOVE_COORDINATES.length];
              int numLegalOffsets = 0;
              for (int offset : CANDIDATE_MOVE_COORDINATES) {
                  if (isFirstColumnExclusion(position, offset) ||
                      isEighthColumnExclusion(position, offset)) {
                      continue;
                  }
                  final int destination = position + offset;
                  if (BoardUtils.isValidTileCoordinate(destination)) {
                      legalOffsets[numLegalOffsets++] = offset;
                  }
              }
              if (numLegalOffsets > 0) {
                  candidates.put(position, Arrays.copyOf(legalOffsets, numLegalOffsets));
              }
          }
          return Collections.unmodifiableMap(candidates);
      }
  
  
      public boolean isCastled() {
          return this.isCastled;
      }
  
      public boolean isKingSideCastleCapable() {
          return this.kingSideCastleCapable;
      }
  
      public boolean isQueenSideCastleCapable() {
          return this.queenSideCastleCapable;
      }
  
      @Override
      public Collection<Move> calculateLegalMoves(final Board board) {
          final List<Move> legalMoves = new ArrayList<>();
          for (final int currentCandidateOffset : PRECOMPUTED_CANDIDATES.get(this.piecePosition)) {
              final int candidateDestinationCoordinate = this.piecePosition + currentCandidateOffset;
              final Piece pieceAtDestination = board.getPiece(candidateDestinationCoordinate);
              if (pieceAtDestination == null) {
                  legalMoves.add(new MajorMove(board, this, candidateDestinationCoordinate));
              } else {
                  final Alliance pieceAtDestinationAllegiance = pieceAtDestination.getPieceAllegiance();
                  if (this.pieceAlliance != pieceAtDestinationAllegiance) {
                      legalMoves.add(new MajorAttackMove(board, this, candidateDestinationCoordinate,
                              pieceAtDestination));
                  }
              }
          }
          return Collections.unmodifiableList(legalMoves);
      }
  
      @Override
      public String toString() {
          return this.pieceType.toString();
      }
  
      @Override
      public int locationBonus() {
          return this.pieceAlliance.kingBonus(this.piecePosition);
      }
  
      @Override
      public King getMovedPiece(final Move move) {
          return PieceUtils.INSTANCE.getKing(move.getMovedPiece().getPieceAllegiance(), move.getDestinationCoordinate(), move.isCastlingMove(), true);
      }
  
      @Override
      public boolean equals(final Object other) {
          if (this == other) {
              return true;
          }
          if (!(other instanceof King)) {
              return false;
          }
          if (!super.equals(other)) {
              return false;
          }
          final King king = (King) other;
          return isCastled == king.isCastled;
      }
  
      @Override
      public int hashCode() {
          return (31 * super.hashCode()) + (isCastled ? 1 : 0);
      }
  
      private static boolean isFirstColumnExclusion(final int currentCandidate,
                                                    final int candidateDestinationCoordinate) {
          return BoardUtils.FIRST_COLUMN.get(currentCandidate)
                  && ((candidateDestinationCoordinate == -9) || (candidateDestinationCoordinate == -1) ||
                  (candidateDestinationCoordinate == 7));
      }
  
      private static boolean isEighthColumnExclusion(final int currentCandidate,
                                                     final int candidateDestinationCoordinate) {
          return BoardUtils.EIGHTH_COLUMN.get(currentCandidate)
                  && ((candidateDestinationCoordinate == -7) || (candidateDestinationCoordinate == 1) ||
                  (candidateDestinationCoordinate == 9));
      }
  }