Skip to main content
Notice removed Improve details by CommunityBot
Bounty Ended with no winning answer by CommunityBot
Notice added Improve details by Alec
Bounty Started worth 50 reputation by Alec
edited title
Link
Alec
  • 425
  • 3
  • 21

Python Chess Piece MovementGame

deleted 456 characters in body
Source Link
Alec
  • 425
  • 3
  • 21
from time import sleep

class Config:
    DEMTIME = .8  # seconds
    types = {'min': 1, 'miniature': 3, 'small': 5, 'default': 8, 'extended': 11, 'large': 15, 'massive': 20, 'max': 26}
    letters = tuple('abcdefghijklmnopqrstuvwxyz')
    white_pieces = {'Pawn' : "♙", 'Rook' : "♖", 'Knight' : "♘", 'Bishop' : "♗", 'King' : "♔", 'Queen' : "♕" }
    black_pieces = {'Pawn' : "♟", 'Rook' : "♜", 'Knight' : "♞", 'Bishop' : "♝", 'King' : "♚", 'Queen' : "♛" }
    board = 'UNINITIALIZED'
    b_len = 'UNINITIALIZED'

    @classmethod
    def new_board(cls, btype='default'):
        def size(x):
            return [['___' for _ in range(x)] for _ in range(x)], x

        s = False

        if btype is not None:
            btype = btype.lower()

            if 'custom' in btype:
                btype = int(btype.replace('custom', '').strip())
                if 1 <= btype <= 26:
                    cls.board, cls.b_len = size(btype)
                    s = True
                else:
                    btype = None
                    cls.new_board(btype)
            elif btype in cls.types:
                cls.board, cls.b_len = size(cls.types[btype])
                s = True
            else:
                print(f'Unable to initialize board of unknown type {btype}')

        else:
            print('Unable to initalize board with a size lower than 1 or greater than 26')

        if s: cls.print_board()

    @classmethod
    def print_board(cls):
        if Config.b_len != 'UNINITIALIZED':
            def printl():
                if len(str(cls.b_len)) == 2:
                    print(' ', end='')
                for x in range(cls.b_len):
                    print(' '*6 + f'{cls.letters[x]}', end='')
                print('\n')

            print('\n'*2)
            printl()
            for x in range(cls.b_len):
                print(f'{cls.b_len-x:0{len(str(cls.b_len))}}  {cls.board[x]}  {cls.b_len-x:0{len(str(cls.b_len))}}\n')
            printl()
            print('\n'*4)

        else:
            print('Unable to print board of uninitialized type')

    @classmethod
    def tile_convert(cls, x, display_tile=False):
        if not display_tile:
            if isinstance(x, str):
                return cls.letters.index(x)
            else:
                return cls.letters[x]
        else:  # display_tile converts the letter in {letter}{number} to a number
            return cls.b_len - int(x)

    @classmethod
    def l_num_to_coord(cls, pos):
        return Config.b_len - int(pos[1]), int(Config.tile_convert(pos[0]))

    @classmethod
    def coord_to_tile(cls, x, y):  # IMPLIMENT THIS FOR EVERYTHING
        return f'{Config.tile_convert(x)}{Config.tile_convert(y, True)}'

    @classmethod
    def c_convert(cls, color):
        if color == 'White':
            return 'b'
        if color == "Black":
            return 'w'

class ChessPiece:
    def __init__(self, pos, color, num, piece):
        self.x = int(Config.tile_convert(pos[0]))
        self.y = Config.b_len - int(pos[1])
        self.color = color
        self.piece = piece
        self.pieceid = num
        self.moves = 0
        self.captured = []
        self.erased = False
        self.set_id()
        self.create()
        Config.print_board()

    def __str__(self):
        return self.piece

    def __repr__(self):
        return self.pieceid

    def set_id(self):
        if self.__class__.__name__ != "Knight":
            self.pieceid = f'{self.piece[0]}{self.pieceid}'
        else:
            self.pieceid = f'N{self.pieceid}'

        if self.color is not None:
            if self.color.lower() in ('black', 'white', 'b', 'w'):
                self.pieceid = self.color.lower()[0] + self.pieceid
                if self.color.lower() == 'b':
                    self.color = 'Black'
                elif self.color.lower() == 'w':
                    self.color = 'White'
            else:
                self.color = None
                print("Invalid color input. Color not set.")
                self.set_id()
        else:
             self.pieceid = '_' + self.pieceid


    def create(self):
        if Config.board[self.y][self.x] != '___':
            po = Config.board[self.y][self.x]
            print(f'Piece {po} erased to make room for {self.pieceid}')

        Config.board[self.y][self.x] = self.pieceid

    def teleport(self, pos, record=False):
        Config.board[self.y][self.x] = '___'

        if record:
            self.moves += 1
            coord = Config.l_num_to_coord(pos)
            if Config.board[coord[0]][coord[1]] != '___':
                self.captured.append(Config.board[coord[0]][coord[1]])
                print(f'{self.pieceid} has captured {Config.board[coord[0]][coord[1]]}!')

        self.x = Config.tile_convert(pos[0])
        self.y = Config.tile_convert(pos[1], True)

        Config.board[self.y][self.x] = self.pieceid

        Config.print_board()

    def move(self, pos):
        if pos in self.possible_moves():
            coord = Config.l_num_to_coord(pos)
            if Config.board[coord[0]][coord[1]] != '___':
                self.captured.append(Config.board[coord[0]][coord[1]])
                print(f'{self.pieceid} has captured {Config.board[coord[0]][coord[1]]}!')
                # Erase piece

            if self.__class__ == Pawn:
                if abs(int(pos[1]) - Config.tile_convert(self.y, True)) == 2:
                    self.two_move = True

            self.teleport(pos)
            self.moves += 1

        else:
            print(f'Unable to move to {pos}')


    def get_info(self):
        print(f'{self.__class__.__name__}:\n')
        print('ID: ', self.pieceid)
        print('Position: ', Config.tile_convert(self.x), Config.tile_convert(self.y, True), sep='')
        print('Color: ', self.color)

    def erase(self):  # Doesn't delete the piece. It can be brought back by moving it to a square
        Config.board[self.y][self.x] = '___'
        self.erased = True

    def demo(self, rec=True):  # default board
        for pos in self.demo_moves:
            self.teleport(pos, rec)
            sleep(Config.DEMTIME)

        if self.__class__ == Pawn:
            self.promote2(Queen)

    @staticmethod
    def castle(king, rook):
        if not king.moves and not rook.moves:
            if not king.in_check:
                pass


