class Color:
    def __init__(self, raw):
    def from_string(cls, string):
    def __repr__(self):
    def __str__(self):
    def __bool__(self):
    def __int__(self):
    def __key(self):
    def __hash__(self):
    def __neg__(self):
    def __eq__(self, other):
    def __ne__(self, other): MOVEMENT = 0
CAPTURE = 1
KING_SIDE_CASTLE = 2
QUEEN_SIDE_CASTLE = 3
EN_PASSANT = 4
PROMOTE = 5
CAPTURE_AND_PROMOTE = 6
NOT_IMPLEMENTED = 7
LONG_ALG = 8 class Location:
    def __init__(self, rank, file):
        """
        Creates a location on a chessboard given x and y coordinates.

        :type: rank: int
        :type: file: int
        """
        if rank < 0 or rank > 7 or file < 0 or file > 7:
            raise IndexError("Location must be on the board")

        self._rank = rank
        self._file = file

    @classmethod
    def from_string(cls, alg_str):
        """
        Creates a location from a two character string consisting of 
        the file then rank written in algebraic notation.
        
        Examples: e4, b5, a7

        :type: alg_str: str
        :rtype: Location
        """
        try:
            return cls(int(alg_str[1]) - 1, ord(alg_str[0]) - 97)
        except ValueError as e:
            raise ValueError("Location.from_string {} invalid: {}".format(alg_str, e))

    @property
    def rank(self):
        return self._rank

    @property
    def file(self):
        return self._file

    def __key(self):
        return self.rank, self.file

    def __hash__(self):
        return hash(self.__key())

    def __eq__(self, other):
        """
        Tests to see if both locations
        are the same ie rank and file is 
        the same.

        :type: other: Location
        """
        if not isinstance(other, self.__class__):
            raise TypeError("Cannot compare other types with Location")

        return int(self.rank) == int(other.rank) and \
            int(self.file) == int(other.file)

    def __ne__(self, other):
        return not self.__eq__(other)

    def __repr__(self):
        return "Location({}, {} ({}))".format(self._rank, self._file, str(self))

    def __str__(self):
        """
        Finds string representation of Location in algebraic form ie "e4"

        :rtype: str
        """
        if self._rank is None:
            rank_str = ""
        else:
            rank_str = str(self._rank + 1)

        if self._file is None:
            file_str = ""
        else:
            file_str = chr(self._file + 97)

        return file_str + rank_str

    def on_board(self):
        """
        Returns if the move is on the board or not.
        If the rank and file are both in between
        0 and 7, this method will return True.

        :rtype: bool
        """
        if -1 < self._rank < 8 and \
                -1 < self._file < 8:
            return True

        return False

    def shift(self, direction):
        """
        Shifts in direction provided by ``Direction`` enum.

        :type: direction: Direction
        :rtype: Location
        """
        try:
            if direction == Direction.UP:
                return self.shift_up()
            elif direction == Direction.DOWN:
                return self.shift_down()
            elif direction == Direction.RIGHT:
                return self.shift_right()
            elif direction == Direction.LEFT:
                return self.shift_left()
            else:
                raise IndexError("Invalid direction {}".format(direction))
        except IndexError as e:
            raise IndexError(e)

    def shift_up(self, times=1):
        """
        Finds Location shifted up by 1

        :rtype: Location
        """
        try:
            return Location(self._rank + times, self._file)
        except IndexError as e:
            raise IndexError(e)

    def shift_down(self, times=1):
        """
        Finds Location shifted down by 1

        :rtype: Location
        """
        try:
            return Location(self._rank - times, self._file)
        except IndexError as e:
            raise IndexError(e)

    def shift_forward(self, ref_color, times=1):
        direction = 1 if ref_color == color.white else -1
        try:
            return Location(self._rank + times*direction, self._file)
        except IndexError as e:
            raise IndexError(e)

    def shift_back(self, ref_color, times=1):
        direction = -1 if ref_color == color.white else 1
        try:
            return Location(self._rank + times*direction, self._file)
        except IndexError as e:
            raise IndexError(e)

    def shift_right(self, times=1):
        """
        Finds Location shifted right by 1

        :rtype: Location
        """
        try:
            return Location(self._rank, self._file + times)
        except IndexError as e:
            raise IndexError(e)

    def shift_left(self, times=1):
        """
        Finds Location shifted left by 1

        :rtype: Location
        """
        try:
            return Location(self._rank, self._file - times)
        except IndexError as e:
            raise IndexError(e)

    def shift_up_right(self, times=1):
        """
        Finds Location shifted up right by 1

        :rtype: Location
        """
        try:
            return Location(self._rank + times, self._file + times)
        except IndexError as e:
            raise IndexError(e)

    def shift_up_left(self, times=1):
        """
        Finds Location shifted up left by 1

        :rtype: Location
        """
        try:
            return Location(self._rank + times, self._file - times)
        except IndexError as e:
            raise IndexError(e)

    def shift_down_right(self, times=1):
        """
        Finds Location shifted down right by 1

        :rtype: Location
        """
        try:
            return Location(self._rank - times, self._file + times)
        except IndexError as e:
            raise IndexError(e)

    def shift_down_left(self, times=1):
        """
        Finds Location shifted down left by 1

        :rtype: Location
        """
        try:
            return Location(self._rank - times, self._file - times)
        except IndexError as e:
            raise IndexError(e) class Move:
    def __init__(self,
                 end_loc,
                 piece,
                 status,
                 start_loc,
                 promoted_to_piece=None):
        """
        Constructor to create move using ``Location``

        :type: end_loc: Location
        :type: piece: Piece
        :type: status: int
        """
        self._end_loc = end_loc
        self._status = status
        self._piece = piece
        self._start_loc = start_loc
        self.color = piece.color
        self.promoted_to_piece = promoted_to_piece

    @property
    def end_loc(self):
        return self._end_loc

    @property
    def status(self):
        return self._status

    @property
    def piece(self):
        return self._piece

    def __key(self):
        return self.end_loc, \
               self.piece, \
               self.status, \
               self.start_loc, \
               self.promoted_to_piece

    def __hash__(self):
        return hash(self.__key())

    def __eq__(self, other):
        """
        Finds if move is same move as this one.
        :type: other: Move
        """
        if not isinstance(other, self.__class__):
            raise TypeError("Cannot compare type {} with Move".format(type(other)))

        for index, item in enumerate(self.__key()):
            if not self._check_equals_or_none(item, other.__key()[index]):
                return False

        return True

    @staticmethod
    def _check_equals_or_none(var1, var2):
        """
        If either is None then return True,
        otherwise compare them and return
        if they are equal.

        :type: var1: object
        :type: var2: object
        :rtype: bool
        """
        if var1 is None or var2 is None:
            return True

        return var1 == var2

    def __ne__(self, other):
        return not self.__eq__(other)

    def __repr__(self):
        return "Move({})".format(self.__dict__)

    def __str__(self):
        """
        Finds string representation in long algebraic notation

        :rtype: str
        """
        move_str = str(self._start_loc) + str(self._end_loc)

        if self.promoted_to_piece is not None:
            move_str += str(self.promoted_to_piece)

        return move_str

    @property
    def start_loc(self):
        """
        Finds start Location of move if specified.
        Otherwise throws an AttributeError

        :rtype: Location
        """
        return self._start_loc

    def would_move_be_promotion(self):
        """
        Finds if move from current location would be a promotion
        """
        return (self._end_loc.rank == 0 and not self.color) or \
            (self._end_loc.rank == 7 and self.color) class Piece:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __init__(self, input_color, location):
        """
        Initializes a piece that is capable of moving

        :type: input_color: Color
        :type: location: Location
        :type: white_symbol: str
        :type: black_symbol: str
        """
        assert isinstance(input_color, Color)
        assert isinstance(location, Location)

        self.color = input_color
        self.location = location

        self.cross_fn = [lambda x: x.shift_up(), lambda x: x.shift_right(),
                         lambda x: x.shift_down(), lambda x: x.shift_left()]

        self.diag_fn = [lambda x: x.shift_up_right(), lambda x: x.shift_up_left(),
                        lambda x: x.shift_down_right(), lambda x: x.shift_down_left()]

    def __key(self):
        return self.color, self.location

    def __hash__(self):
        return hash(str(self))

    def __eq__(self, other):
        """
        Finds out if piece is the same type and color as self
        :type: other: Piece
        """
        return type(other) is type(self) and other.color == self.color

    def __ne__(self, other):
        return not self.__eq__(other)

    def __repr__(self):
        return "{}({}, {})".format(self.__class__, self.color, self.location)

    @abstractmethod
    def __str__(self):
        pass

    @abstractmethod
    def _symbols(self):
        pass

    @abstractmethod
    def possible_moves(self, position):
        pass

    def __copy__(self):
        return self.__class__(self.color, self.location)

    @property
    def symbol(self):
        return self._symbols()[self.color]

    def contains_opposite_color_piece(self, square, position):
        """
        Finds if square on the board is occupied by a ``Piece``
        belonging to the opponent.

        :type: square: Location
        :type: position: Board
        :rtype: bool
        """
        return not position.is_square_empty(square) and \
            position.piece_at_square(square).color != self.color

    def create_move(self, end_loc, status):
        # TODO: fix circular imports
        from chess_py import Move
        return Move(end_loc=end_loc,
                    piece=self,
                    status=status,
                    start_loc=self.location) class Bishop(Rook, Piece):
    def __init__(self, input_color, location):
        """
        Creates Bishop object that can be compared to and return possible moves

        :type: input_color: Color
        """
        Piece.__init__(self, input_color, location)

    def _symbols(self):
        return {color.white: "♝", color.black: "♗"}

    def __str__(self):
        return "B"

    def possible_moves(self, position):
        """
        Returns all possible bishop moves.

        :type: position: Board
        :rtype: list
        """

        for move in itertools.chain(*[self.moves_in_direction(fn, position) for fn in self.diag_fn]):
            yield move class King(Piece):
    def __init__(self, input_color, location):
        """
        Creates a King.

        :type: input_color: Color
        :type: location: Location
        """
        super(King, self).__init__(input_color, location)
        self.has_moved = False
        self.cardinal_directions = self.cross_fn + self.diag_fn

    def _symbols(self):
        return {color.white: "♚", color.black: "♔"}

    def __str__(self):
        return "K"

    def in_check_as_result(self, pos, move):
        """
        Finds if playing my move would make both kings meet.

        :type: pos: Board
        :type: move: Move
        :rtype: bool
        """
        test = cp(pos)
        test.update(move)
        test_king = test.get_king(move.color)

        return self.loc_adjacent_to_opponent_king(test_king.location, test)

    def loc_adjacent_to_opponent_king(self, location, position):
        """
        Finds if 2 kings are touching given the position of one of the kings.

        :type: location: Location
        :type: position: Board
        :rtype: bool
        """
        for fn in self.cardinal_directions:
            try:
                if isinstance(position.piece_at_square(fn(location)), King) and \
                        position.piece_at_square(fn(location)).color != self.color:
                    return True

            except IndexError:
                pass

        return False

    def add(self, func, position):
        """
        Adds all 8 cardinal directions as moves for the King if legal.

        :type: function: function
        :type: position: Board
        :rtype: gen
        """
        try:
            if self.loc_adjacent_to_opponent_king(func(self.location), position):
                return
        except IndexError:
            return

        if position.is_square_empty(func(self.location)):
            yield self.create_move(func(self.location), notation_const.MOVEMENT)

        elif position.piece_at_square(func(self.location)).color != self.color:
            yield self.create_move(func(self.location), notation_const.CAPTURE)

    def _rook_legal_for_castle(self, rook):
        """
        Decides if given rook exists, is of this color, and has not moved so it
        is eligible to castle.

        :type: rook: Rook
        :rtype: bool
        """
        return rook is not None and \
            type(rook) is Rook and \
            rook.color == self.color and \
            not rook.has_moved

    def _empty_not_in_check(self, position, direction):
        """
        Checks if set of squares in between ``King`` and ``Rook`` are empty and safe
        for the king to castle.

        :type: position: Position
        :type: direction: function
        :type: times: int
        :rtype: bool
        """
        def valid_square(square):
            return position.is_square_empty(square) and \
                   not self.in_check(position, square)

        return valid_square(direction(self.location, 1)) and \
            valid_square(direction(self.location, 2))

    def add_castle(self, position):
        """
        Adds kingside and queenside castling moves if legal

        :type: position: Board
        """
        if self.has_moved or self.in_check(position):
            return

        if self.color == color.white:
            rook_rank = 0
        else:
            rook_rank = 7

        castle_type = {
            notation_const.KING_SIDE_CASTLE: {
                "rook_file": 7,
                "direction": lambda king_square, times: king_square.shift_right(times)
            },
            notation_const.QUEEN_SIDE_CASTLE: {
                "rook_file": 0,
                "direction": lambda king_square, times: king_square.shift_left(times)
            }
        }
        for castle_key in castle_type:
            castle_dict = castle_type[castle_key]
            castle_rook = position.piece_at_square(Location(rook_rank, castle_dict["rook_file"]))
            if self._rook_legal_for_castle(castle_rook) and \
                    self._empty_not_in_check(position, castle_dict["direction"]):
                yield self.create_move(castle_dict["direction"](self.location, 2), castle_key)

    def possible_moves(self, position):
        """
        Generates list of possible moves

        :type: position: Board
        :rtype: list
        """
        # Chain used to combine multiple generators
        for move in itertools.chain(*[self.add(fn, position) for fn in self.cardinal_directions]):
            yield move

        for move in self.add_castle(position):
            yield move

    def in_check(self, position, location=None):
        """
        Finds if the king is in check or if both kings are touching.

        :type: position: Board
        :return: bool
        """
        location = location or self.location
        for piece in position:

            if piece is not None and piece.color != self.color:
                if not isinstance(piece, King):
                    for move in piece.possible_moves(position):

                        if move.end_loc == location:
                            return True
                else:
                    if self.loc_adjacent_to_opponent_king(piece.location, position):
                        return True

        return False class Pawn(Piece):
    def __init__(self, input_color, location):
        """
        Initializes a Pawn that is capable of moving

        :type: input_color: Color
        :type: location: Location
        """
        self.just_moved_two_steps = False
        super(Pawn, self).__init__(input_color, location)

    def _symbols(self):
        return {color.white: "♟", color.black: "♙"}

    def __str__(self):
        return "P"

    def on_home_row(self, location=None):
        """
        Finds out if the piece is on the home row.

        :return: bool for whether piece is on home row or not
        """
        location = location or self.location
        return (self.color == color.white and location.rank == 1) or \
               (self.color == color.black and location.rank == 6)

    def would_move_be_promotion(self, location=None):
        """
        Finds if move from current get_location would result in promotion

        :type: location: Location
        :rtype: bool
        """
        location = location or self.location
        return (location.rank == 1 and self.color == color.black) or \
                (location.rank == 6 and self.color == color.white)

    def square_in_front(self, location=None):
        """
        Finds square directly in front of Pawn

        :type: location: Location
        :rtype: Location
        """
        location = location or self.location
        return location.shift_up() if self.color == color.white else location.shift_down()

    def two_squares_in_front(self, location):
        """
        Finds square two squares in front of Pawn

        :type: location: Location
        :rtype: get_location
        """
        return self.square_in_front(self.square_in_front(location))

    def create_promotion_moves(self, status, location=None):
        location = location or self.square_in_front()
        def create_each_move(piece):
            return Move(end_loc=location,
                        piece=self,
                        status=status,
                        start_loc=self.location,
                        promoted_to_piece=piece)

        yield create_each_move(Queen)
        yield create_each_move(Rook)
        yield create_each_move(Bishop)
        yield create_each_move(Knight)

    def forward_moves(self, position):
        """
        Finds possible moves one step and two steps in front
        of Pawn.

        :type: position: Board
        :rtype: list
        """
        if position.is_square_empty(self.square_in_front(self.location)):
            """
            If square in front is empty add the move
            """
            if self.would_move_be_promotion():
                for move in self.create_promotion_moves(notation_const.PROMOTE):
                    yield move
            else:
                yield self.create_move(end_loc=self.square_in_front(self.location),
                                       status=notation_const.MOVEMENT)

            if self.on_home_row() and \
                    position.is_square_empty(self.two_squares_in_front(self.location)):
                """
                If pawn is on home row and two squares in front of the pawn is empty
                add the move
                """
                yield self.create_move(
                    end_loc=self.square_in_front(self.square_in_front(self.location)),
                    status=notation_const.MOVEMENT
                )

    def _one_diagonal_capture_square(self, capture_square, position):
        """
        Adds specified diagonal as a capture move if it is one
        """
        if self.contains_opposite_color_piece(capture_square, position):

            if self.would_move_be_promotion():
                for move in self.create_promotion_moves(status=notation_const.CAPTURE_AND_PROMOTE,
                                                        location=capture_square):
                    yield move

            else:
                yield self.create_move(end_loc=capture_square,
                                       status=notation_const.CAPTURE)

    def capture_moves(self, position):
        """
        Finds out all possible capture moves

        :rtype: list
        """
        try:
            right_diagonal = self.square_in_front(self.location.shift_right())
            for move in self._one_diagonal_capture_square(right_diagonal, position):
                yield move
        except IndexError:
            pass

        try:
            left_diagonal = self.square_in_front(self.location.shift_left())
            for move in self._one_diagonal_capture_square(left_diagonal, position):
                yield move
        except IndexError:
            pass

    def on_en_passant_valid_location(self):
        """
        Finds out if pawn is on enemy center rank.

        :rtype: bool
        """
        return (self.color == color.white and self.location.rank == 4) or \
               (self.color == color.black and self.location.rank == 3)

    def _is_en_passant_valid(self, opponent_pawn_location, position):
        """
        Finds if their opponent's pawn is next to this pawn

        :rtype: bool
        """
        try:
            pawn = position.piece_at_square(opponent_pawn_location)
            return pawn is not None and \
                isinstance(pawn, Pawn) and \
                pawn.color != self.color and \
                position.piece_at_square(opponent_pawn_location).just_moved_two_steps
        except IndexError:
            return False

    def add_one_en_passant_move(self, direction, position):
        """
        Yields en_passant moves in given direction if it is legal.

        :type: direction: function
        :type: position: Board
        :rtype: gen
        """
        try:
            if self._is_en_passant_valid(direction(self.location), position):
                yield self.create_move(
                    end_loc=self.square_in_front(direction(self.location)),
                    status=notation_const.EN_PASSANT
                )
        except IndexError:
            pass

    def en_passant_moves(self, position):
        """
        Finds possible en passant moves.

        :rtype: list
        """

        # if pawn is not on a valid en passant get_location then return None
        if self.on_en_passant_valid_location():
            for move in itertools.chain(self.add_one_en_passant_move(lambda x: x.shift_right(), position),
                                        self.add_one_en_passant_move(lambda x: x.shift_left(), position)):
                yield move

    def possible_moves(self, position):
        """
        Finds out the locations of possible moves given board.Board position.
        :pre get_location is on board and piece at specified get_location on position

        :type: position: Board
        :rtype: list
        """
        for move in itertools.chain(self.forward_moves(position),
                                    self.capture_moves(position),
                                    self.en_passant_moves(position)):
            yield move class Queen(Bishop, Piece):
    def __init__(self, input_color, location):
        Piece.__init__(self, input_color, location)

    def _symbols(self):
        return {color.white: "♛", color.black: "♕"}

    def __str__(self):
        return "Q"

    def possible_moves(self, position):
        for move in itertools.chain(Rook.possible_moves(self, position),
                                    Bishop.possible_moves(self, position)):
            yield move class Rook(Piece):
    def __init__(self, input_color, location):
        """
        Initializes a rook that is capable of being compared to another rook,
        and returning a list of possible moves.

        :type: input_color: Color
        :type: location: Location
        """
        super(Rook, self).__init__(input_color, location)
        self.has_moved = False

    def _symbols(self):
        return {color.white: "♜", color.black: "♖"}

    def __str__(self):
        return "R"

    def moves_in_direction(self, direction, position):
        """
        Finds moves in a given direction

        :type: direction: lambda
        :type: position: Board
        :rtype: list
        """
        current_square = self.location

        while True:
            try:
                current_square = direction(current_square)
            except IndexError:
                return

            if self.contains_opposite_color_piece(current_square, position):
                yield self.create_move(current_square, notation_const.CAPTURE)

            if not position.is_square_empty(current_square):
                return

            yield self.create_move(current_square, notation_const.MOVEMENT)

    def possible_moves(self, position):
        """
        Returns all possible rook moves.

        :type: position: Board
        :rtype: list
        """
        for move in itertools.chain(*[self.moves_in_direction(fn, position) for fn in self.cross_fn]):
            yield move class Knight(Piece):
    def __init__(self, input_color, location):
        """
        Initializes Knight
        :type: input_color: Color
        :type: location Location
        """
        super(Knight, self).__init__(input_color, location)

    def _symbols(self):
        return {color.white: "♞", color.black: "♘"}

    def __str__(self):
        return "N"

    @staticmethod
    def _rotate_direction_ninety_degrees(direction):
        if direction == 3:
            return 0, 2
        right_angles = [direction - 1, direction + 1]
        for index, angle in enumerate(right_angles):
            if angle == -1:
                right_angles[index] = 3
            elif angle == 4:
                right_angles[index] = 0

        return right_angles

    def possible_moves(self, position):
        """
        Finds all possible knight moves
        :type: position Board
        :rtype: list
        """
        for direction in [0, 1, 2, 3]:
            angles = self._rotate_direction_ninety_degrees(direction)
            for angle in angles:
                try:
                    end_loc = self.location.shift(angle).shift(direction).shift(direction)
                    if position.is_square_empty(end_loc):
                        status = notation_const.MOVEMENT
                    elif not position.piece_at_square(end_loc).color == self.color:
                        status = notation_const.CAPTURE
                    else:
                        continue

                    yield Move(end_loc=end_loc,
                               piece=self,
                               status=status,
                               start_loc=self.location)

                except IndexError:
                    pass
