+# ChessBoard.py
+
+import json
+import os
+from copy import deepcopy, copy
+from functools import wraps
+from typing import Union, List, Dict, Generator
+
+from .ChessVector import ChessVector
+from .Pieces import *
+from .Moves import *
+from .configurations import ClassicConfig, FourPlayerConfig
+from .Utils import countAlpha, getResourcePath
+from .Exceptions import *
+
+
+def _defaultColors(func):
+    @wraps(func)
+    def wrapper(self, *colors):
+        if not colors:
+            colors = self.getColors()
+        returned = func(self, *colors)
+        if isinstance(returned, dict) and len(returned) == 1:
+            returned = returned.pop(*colors)
+        return returned
+    return wrapper
+
+
+class Board():
+    """Board object for storing and moving pieces
+
+    :param config: Board configuration (defaults to emtpy board)
+    """
+
+    def __init__(self, config={}):
+
+        self._board = []
+        with open(getResourcePath(os.path.join(os.path.dirname(os.path.abspath(__file__)), "configurations/DefaultConfig.JSON")), "r") as default:
+            dConfig = json.load(default)
+
+            self._rows = config.get("rows") or dConfig.get("rows")
+            self._cols = config.get("cols") or dConfig.get("cols")
+            self._pieces = config.get("pieces") or dConfig.get("pieces")
+            self._moves = config.get("moves") or dConfig.get("moves")
+            self._promoteTo = config.get("promoteTo") or dConfig.get("promoteTo")
+            self._promoteFrom = config.get("promoteFrom") or dConfig.get("promoteFrom")
+            self._promoteAt = config.get("promoteAt") or dConfig.get("promteAt")
+            self._turnorder = config.get("turnorder") or dConfig.get("turnorder")
+
+            try:
+                self.currentTurn = self._turnorder[0]
+            except IndexError:
+                self.currentTurn = None
+            self._board = [[Empty(ChessVector((row, col))) for col in range(self._cols)] for row in range(self._rows)]
+
+            for color, pieceList in self._pieces.items():
+                for piece in pieceList:
+                    self[piece.vector] = piece
+
+            for vec in config.get("disabled") or dConfig.get("disabled"):
+                self[vec] = Disabled(vec)
+
+        self._checks = {key: False for key in self._pieces.keys()}
+        self._checkmates = copy(self._checks)
+        self._kings = {key: [piece for piece in self._pieces[key] if isinstance(piece, King)] for key in self._pieces.keys()}
+        self._history = []
+
+        self.checkForCheck()
+
+    def __eq__(self, other):
+        for p1, p2 in zip(self, other):
+            if type(p1) == type(p2):
+                continue
+            else:
+                break
+
+        else:
+            return True
+        return False
+
+    def __ne__(self, other):
+        return not self == other
+
+    def __iter__(self):
+        """Iterates through all positions in board
+
+        Use iterPieces() method to iterate through pieces of board.
+        """
+        for p in [p for row in self._board for p in row]:
+            yield p
+
+    def __str__(self):
+        string = "\n"
+        ending = "\t|\n\n\t\t__" + ("\t__" * self._cols) + "\n\n\t\t"
+        alpha = countAlpha()
+
+        for row in self._board:
+
+            num, char = next(alpha)
+
+            string += str(self._rows - num) + "\t|\t\t"
+
+            for piece in row:
+                string += str(piece) + "\t"
+
+            string += "\n\n"
+
+            ending += "\t" + char.upper()
+
+        return string + ending + "\n\n"
+
+    def __setitem__(self, index, item):
+
+        try:
+            iter(item)
+        except TypeError:
+            item = [item]
+
+        try:
+            iter(index)
+        except TypeError:
+            index = [index]
+
+        if len(index) != len(item):
+            raise ValueError("List index expected {0} values to unpack but {1} were given".format(
+                len(item), len(index)))
+
+        for i, vec in enumerate(index):
+
+            if isinstance(self._board[vec.row][vec.col], Disabled):
+                raise DisabledError(vec.getStr(self))
+
+            item1 = self._board[vec.row][vec.col]
+            item2 = item[i]
+
+            if not isinstance(item2, Disabled):
+
+                if not isinstance(item1, Empty):
+                    self._removePiece(item1)
+
+                if not isinstance(item2, Empty):
+
+                    if item2 in self.iterPieces(item2.color):
+                        pass
+                    else:
+                        self._addPiece(item2, vec)
+
+            self._board[vec.row][vec.col] = item2
+
+    def __getitem__(self, index):
+        res = []
+
+        try:
+            iter(index)
+        except TypeError:
+            index = [index]
+
+        for vec in index:
+            if isinstance(self._board[vec.row][vec.col], Disabled):
+                raise DisabledError(vec.getStr(self))
+            res.append(self._board[vec.row][vec.col])
+
+        if len(res) == 1:
+            return res.pop()
+        else:
+            return res
+
+    def getRows(self) -> int:
+        """Get rows in board
+
+        :returns: Number of rows in board
+        :rtype: ``int``
+        """
+        return self._rows
+
+    def getCols(self) -> int:
+        """Get columns in board
+
+        :returns: Number of columns in board
+        :rtype: ``int``
+        """
+        return self._cols
+
+    def getHistory(self) -> list:
+        """Get history list of board
+
+        :returns: History of board
+        :rtype: ``list``
+        """
+        return self._history
+
+
+    def getTurnorder(self) -> list:
+        """Get turnorder list of board
+
+        :returns: Turnorder of board
+        :rtype: ``list``
+        """
+        return self._turnorder
+
+    @_defaultColors
+    def getChecks(self, *colors: str) -> Union[bool, Dict[str, bool]]:
+        """Get checks in board
+
+        If more than one color is given, this returns a ``dict``
+        with a ``bool`` corresponding to each color.
+
+        :param *colors: Colors to return
+        :returns: If colors are in check or not
+        :rtype: ``bool`` or ``dict``
+        """
+        return {col: self._checks[col] for col in colors}
+
+    @_defaultColors
+    def getCheckmates(self, *colors: str) -> Union[bool, Dict[str, bool]]:
+        """Get checkmates in board
+
+        If more than one color is given, this returns a ``dict``
+        with a ``bool`` corresponding to each color.
+
+        :param *colors: Colors to return
+        :returns: If colors are in checkmate or not
+        :rtype: ``bool`` or ``dict``
+        """
+        return {col: self._checkmates[col] for col in colors}
+
+    @_defaultColors
+    def getKings(self, *colors: str) -> Union[List[King], Dict[str, List[King]]]:
+        """Get kings in board
+
+        If more than one color is given, this returns a ``dict``
+        with a ``list`` of kings corresponding to each color.
+
+        :param *colors: Colors to return
+        :returns: All kings of colors in board
+        :rtype: ``list`` or ``dict``
+        """
+        return {col: self._kings[col] for col in colors}
+
+    @_defaultColors
+    def getMoves(self, *colors: str) -> Union[List[Move], Dict[str, List[Move]]]:
+        """Get moves of board
+
+        If more than one color is given, this returns a ``dict``
+        with a ``list`` of moves corresponding to each color.
+
+        :param *colors: Colors to return
+        :returns: All moves of colors in board
+        :rtype: ``list`` or ``dict``
+        """
+        return {col: self._moves[col] for col in colors}
+
+    @_defaultColors
+    def getPromoteAt(self, *colors: str) -> Union[int, Dict[str, int]]:
+        """Get promotion position of board
+
+        If more than one color is given, this returns a ``dict``
+        with a ``int`` corresponding to each color.
+
+        :param *colors: Colors to return
+        :returns: The promotion position of colors in board
+        :rtype: ``list`` or ``dict``
+        """
+        return {col: self._promoteAt[col] for col in colors}
+
+    @_defaultColors
+    def getPromoteFrom(self, *colors: str) -> Union[List[Piece], Dict[str, List[Piece]]]:
+        """Get promotion starting pieces of board
+
+        If more than one color is given, this returns a ``dict``
+        with a ``list`` corresponding to each color.
+
+        :param *colors: Colors to return
+        :returns: The promotion starting piece types of colors in board
+        :rtype: ``list`` or ``dict``
+        """
+        return {col: self._promoteFrom[col] for col in colors}
+
+    @_defaultColors
+    def getPromoteTo(self, *colors: str) -> Union[List[Piece], Dict[str, List[Piece]]]:
+        """Get promotion target pieces of board
+
+        If more than one color is given, this returns a ``dict``
+        with a ``list`` corresponding to each color.
+
+        :param *colors: Colors to return
+        :returns: The promotion target piece types of colors in board
+        :rtype: ``list`` or ``dict``
+        """
+        return {col: self._promoteTo[col] for col in colors}
+
+    def getColors(self) -> List[str]:
+        """Get all colors of board
+
+        :returns: List of colors in board
+        :rtype: ``list``
+        """
+        return self._pieces.keys()
+
+    @_defaultColors
+    def iterPieces(self, *colors: str) -> Generator[Piece, None, None]:
+        """Iterate through pieces of board
+
+        Use __iter__ to iterate through all positions of the board.
+
+        :param *colors: Colors of pieces to iterate through (default is all colors)
+        :yields: Every piece in board
+        :ytype: ``generator``
+        """
+        for col in colors:
+            for p in self._pieces[col]:
+                yield p
+
+    @_defaultColors
+    def eval(self, *colors: str) -> Dict[str, int]:
+        """Evaluate board
+
+        Returns the sum of all pieces' values of colors in board
+
+        :param *colors: Colors to evaluate (defaults to all colors of board)
+
+        :returns: Colors with corresponding sum of pieces
+        :rtype: ``dict``
+        """
+        return {col: sum(list(map(lambda p: p.value, list(self.iterPieces(col))))) for col in colors}
+
+    def removeColor(self, color: str) -> None:
+        """Remove color from board
+
+        :param color: Color to remove
+        """
+        vectors = list(map(lambda p: p.vector, list(self.iterPieces(color))))
+        self[vectors] = [Empty(vector) for vector in vectors]
+        self.checkForCheck()
+
+    def swapPositions(self, vec1: ChessVector, vec2: ChessVector) -> None:
+        """Swap position of two pieces
+
+        :param vec1: Starting position of first piece
+        :param vec2: Starting position of second piece
+        """
+        self._board[vec1.row][vec1.col].move(vec2)
+        self._board[vec2.row][vec2.col].move(vec1)
+        self._board[vec1.row][vec1.col], self._board[vec2.row][vec2.col] = self._board[vec2.row][vec2.col], self._board[vec1.row][vec1.col]
+
+    def isEmpty(self, vec: ChessVector) -> bool:
+        """Check if position is empty
+
+        :param vec: Position to check
+        :returns: True if position is empty, else False
+        :rtype: ``bool``
+        """
+        return isinstance(self[vec], Empty)
+
+    def isThreatened(self, vec: ChessVector, alliedColor: str) -> bool:
+        """Check if position is threatened by enemy pieces
+
+        :param vector: Position to check for threats
+        :param alliedColor: Color to exclude from enemy pieces
+        :returns: True if position is threatened, else False
+        :rtype: ``bool``
+        """
+        hostilePieces = [piece for col in self.getColors() if col != alliedColor for piece in self.iterPieces(col)]
+
+        for hp in hostilePieces:
+            hostile = hp.getMoves(self, ignoreCheck=True)
+            if vec in hostile:
+                return True
+        else:
+            return False
+
+    def checkForCheck(self, checkForMate=True) -> None:
+        """Check for any checks in board
+
+        If checkForMate is True and king is in check,
+        method checks if any allied pieces can move to
+        interfere with the threatened check.
+
+        :param checkForMate: Flag False to ignore checkmate (default is True)
+        :returns: None, stores result in attributes ``checks`` and ``checkmates``
+        """
+        for color in self.getColors():
+
+            for alliedKing in self._kings[color]:
+
+                if self.isThreatened(alliedKing.vector, color):
+                    self._checks[color] = True
+                    break
+            else:
+                self._checks[color] = False
+
+            if self._checks[color] and checkForMate:
+
+                alliedPiecesPos = map(lambda p: p.vector, list(self.iterPieces(color)))
+
+                for alliedPos in list(alliedPiecesPos):
+                    for move in self[alliedPos].getMoves(self, ignoreCheck=True):
+                        testBoard = deepcopy(self)
+                        for pieceType in [None, *self._promoteTo[color]]:
+                            try:
+                                testBoard.movePiece(alliedPos, move, ignoreMate=True,
+                                                    checkForMate=False, promote=pieceType,
+                                                    printout=False, checkMove=False, ignoreOrder=True)
+                            except PromotionError:
+                                continue
+                            else:
+                                break
+
+                        if testBoard._checks[color]:
+                            continue
+                        else:
+                            self._checkmates[color] = False
+                            break
+                    else:
+                        continue
+                    break
+                else:
+                    self._checkmates[color] = True
+
+    def advanceTurn(self) -> None:
+        """Advance the turn according to turnorder
+        """
+        newidx = self._turnorder.index(self.currentTurn) + 1
+        try:
+            self.currentTurn = self._turnorder[newidx]
+        except IndexError:
+            self.currentTurn = self._turnorder[0]
+
+    def movePiece(self, startVec: ChessVector, targetVec: ChessVector,
+                  ignoreOrder=False, ignoreMate=False, ignoreCheck=False,
+                  checkForCheck=True, checkForMate=True, checkMove=True,
+                  printout=True, promote=None) -> str:
+        """Move piece on board
+
+        :param startVec: Position of moving piece
+        :param targetVec: Destination of moving piece
+        :param **Flags: Flags altering move rules, see below
+        :returns: Notation of move
+        :rtype: ``str``
+
+        :**Flags:
+            :ignoreOrder (False): Ignore the turnorder
+            :ignoreMate (False): Ignore if any pieces are in checkmate
+            :ignoreCheck (False): Ignore if any pieces are in check
+            :checkForCheck (True): Check for any checks after move
+            :checkForMate (True): Check for any checkmates after move
+            :checkMove (True): Check if piece is able to move to destination
+            :printout (True): Print the results of the move; checks, checkmates and move notation
+            :promote (None): Piece type to promote to
+        """
+
+        if self.isEmpty(startVec):
+            raise EmptyError(startVec.getStr(self))
+
+        startPiece = self[startVec]
+
+        if not ignoreOrder and self.currentTurn != startPiece.color:
+            raise TurnError
+
+        if self._checkmates[startPiece.color] and not ignoreMate:
+            raise CheckMate
+
+        if checkMove and not targetVec.matches(startPiece.getMoves(self, ignoreCheck=ignoreCheck, ignoreMate=ignoreMate)):
+            raise IllegalMove(startVec.getStr(self), targetVec.getStr(self))
+
+        for move in self._moves[startPiece.color]:
+            if move.pieceCondition(startPiece):
+                if targetVec in move.getDestinations(startPiece, self):
+                    notation = move.action(startPiece, targetVec, self, promote)
+                    if checkForCheck:
+                        self.checkForCheck(checkForMate=checkForMate)
+                    break
+
+        else:
+            raise IllegalMove(startVec.getStr(self), targetVec.getStr(self))
+
+        for color in self._checks.keys():
+            if self._checkmates[color]:
+                if printout:
+                    print(f"{color} in Checkmate!")
+                if not "#" in notation:
+                    notation += "#"
+
+            elif self._checks[color]:
+                if printout:
+                    print(f"{color} in Check!")
+                if not "+" in notation:
+                    notation += "+"
+
+        for piece in self.iterPieces():
+
+            if not piece is startPiece:
+                piece.postAction(self)
+
+        self._history.append(notation)
+        if printout:
+            print(notation)
+
+        self.advanceTurn()
+        return notation
+
+    def _addPiece(self, piece: Piece, vec: ChessVector) -> None:
+        if not piece.color in self.getColors():
+            self._pieces[piece.color] = []
+            self._kings[piece.color] = []
+            self._checks[piece.color] = False
+            self._checkmates[piece.color] = False
+
+        self._pieces[piece.color].append(piece)
+
+        if isinstance(piece, King):
+            self._kings[piece.color].append(piece)
+
+        piece.vector = vec
+
+    def _removePiece(self, piece: Piece) -> None:
+
+        self._pieces[piece.color].remove(piece)
+
+        if isinstance(piece, King) and piece in self._kings[piece.color]:
+            self._kings[piece.color].remove(piece)
+
+        if not self._pieces[piece.color]:
+            del self._pieces[piece.color]
+            del self._promoteTo[piece.color]
+            del self._promoteFrom[piece.color]
+            del self._promoteAt[piece.color]
+            del self._kings[piece.color]
+            del self._checks[piece.color]
+            del self._checkmates[piece.color]
+
+            self._turnorder.remove(piece.color)
+
+        piece.vector = None
+
+
+def initClassic() -> Board:
+    """Initialize a chessBoard setup for 2 players, classic setup
+
+    :returns: Classic chessboard
+    :rtype: ``Board``
+    """
+    board = Board(deepcopy(ClassicConfig.CONFIG))
+    return board
+
+
+def init4P() -> Board:
+    """Initialize a chessboard setup for four players
+
+    :returns 4 player chessboard
+    :rtype: ``Board``
+    """
+    board = Board(deepcopy(FourPlayerConfig.CONFIG))
+    return board