class Pawn(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8')
        self.two_move = False

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        # Forward
        if not self.moves: # Starting Position
            if self.color != 'White':
                for new_y in (y+1, y+2):
                    try:
                        if Config.board[new_y][x] == '___':
                            pos_moves.append(Config.coord_to_tile(x, new_y))
                        else: break
                    except IndexError: pass

            if self.color != 'Black':
                for new_y in (y-1, y-2):
                    try:
                        if Config.board[new_y][x] == '___':
                            pos_moves.append(Config.coord_to_tile(x, new_y))
                        else: break
                    except IndexError: pass

        else:  # Post-Start
            if self.color != 'White':
                try:
                    if Config.board[y+1][x] == '___':
                        pos_moves.append(Config.coord_to_tile(x, y+1))
                except IndexError: pass

            if self.color != 'Black':
                try:
                    if Config.board[y-1][x] == '___':
                        pos_moves.append(Config.coord_to_tile(x, y-1))
                except IndexError: pass

        # Capturing
        if self.color != 'White':
            if self.color is not None:
                try:
                    if Config.c_convert(self.color) in Config.board[y+1][x+1]:
                        pos_moves.append(Config.coord_to_tile(x+1, y+1))
                except IndexError: pass
            else:
                try:
                    if Config.board[y+1][x+1] != '___':
                        pos_moves.append(Config.coord_to_tile(x+1, y+1))
                except IndexError: pass

        if self.color != 'Black':
            if self.color is not None:
                try:
                    if Config.c_convert(self.color) in Config.board[y-1][x-1]:
                        pos_moves.append(Config.coord_to_tile(x-1, y-1))
                except IndexError: pass
            else:
                try:
                    if Config.board[y+1][x+1] != '___':
                        pos_moves.append(Config.coord_to_tile(x-1, y-1))
                except IndexError: pass

        # En Passant


        return sorted(pos_moves)

    def promote(self, piece):  # oringal_piece = original_piece.promote(new_piece)
        pos = f'{Config.tile_convert(self.x)}{Config.tile_convert(self.y, True)}'

        return piece(pos, color=self.color, num='p')

    def promote2(self, piece):
        pos = f'{Config.tile_convert(self.x)}{Config.tile_convert(self.y, True)}'

        self.__class__ = piece
        self.__init__(pos, self.color, 'p')


class Knight(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('e1', 'f3', 'g5', 'h7', 'f8', 'e6', 'c5', 'd3', 'e1')

    def possible_moves(self):
        pos_moves = []
        for x_off, y_off in ( (1, 2), (-1, 2), (1, -2), (-1, -2), (2, 1), (-2, 1), (2, -1), (-2, -1) ):
            new_x = self.x + x_off
            new_y = self.y + y_off
            if 0 <= new_x < Config.b_len and 0 <= new_y < Config.b_len:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(new_y, Truenew_y)}')
                else:
                    pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(new_y, Truenew_y)}')

        return sorted(pos_moves)


class Bishop(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('a1', 'e5', 'b8', 'h2', 'e5', 'a1')

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        right_up = zip(range(x + 1, Config.b_len), range(y - 1, -1, -1))
        right_down = zip(range(x + 1, Config.b_len), range(y + 1, Config.b_len))

        left_up = zip(range(x - 1, -1, -1), range(y - 1, -1, -1))
        left_down = zip(range(x - 1, -1, -1), range(y + 1, Config.b_len))

        for r in (right_up, right_down, left_up, left_down):
            for new_x, new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(new_y, Truenew_y)}')
                        if Config.board[new_y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(new_y, Truenew_y)}')

        return sorted(pos_moves)