import inspect
from multiprocessing import Process
from copy import copy as cp
from math import fabs
from .color import white, black
from .algebraic import notation_const
from .algebraic.location import Location
from .algebraic.move import Move
from ..pieces.piece import Piece
from ..pieces.bishop import Bishop
from ..pieces.king import King
from ..pieces.pawn import Pawn
from ..pieces.queen import Queen
from ..pieces.rook import Rook
from ..pieces.knight import Knight
# -*- coding: utf-8 -*-

"""
Constructs board object which stores the get_location of all the pieces.

Default Array

| [[0th row 0th item,  0th row 1st item,  0th row 2nd item],
|  [1st row 0th item,  1st row 1st item,  1st row 2nd item],
|  [2nd row 0th item, 2nd row 1st item,  2nd row 2nd item]]

| Default board
| 8 ║♜ ♞ ♝ ♛ ♚ ♝ ♞ ♜ Black pieces
| 7 ║♟ ♟ ♟ ♟ ♟ ♟ ♟ ♟ Black pawns
| 6 ║a6… … … … … …h6
| 5 ║… … … … … … … …
| 4 ║… … … … … … … …
| 3 ║a3… … … … … …h3 Algebraic
| 2 ║♙ ♙ ♙ ♙ ♙ ♙ ♙ ♙ White pawns
| 1 ║♖ ♘ ♗ ♕ ♔ ♗ ♘ ♖ White pieces
| -—╚═══════════════
| ——-a b c d e f g h

Pieces on the board are flipped in position array so white home row is at index 0
and black home row is at index 7

| Copyright © 2016 Aubhro Sengupta. All rights reserved.
"""