class Rook(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('a1', 'a8', 'h8', 'h1', 'a1')

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        # Horizontal
        for r in (range(x+1, Config.b_len), reversed(range(x))):
            for new_x in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[y][new_x]:
                        pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(y, Truey)}')
                        if Config.board[y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(y, Truey)}')
                    if Config.board[new_y][new_x] != '___': break

        # Vertical
        for r in (range(y+1, Config.b_len), reversed(range(y))):
            for new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][x]:
                        pos_moves.append(f'{Config.tile_convertcoord_to_tile(x)}{Config.tile_convert(new_y, Truenew_y)}')
                        if Config.board[new_y][x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convertcoord_to_tile(x)}{Config.tile_convert(new_y, Truenew_y)}')
                    if Config.board[new_y][new_x] != '___': break

        return sorted(pos_moves)


class Queen(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('a1', 'h8', 'a8', 'h1', 'a1')

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        # Horizontal
        for r in (range(x+1, Config.b_len), reversed(range(x))):
            for new_x in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[y][new_x]:
                        pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(y, Truey)}')
                        if Config.board[y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(y, True)}')
                    if Config.board[new_y][new_x] != '___': break

        # Vertical
        for r in (range(y+1, Config.b_len), reversed(range(y))):
            for new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][x]:
                        pos_moves.append(f'{Config.tile_convertcoord_to_tile(x)}{Config.tile_convert(new_y, Truenew_y)}')
                        if Config.board[new_y][x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convert(x)}{Config.tile_convert(new_y, True)}')
                    if Config.board[new_y][new_x] != '___': break

        #Diagonal
        right_up = zip(range(x + 1, Config.b_len), range(y - 1, -1, -1))
        right_down = zip(range(x + 1, Config.b_len), range(y + 1, Config.b_len))

        left_up = zip(range(x - 1, -1, -1), range(y - 1, -1, -1))
        left_down = zip(range(x - 1, -1, -1), range(y + 1, Config.b_len))

        for r in (right_up, right_down, left_up, left_down):
            for new_x, new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(new_y, Truenew_y)}')
                        if Config.board[new_y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(new_y, Truenew_y)}')

        return sorted(pos_moves)


class King(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('e4', 'd5', 'c4', 'c5', 'd6', 'e5', 'e4')
        self.in_check = False

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        for x_off, y_off in ( (0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (-1, 1), (-1, -1), (1, -1) ):
            new_x = self.x + x_off
            new_y = self.y + y_off
            if 0 <= new_x < Config.b_len and 0 <= new_y < Config.b_len:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(new_y, Truenew_y)}')
                else:
                    pos_moves.append(f'{Config.tile_convertcoord_to_tile(new_x)}{Config.tile_convert(new_y, Truenew_y)}')


        return sorted(pos_moves)


# Some commands to start out with

Config.new_board('default')

r1 = Rook(color='w')
n1 = Knight('a5', color='b')
p1 = Pawn('e1', color='w')
p2 = Pawn('e8', color='b')
p3 = Pawn('f7', color='w')
r1.teleport('b3')
from time import sleep

class Config:
    DEMTIME = .8
    types = {'min': 1, 'miniature': 3, 'small': 5, 'default': 8, 'extended': 11, 'large': 15, 'massive': 20, 'max': 26}
    letters = tuple('abcdefghijklmnopqrstuvwxyz')
    white_pieces = {'Pawn' : "♙", 'Rook' : "♖", 'Knight' : "♘", 'Bishop' : "♗", 'King' : "♔", 'Queen' : "♕" }
    black_pieces = {'Pawn' : "♟", 'Rook' : "♜", 'Knight' : "♞", 'Bishop' : "♝", 'King' : "♚", 'Queen' : "♛" }
    board = 'UNINITIALIZED'
    b_len = 'UNINITIALIZED'

    @classmethod
    def new_board(cls, btype='default'):
        def size(x):
            return [['___' for _ in range(x)] for _ in range(x)], x

        s = False

        if btype is not None:
            btype = btype.lower()

            if 'custom' in btype:
                btype = int(btype.replace('custom', '').strip())
                if 1 <= btype <= 26:
                    cls.board, cls.b_len = size(btype)
                    s = True
                else:
                    btype = None
                    cls.new_board(btype)
            elif btype in cls.types:
                cls.board, cls.b_len = size(cls.types[btype])
                s = True
            else:
                print(f'Unable to initialize board of unknown type {btype}')

        else:
            print('Unable to initalize board with a size lower than 1 or greater than 26')

        if s: cls.print_board()

    @classmethod
    def print_board(cls):
        if Config.b_len != 'UNINITIALIZED':
            def printl():
                if len(str(cls.b_len)) == 2:
                    print(' ', end='')
                for x in range(cls.b_len):
                    print(' '*6 + f'{cls.letters[x]}', end='')
                print('\n')

            print('\n'*2)
            printl()
            for x in range(cls.b_len):
                print(f'{cls.b_len-x:0{len(str(cls.b_len))}}  {cls.board[x]}  {cls.b_len-x:0{len(str(cls.b_len))}}\n')
            printl()
            print('\n'*4)

        else:
            print('Unable to print board of uninitialized type')

    @classmethod
    def tile_convert(cls, x, display_tile=False):
        if not display_tile:
            if isinstance(x, str):
                return cls.letters.index(x)
            else:
                return cls.letters[x]
        else:  # display_tile converts the letter in {letter}{number} to a number
            return cls.b_len - int(x)

    @classmethod
    def l_num_to_coord(cls, pos):
        return Config.b_len - int(pos[1]), int(Config.tile_convert(pos[0]))

    @classmethod
    def coord_to_tile(cls, x, y):  # IMPLIMENT THIS FOR EVERYTHING
        return f'{Config.tile_convert(x)}{Config.tile_convert(y, True)}'

    @classmethod
    def c_convert(cls, color):
        if color == 'White':
            return 'b'
        if color == "Black":
            return 'w'

class ChessPiece:
    def __init__(self, pos, color, num, piece):
        self.x = int(Config.tile_convert(pos[0]))
        self.y = Config.b_len - int(pos[1])
        self.color = color
        self.piece = piece
        self.pieceid = num
        self.moves = 0
        self.captured = []
        self.erased = False
        self.set_id()
        self.create()
        Config.print_board()

    def __str__(self):
        return self.piece

    def __repr__(self):
        return self.pieceid

    def set_id(self):
        if self.__class__.__name__ != "Knight":
            self.pieceid = f'{self.piece[0]}{self.pieceid}'
        else:
            self.pieceid = f'N{self.pieceid}'

        if self.color is not None:
            if self.color.lower() in ('black', 'white', 'b', 'w'):
                self.pieceid = self.color.lower()[0] + self.pieceid
                if self.color.lower() == 'b':
                    self.color = 'Black'
                elif self.color.lower() == 'w':
                    self.color = 'White'
            else:
                self.color = None
                print("Invalid color input. Color not set.")
                self.set_id()
        else:
             self.pieceid = '_' + self.pieceid


    def create(self):
        if Config.board[self.y][self.x] != '___':
            po = Config.board[self.y][self.x]
            print(f'Piece {po} erased to make room for {self.pieceid}')

        Config.board[self.y][self.x] = self.pieceid

    def teleport(self, pos, record=False):
        Config.board[self.y][self.x] = '___'

        if record:
            self.moves += 1
            coord = Config.l_num_to_coord(pos)
            if Config.board[coord[0]][coord[1]] != '___':
                self.captured.append(Config.board[coord[0]][coord[1]])
                print(f'{self.pieceid} has captured {Config.board[coord[0]][coord[1]]}!')

        self.x = Config.tile_convert(pos[0])
        self.y = Config.tile_convert(pos[1], True)

        Config.board[self.y][self.x] = self.pieceid

        Config.print_board()

    def move(self, pos):
        if pos in self.possible_moves():
            coord = Config.l_num_to_coord(pos)
            if Config.board[coord[0]][coord[1]] != '___':
                self.captured.append(Config.board[coord[0]][coord[1]])
                print(f'{self.pieceid} has captured {Config.board[coord[0]][coord[1]]}!')
                # Erase piece

            if self.__class__ == Pawn:
                if abs(int(pos[1]) - Config.tile_convert(self.y, True)) == 2:
                    self.two_move = True

            self.teleport(pos)
            self.moves += 1

        else:
            print(f'Unable to move to {pos}')


    def get_info(self):
        print(f'{self.__class__.__name__}:\n')
        print('ID: ', self.pieceid)
        print('Position: ', Config.tile_convert(self.x), Config.tile_convert(self.y, True), sep='')
        print('Color: ', self.color)

    def erase(self):  # Doesn't delete the piece. It can be brought back by moving it to a square
        Config.board[self.y][self.x] = '___'
        self.erased = True

    def demo(self, rec=True):  # default board
        for pos in self.demo_moves:
            self.teleport(pos, rec)
            sleep(Config.DEMTIME)

        if self.__class__ == Pawn:
            self.promote2(Queen)

    @staticmethod
    def castle(king, rook):
        if not king.moves and not rook.moves:
            if not king.in_check:
                pass


class Pawn(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8')
        self.two_move = False

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        # Forward
        if not self.moves: # Starting Position
            if self.color != 'White':
                for new_y in (y+1, y+2):
                    try:
                        if Config.board[new_y][x] == '___':
                            pos_moves.append(Config.coord_to_tile(x, new_y))
                        else: break
                    except IndexError: pass

            if self.color != 'Black':
                for new_y in (y-1, y-2):
                    try:
                        if Config.board[new_y][x] == '___':
                            pos_moves.append(Config.coord_to_tile(x, new_y))
                        else: break
                    except IndexError: pass

        else:  # Post-Start
            if self.color != 'White':
                try:
                    if Config.board[y+1][x] == '___':
                        pos_moves.append(Config.coord_to_tile(x, y+1))
                except IndexError: pass

            if self.color != 'Black':
                try:
                    if Config.board[y-1][x] == '___':
                        pos_moves.append(Config.coord_to_tile(x, y-1))
                except IndexError: pass

        # Capturing
        if self.color != 'White':
            if self.color is not None:
                try:
                    if Config.c_convert(self.color) in Config.board[y+1][x+1]:
                        pos_moves.append(Config.coord_to_tile(x+1, y+1))
                except IndexError: pass
            else:
                try:
                    if Config.board[y+1][x+1] != '___':
                        pos_moves.append(Config.coord_to_tile(x+1, y+1))
                except IndexError: pass

        if self.color != 'Black':
            if self.color is not None:
                try:
                    if Config.c_convert(self.color) in Config.board[y-1][x-1]:
                        pos_moves.append(Config.coord_to_tile(x-1, y-1))
                except IndexError: pass
            else:
                try:
                    if Config.board[y+1][x+1] != '___':
                        pos_moves.append(Config.coord_to_tile(x-1, y-1))
                except IndexError: pass

        # En Passant


        return sorted(pos_moves)

    def promote(self, piece):  # oringal_piece = original_piece.promote(new_piece)
        pos = f'{Config.tile_convert(self.x)}{Config.tile_convert(self.y, True)}'

        return piece(pos, color=self.color, num='p')

    def promote2(self, piece):
        pos = f'{Config.tile_convert(self.x)}{Config.tile_convert(self.y, True)}'

        self.__class__ = piece
        self.__init__(pos, self.color, 'p')


class Knight(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('e1', 'f3', 'g5', 'h7', 'f8', 'e6', 'c5', 'd3', 'e1')

    def possible_moves(self):
        pos_moves = []
        for x_off, y_off in ( (1, 2), (-1, 2), (1, -2), (-1, -2), (2, 1), (-2, 1), (2, -1), (-2, -1) ):
            new_x = self.x + x_off
            new_y = self.y + y_off
            if 0 <= new_x < Config.b_len and 0 <= new_y < Config.b_len:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(new_y, True)}')
                else:
                    pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(new_y, True)}')

        return sorted(pos_moves)


class Bishop(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('a1', 'e5', 'b8', 'h2', 'e5', 'a1')

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        right_up = zip(range(x + 1, Config.b_len), range(y - 1, -1, -1))
        right_down = zip(range(x + 1, Config.b_len), range(y + 1, Config.b_len))

        left_up = zip(range(x - 1, -1, -1), range(y - 1, -1, -1))
        left_down = zip(range(x - 1, -1, -1), range(y + 1, Config.b_len))

        for r in (right_up, right_down, left_up, left_down):
            for new_x, new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(new_y, True)}')
                        if Config.board[new_y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(new_y, True)}')

        return sorted(pos_moves)


class Rook(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('a1', 'a8', 'h8', 'h1', 'a1')

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        # Horizontal
        for r in (range(x+1, Config.b_len), reversed(range(x))):
            for new_x in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[y][new_x]:
                        pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(y, True)}')
                        if Config.board[y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(y, True)}')
                    if Config.board[new_y][new_x] != '___': break

        # Vertical
        for r in (range(y+1, Config.b_len), reversed(range(y))):
            for new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][x]:
                        pos_moves.append(f'{Config.tile_convert(x)}{Config.tile_convert(new_y, True)}')
                        if Config.board[new_y][x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convert(x)}{Config.tile_convert(new_y, True)}')
                    if Config.board[new_y][new_x] != '___': break

        return sorted(pos_moves)