from __future__ import print_function




class Board:
    """
    Standard starting position in a chess game.
    Initialized upon startup and is used when init_default constructor is used

    """

    def __init__(self, position):
        """
        Creates a ``Board`` given an array of ``Piece`` and ``None``
        objects to represent the given position of the board.

        :type: position: list
        """
        self.position = position
        self.possible_moves = dict()
        try:
            self.king_loc_dict = {white: self.find_king(white),
                                  black: self.find_king(black)}
        except ValueError:
            self.king_loc_dict = None

    @classmethod
    def init_default(cls):
        """
        Creates a ``Board`` with the standard chess starting position.

        :rtype: Board
        """
        return cls([

            # First rank
            [Rook(white, Location(0, 0)), Knight(white, Location(0, 1)), Bishop(white, Location(0, 2)),
             Queen(white, Location(0, 3)), King(white, Location(0, 4)), Bishop(white, Location(0, 5)),
             Knight(white, Location(0, 6)), Rook(white, Location(0, 7))],

            # Second rank
            [Pawn(white, Location(1, file)) for file in range(8)],

            # Third rank
            [None for _ in range(8)],

            # Fourth rank
            [None for _ in range(8)],

            # Fifth rank
            [None for _ in range(8)],

            # Sixth rank
            [None for _ in range(8)],

            # Seventh rank
            [Pawn(black, Location(6, file)) for file in range(8)],

            # Eighth rank

            [Rook(black, Location(7, 0)), Knight(black, Location(7, 1)), Bishop(black, Location(7, 2)),