class Queen(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('a1', 'h8', 'a8', 'h1', 'a1')

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        # Horizontal
        for r in (range(x+1, Config.b_len), reversed(range(x))):
            for new_x in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[y][new_x]:
                        pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(y, True)}')
                        if Config.board[y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(y, True)}')
                    if Config.board[new_y][new_x] != '___': break

        # Vertical
        for r in (range(y+1, Config.b_len), reversed(range(y))):
            for new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][x]:
                        pos_moves.append(f'{Config.tile_convert(x)}{Config.tile_convert(new_y, True)}')
                        if Config.board[new_y][x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convert(x)}{Config.tile_convert(new_y, True)}')
                    if Config.board[new_y][new_x] != '___': break

        #Diagonal
        right_up = zip(range(x + 1, Config.b_len), range(y - 1, -1, -1))
        right_down = zip(range(x + 1, Config.b_len), range(y + 1, Config.b_len))

        left_up = zip(range(x - 1, -1, -1), range(y - 1, -1, -1))
        left_down = zip(range(x - 1, -1, -1), range(y + 1, Config.b_len))

        for r in (right_up, right_down, left_up, left_down):
            for new_x, new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(new_y, True)}')
                        if Config.board[new_y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(new_y, True)}')

        return sorted(pos_moves)


class King(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('e4', 'd5', 'c4', 'c5', 'd6', 'e5', 'e4')
        self.in_check = False

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        for x_off, y_off in ( (0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (-1, 1), (-1, -1), (1, -1) ):
            new_x = self.x + x_off
            new_y = self.y + y_off
            if 0 <= new_x < Config.b_len and 0 <= new_y < Config.b_len:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(new_y, True)}')
                else:
                    pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(new_y, True)}')


        return sorted(pos_moves)


# Some commands to start out with

Config.new_board('default')

r1 = Rook(color='w')
n1 = Knight('a5', color='b')
p1 = Pawn('e1', color='w')
p2 = Pawn('e8', color='b')
p3 = Pawn('f7', color='w')
r1.teleport('b3')
from time import sleep

class Config:
    DEMTIME = .8  # seconds
    types = {'min': 1, 'miniature': 3, 'small': 5, 'default': 8, 'extended': 11, 'large': 15, 'massive': 20, 'max': 26}
    letters = tuple('abcdefghijklmnopqrstuvwxyz')
    white_pieces = {'Pawn' : "♙", 'Rook' : "♖", 'Knight' : "♘", 'Bishop' : "♗", 'King' : "♔", 'Queen' : "♕" }
    black_pieces = {'Pawn' : "♟", 'Rook' : "♜", 'Knight' : "♞", 'Bishop' : "♝", 'King' : "♚", 'Queen' : "♛" }
    board = 'UNINITIALIZED'
    b_len = 'UNINITIALIZED'

    @classmethod
    def new_board(cls, btype='default'):
        def size(x):
            return [['___' for _ in range(x)] for _ in range(x)], x

        s = False

        if btype is not None:
            btype = btype.lower()

            if 'custom' in btype:
                btype = int(btype.replace('custom', '').strip())
                if 1 <= btype <= 26:
                    cls.board, cls.b_len = size(btype)
                    s = True
                else:
                    btype = None
                    cls.new_board(btype)
            elif btype in cls.types:
                cls.board, cls.b_len = size(cls.types[btype])
                s = True
            else:
                print(f'Unable to initialize board of unknown type {btype}')

        else:
            print('Unable to initalize board with a size lower than 1 or greater than 26')

        if s: cls.print_board()

    @classmethod
    def print_board(cls):
        if Config.b_len != 'UNINITIALIZED':
            def printl():
                if len(str(cls.b_len)) == 2:
                    print(' ', end='')
                for x in range(cls.b_len):
                    print(' '*6 + f'{cls.letters[x]}', end='')
                print('\n')

            print('\n'*2)
            printl()
            for x in range(cls.b_len):
                print(f'{cls.b_len-x:0{len(str(cls.b_len))}}  {cls.board[x]}  {cls.b_len-x:0{len(str(cls.b_len))}}\n')
            printl()
            print('\n'*4)

        else:
            print('Unable to print board of uninitialized type')

    @classmethod
    def tile_convert(cls, x, display_tile=False):
        if not display_tile:
            if isinstance(x, str):
                return cls.letters.index(x)
            else:
                return cls.letters[x]
        else:  # display_tile converts the letter in {letter}{number} to a number
            return cls.b_len - int(x)

    @classmethod
    def l_num_to_coord(cls, pos):
        return Config.b_len - int(pos[1]), int(Config.tile_convert(pos[0]))

    @classmethod
    def coord_to_tile(cls, x, y):
        return f'{Config.tile_convert(x)}{Config.tile_convert(y, True)}'

    @classmethod
    def c_convert(cls, color):
        if color == 'White':
            return 'b'
        if color == "Black":
            return 'w'

class ChessPiece:
    def __init__(self, pos, color, num, piece):
        self.x = int(Config.tile_convert(pos[0]))
        self.y = Config.b_len - int(pos[1])
        self.color = color
        self.piece = piece
        self.pieceid = num
        self.moves = 0
        self.captured = []
        self.erased = False
        self.set_id()
        self.create()
        Config.print_board()

    def __str__(self):
        return self.piece

    def __repr__(self):
        return self.pieceid

    def set_id(self):
        if self.__class__.__name__ != "Knight":
            self.pieceid = f'{self.piece[0]}{self.pieceid}'
        else:
            self.pieceid = f'N{self.pieceid}'

        if self.color is not None:
            if self.color.lower() in ('black', 'white', 'b', 'w'):
                self.pieceid = self.color.lower()[0] + self.pieceid
                if self.color.lower() == 'b':
                    self.color = 'Black'
                elif self.color.lower() == 'w':
                    self.color = 'White'
            else:
                self.color = None
                print("Invalid color input. Color not set.")
                self.set_id()
        else:
             self.pieceid = '_' + self.pieceid


    def create(self):
        if Config.board[self.y][self.x] != '___':
            po = Config.board[self.y][self.x]
            print(f'Piece {po} erased to make room for {self.pieceid}')

        Config.board[self.y][self.x] = self.pieceid

    def teleport(self, pos, record=False):
        Config.board[self.y][self.x] = '___'

        if record:
            self.moves += 1
            coord = Config.l_num_to_coord(pos)
            if Config.board[coord[0]][coord[1]] != '___':
                self.captured.append(Config.board[coord[0]][coord[1]])
                print(f'{self.pieceid} has captured {Config.board[coord[0]][coord[1]]}!')

        self.x = Config.tile_convert(pos[0])
        self.y = Config.tile_convert(pos[1], True)

        Config.board[self.y][self.x] = self.pieceid

        Config.print_board()

    def move(self, pos):
        if pos in self.possible_moves():
            coord = Config.l_num_to_coord(pos)
            if Config.board[coord[0]][coord[1]] != '___':
                self.captured.append(Config.board[coord[0]][coord[1]])
                print(f'{self.pieceid} has captured {Config.board[coord[0]][coord[1]]}!')
                # Erase piece

            if self.__class__ == Pawn:
                if abs(int(pos[1]) - Config.tile_convert(self.y, True)) == 2:
                    self.two_move = True

            self.teleport(pos)
            self.moves += 1

        else:
            print(f'Unable to move to {pos}')


    def get_info(self):
        print(f'{self.__class__.__name__}:\n')
        print('ID: ', self.pieceid)
        print('Position: ', Config.tile_convert(self.x), Config.tile_convert(self.y, True), sep='')
        print('Color: ', self.color)

    def erase(self):  # Doesn't delete the piece. It can be brought back by moving it to a square
        Config.board[self.y][self.x] = '___'
        self.erased = True

    def demo(self, rec=True):  # default board
        for pos in self.demo_moves:
            self.teleport(pos, rec)
            sleep(Config.DEMTIME)

        if self.__class__ == Pawn:
            self.promote2(Queen)

    @staticmethod
    def castle(king, rook):
        if not king.moves and not rook.moves:
            if not king.in_check:
                pass


class Pawn(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('e1', 'e2', 'e3', 'e4', 'e5', 'e6', 'e7', 'e8')
        self.two_move = False

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        # Forward
        if not self.moves: # Starting Position
            if self.color != 'White':
                for new_y in (y+1, y+2):
                    try:
                        if Config.board[new_y][x] == '___':
                            pos_moves.append(Config.coord_to_tile(x, new_y))
                        else: break
                    except IndexError: pass

            if self.color != 'Black':
                for new_y in (y-1, y-2):
                    try:
                        if Config.board[new_y][x] == '___':
                            pos_moves.append(Config.coord_to_tile(x, new_y))
                        else: break
                    except IndexError: pass

        else:  # Post-Start
            if self.color != 'White':
                try:
                    if Config.board[y+1][x] == '___':
                        pos_moves.append(Config.coord_to_tile(x, y+1))
                except IndexError: pass

            if self.color != 'Black':
                try:
                    if Config.board[y-1][x] == '___':
                        pos_moves.append(Config.coord_to_tile(x, y-1))
                except IndexError: pass

        # Capturing
        if self.color != 'White':
            if self.color is not None:
                try:
                    if Config.c_convert(self.color) in Config.board[y+1][x+1]:
                        pos_moves.append(Config.coord_to_tile(x+1, y+1))
                except IndexError: pass
            else:
                try:
                    if Config.board[y+1][x+1] != '___':
                        pos_moves.append(Config.coord_to_tile(x+1, y+1))
                except IndexError: pass

        if self.color != 'Black':
            if self.color is not None:
                try:
                    if Config.c_convert(self.color) in Config.board[y-1][x-1]:
                        pos_moves.append(Config.coord_to_tile(x-1, y-1))
                except IndexError: pass
            else:
                try:
                    if Config.board[y+1][x+1] != '___':
                        pos_moves.append(Config.coord_to_tile(x-1, y-1))
                except IndexError: pass

        # En Passant


        return sorted(pos_moves)

    def promote(self, piece):  # oringal_piece = original_piece.promote(new_piece)
        pos = f'{Config.tile_convert(self.x)}{Config.tile_convert(self.y, True)}'

        return piece(pos, color=self.color, num='p')

    def promote2(self, piece):
        pos = f'{Config.tile_convert(self.x)}{Config.tile_convert(self.y, True)}'

        self.__class__ = piece
        self.__init__(pos, self.color, 'p')


class Knight(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('e1', 'f3', 'g5', 'h7', 'f8', 'e6', 'c5', 'd3', 'e1')

    def possible_moves(self):
        pos_moves = []
        for x_off, y_off in ( (1, 2), (-1, 2), (1, -2), (-1, -2), (2, 1), (-2, 1), (2, -1), (-2, -1) ):
            new_x = self.x + x_off
            new_y = self.y + y_off
            if 0 <= new_x < Config.b_len and 0 <= new_y < Config.b_len:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(Config.coord_to_tile(new_x, new_y))
                else:
                    pos_moves.append(Config.coord_to_tile(new_x, new_y))

        return sorted(pos_moves)


class Bishop(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('a1', 'e5', 'b8', 'h2', 'e5', 'a1')

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        right_up = zip(range(x + 1, Config.b_len), range(y - 1, -1, -1))
        right_down = zip(range(x + 1, Config.b_len), range(y + 1, Config.b_len))

        left_up = zip(range(x - 1, -1, -1), range(y - 1, -1, -1))
        left_down = zip(range(x - 1, -1, -1), range(y + 1, Config.b_len))

        for r in (right_up, right_down, left_up, left_down):
            for new_x, new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(Config.coord_to_tile(new_x, new_y))
                        if Config.board[new_y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(Config.coord_to_tile(new_x, new_y))

        return sorted(pos_moves)


class Rook(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('a1', 'a8', 'h8', 'h1', 'a1')

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        # Horizontal
        for r in (range(x+1, Config.b_len), reversed(range(x))):
            for new_x in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[y][new_x]:
                        pos_moves.append(Config.coord_to_tile(new_x, y))
                        if Config.board[y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(Config.coord_to_tile(new_x, y))
                    if Config.board[new_y][new_x] != '___': break

        # Vertical
        for r in (range(y+1, Config.b_len), reversed(range(y))):
            for new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][x]:
                        pos_moves.append(Config.coord_to_tile(x, new_y))
                        if Config.board[new_y][x] != '___': break
                    else: break
                else:
                    pos_moves.append(Config.coord_to_tile(x, new_y))
                    if Config.board[new_y][new_x] != '___': break

        return sorted(pos_moves)


class Queen(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('a1', 'h8', 'a8', 'h1', 'a1')

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        # Horizontal
        for r in (range(x+1, Config.b_len), reversed(range(x))):
            for new_x in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[y][new_x]:
                        pos_moves.append(Config.coord_to_tile(new_x, y))
                        if Config.board[y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convert(new_x)}{Config.tile_convert(y, True)}')
                    if Config.board[new_y][new_x] != '___': break

        # Vertical
        for r in (range(y+1, Config.b_len), reversed(range(y))):
            for new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][x]:
                        pos_moves.append(Config.coord_to_tile(x, new_y))
                        if Config.board[new_y][x] != '___': break
                    else: break
                else:
                    pos_moves.append(f'{Config.tile_convert(x)}{Config.tile_convert(new_y, True)}')
                    if Config.board[new_y][new_x] != '___': break

        #Diagonal
        right_up = zip(range(x + 1, Config.b_len), range(y - 1, -1, -1))
        right_down = zip(range(x + 1, Config.b_len), range(y + 1, Config.b_len))

        left_up = zip(range(x - 1, -1, -1), range(y - 1, -1, -1))
        left_down = zip(range(x - 1, -1, -1), range(y + 1, Config.b_len))

        for r in (right_up, right_down, left_up, left_down):
            for new_x, new_y in r:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(Config.coord_to_tile(new_x, new_y))
                        if Config.board[new_y][new_x] != '___': break
                    else: break
                else:
                    pos_moves.append(Config.coord_to_tile(new_x, new_y))

        return sorted(pos_moves)


class King(ChessPiece):
    def __init__(self, pos='a1', color=None, num='_'):
        ChessPiece.__init__(self, pos, color, num, self.__class__.__name__)
        self.demo_moves = ('e4', 'd5', 'c4', 'c5', 'd6', 'e5', 'e4')
        self.in_check = False

    def possible_moves(self):
        pos_moves = []

        x, y = self.x, self.y

        for x_off, y_off in ( (0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (-1, 1), (-1, -1), (1, -1) ):
            new_x = self.x + x_off
            new_y = self.y + y_off
            if 0 <= new_x < Config.b_len and 0 <= new_y < Config.b_len:
                if self.color is not None:
                    if self.color[0].lower() not in Config.board[new_y][new_x]:
                        pos_moves.append(Config.coord_to_tile(new_x, new_y))
                else:
                    pos_moves.append(Config.coord_to_tile(new_x, new_y))


        return sorted(pos_moves)


# Some commands to start out with

Config.new_board('default')

r1 = Rook(color='w')
n1 = Knight('a5', color='b')
p1 = Pawn('e1', color='w')
p2 = Pawn('e8', color='b')
p3 = Pawn('f7', color='w')
r1.teleport('b3')
added 66 characters in body
Source Link
Alec
  • 425
  • 3
  • 21

I plan to use the IDs in order to dictate piece images on the GUI.

I plan to use the IDs in order to dictate piece images on the GUI.

Edited tags, fix closing code fence
Source Link
AlexV
  • 7.4k
  • 2
  • 24
  • 47
Loading
Source Link
Alec
  • 425
  • 3
  • 21
Loading