-2

我正在用python下棋。一切正常,我将动作作为索引列表,但我决定改用一个类,因为我认为这样会更好。但是,现在获取所有合法移动的功能似乎不起作用,我发现这是因为出于某种原因它认为它可以在起跑板上将棋子从 a4 移动到 a5,我认为这可能是因为在检查 a2 到 a4 后它不会将位置更改回正常位置,而是将它移动到由列表列表组成的板上但是我找不到原因。该错误是由第 379-387 行中的代码引起的,如果它使您受到检查,它会阻止您采取行动,因为当我将其注释掉时,没有错误。我一直在寻找几个小时,所以如果有人可以提供帮助,那将不胜感激。

异常.py

    # Base class for other exceptions
    pass

class NoPieceThereError(Error):
    # When there's no piece on the selected tile
    pass

class InvalidFENError(Error):
    # When the inputed FEN is invalid
    pass

class WrongPieceColour(Error):
    # Chose the wrong piece colour
    pass

class InvalidMove(Error):
    # Not a legal move
    pass

class InvalidInput(Error):
    pass

class KingNotFound(Error):
    pass

国际象棋.py

import random

alphabet = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z".split()

def IsInt(number):
    try:
        int(number)
        return True
    except:
        return False #returns True if the input is a number

def AddTuples(tuple1, tuple2):
    return tuple([x + y for x, y in zip(tuple1, tuple2)])

def MultiplyTuple(tuple1, multiplyer):
    return tuple([int(x * multiplyer) for x in tuple1])

def PositionToIndex(position):
    try:
        row = int(position[1]) - 1
        column = alphabet.index(position[0].upper())
        return (row, column)  # Converts a position "e2" to an index "(1, 4)"
    except:
        raise Exceptions.InvalidInput

def IndexToPosition(index):
    try:
        row = str(index[0] + 1)
        column = alphabet[index[1]].lower()
        return column + row     # Converts an index "(1, 4)" to a position "e2"
    except:
        raise Exceptions.InvalidInput

class FEN():

    def __init__(self, passed_FEN):
        passed_FEN = passed_FEN.split()
        if len(passed_FEN) != 6:
            raise Exceptions.InvalidFENError

        self.board_FEN = passed_FEN[0]
        self.turn = passed_FEN[1]
        self.castling = passed_FEN[2]
        self.en_passant = passed_FEN[3]
        self.half_move_clock = passed_FEN[4]
        self.full_move_clock = passed_FEN[5]

    def __repr__(self):
        return " ".join([
            self.board_FEN,
            self.turn,
            self.castling,
            self.en_passant,
            self.half_move_clock,
            self.full_move_clock
            ])
   
    def ChangeTurn(self):
        self.turn = "b" if self.turn == "w" else "w"
   
    @property
    def board_FEN_split(self):
        return self.board_FEN.split("/")

class Move():
    
    def __init__(self, start_row, start_column, end_row, end_column, capturing_piece, capturing_row, capturing_column, FEN_before):
        self.start_row = start_row
        self.start_column = start_column
        self.end_row = end_row
        self.end_column = end_column
        self.capturing = capturing_piece
        self.capturing_row = capturing_row
        self.capturing_column = capturing_column
        self.FEN_before = FEN_before
    
    @property
    def start(self):
        return (self.start_row, self.start_column)
    
    @property
    def end(self):
        return (self.end_row, self.end_column)
    
    @property
    def capturing_pos(self):
        return (self.capturing_row, self.capturing_column)
    
    def __repr__(self):
        if self.capturing != None:
            string = f" capturing {self.capturing.__repr__()} at {IndexToPosition(self.capturing_pos)}"
        else:
            string = ""
        
        return f"Piece moved from {IndexToPosition(self.start)} to {IndexToPosition(self.end)}" + string + f", FEN before: {self.FEN_before}"
    
    def __eq__(self, other):
        return True if self.__repr__() == other.__repr__() else False
    
    @property
    def taking_equals_moved(self):
        return True if None == self.capturing_row == self.capturing_column or self.capturing_row == self.end_row and self.capturing_column == self.end_column else False
"""
class Engine():
    piece_values = {
        "p": 1,
        "n": 3,
        "b": 3,
        "r": 5,
        "q": 9
        }

    @staticmethod
    def EvaluatePosition(board):
        total = 0
        for row in board.board:
            for cell in row:
                if cell != None:
                    if cell.__repr__().lower() in Engine.piece_values:
                        if cell.__repr__().islower():
                            total -= Engine.piece_values[cell.__repr__()]
                        else:
                            total += Engine.piece_values[cell.__repr__().lower()]
        return total
    
    @staticmethod
    def ChooseComputerMove(board):
        move = random.choice(board.GetAllLegalMoves())
        board.MovePiece(*move)
    
    @staticmethod
    def Search(board, depth=10, alpha=0, beta=0):
        if depth == 0:
            return Engine.EvaluatePosition(board)

        moves = board.GetAllLegalMoves()

        if len(moves) == 0:
            if board.IsInCheck(board.FEN.turn):
                return -10000
            return 0

        for move in moves:
            board.MovePiece(*move)
            evaluation = -Engine.Search(board, depth-1, -beta, -alpha)
            board.UnmakeMove()
            if evaluation >= beta:
                # Move was too good so the opponent will avoid this position
                return beta

            alpha = max(alpha, evaluation)

        return alpha
"""
class Board():

    def __init__(self,
                 passed_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
                 extra_pieces = [],
                 board_height = 8,
                 board_width = 8
                 ):

        pieces = [
            #default pieces
            ('p', 'Pawn', Pawn),
            ('r', 'Rook', Rook),
            ('n', 'Knight', Knight),
            ('b', 'Bishop', Bishop),
            ('q', 'Queen', Queen),
            ('k', 'King', King),
            ] + extra_pieces #any extra piece types
       
        self.pieces_letters, self.pieces_names, self.pieces_classes = [tuple([piece[x] for piece in pieces]) for x in range(3)]
        self.FEN = FEN(passed_FEN)
        
        self.move_list = []
        self.board = []
        self.board_height = board_height
        self.board_width = board_width

        for row_index, row in enumerate(self.FEN.board_FEN_split[::-1]):
            self.board.append([])
            cell_index = 0
            for cell in row:
                if IsInt(cell):
                    [self.board[-1].append(None) for x in range(int(cell))]
                    cell_index += int(cell)
                else:
                    self.board[-1].append(self.CreatePiece(cell, (row_index, cell_index)))
                    cell_index += 1
   
    def CreatePiece(self, piece_code, position):
        piece_name = self.pieces_classes[self.pieces_letters.index(piece_code.lower())]
        colour = "w" if piece_code.upper() == piece_code else "b"
        return piece_name(colour, position, self)
   
    def DisplayBoard(self, debug = False):
        print()
        print()
        for index, column in enumerate(self.board[::-1]):
            if debug:
                print(7 - index, end = " ")
            else:
                print(8 - index, end = " ")
            print(" | ".join(" " if x == None else x.__repr__() for x in column))
            print("-" * 4 * self.board_width)
        print("  ", end = "")
        if debug:
            print(" | ".join([str(x) for x in range(self.board_width)]))
        else:
            print(" | ".join(alphabet[:self.board_width]))
   
    def GetBoardValue(self, row, column):
        return self.board[row][column]
    
    def FindKing(self, colour):
        for rowi, row in enumerate(self.board):
            for celli, cell in enumerate(row):
                if cell != None:
                    if cell.type == "King" and cell.colour == colour:
                        return (rowi, celli)
        raise Exceptions.KingNotFound
    
    def IsInCheck(self, colour):
        for row in self.board:
            for cell in row:
                if cell != None:
                    if cell.colour != colour:
                        if cell.IsChecking():
                            return True
        return False
    
    def GetAllLegalMoves(self):
        LegalMoves = []
       
        for row in self.board:
            for cell in row:
                if cell != None:
                    if cell.colour == self.FEN.turn:
                        LegalMoves += cell.LegalMoves()
       
        return LegalMoves
    
    def FindMove(self, moves = None, start_row = "*", start_column = "*", end_row = "*", end_column = "*"):
        if moves == None:
            moves = self.GetAllLegalMoves()
        for move in moves:
            if (
                (start_row == "*" or start_row == move.start_row) and
                (start_column == "*" or start_column == move.start_column) and
                (end_row == "*" or end_row == move.end_row) and
                (end_column == "*" or end_column == move.end_column)
                ):
                return move
        raise Exceptions.InvalidInput
    
    def MakeMove(self, move):

        self.move_list.append(move)
        
        # Changing en passant FEN
        if self.board[move.start_row][move.start_column].__repr__().lower() == "p" and abs(move.start_row - move.end_row) == 2:
            self.FEN.en_passant = IndexToPosition(((move.start_row + move.end_row) / 2, move.end_column))
        else:
            self.FEN.en_passant = "-"
        
        # Move piece
        print(move)
        self.board[move.start_row][move.start_row].position = move.end
        self.board[move.end_row][move.end_column] = self.board[move.start_row][move.start_column]
        self.board[move.start_row][move.start_column] = None
        
        # If taking a piece not at the location it moved to i.e. en passant
        if not move.taking_equals_moved:
            self.board[capturing_row][capturing_column] = None
        
    def UnmakeMove(self, number_to_undo=1):
        for x in range(number_to_undo):
            if self.move_list == []:
                print(f"No more moves to undo. Only undid {x} moves")
                return

            move = self.move_list.pop()
        
            # Move piece back
            self.board[move.end_row][move.end_column].position = move.start
            self.board[move.start_row][move.start_column] = self.board[move.end_row][move.end_column]

            self.board[move.end_row][move.end_column] = None

            # Restore captured piece
            if move.capturing != None:
                self.board[move.capturing_row][move.capturing_column] = move.capturing
        
            # Restore FEN
            self.FEN = move.FEN_before
    
    def OnBoard(self, row, column):
        return True if 0 <= row < self.board_height and 0 <= column < self.board_width else False

class Piece():

    def __init__(self, colour, position, board):
        self.colour = colour
        self.position = position
        self.board = board
   
    def SimilarMoves(self, directions, *args, **kwargs):
        legal_moves = []
        for direction in directions:
            legal_moves += self.CheckForCollision(direction, *args, **kwargs)
        return legal_moves
   
    def CheckForCollision(self, direction, limit, must_capture = False, can_capture = True, can_capture_en_passant = False, current_turn = False):
        legal_moves = []
        checks = 1
        current_pos = self.position
        
        while checks <= limit or limit == 0:
            current_pos = AddTuples(current_pos, direction)
            
            #if off the board
            if not self.board.OnBoard(*current_pos):
                break
           
            current_pos_piece = self.GetBoardValue(*current_pos)

            #if moving to an empty square
            if current_pos_piece == None:
                #capturing en passant

                #if there is an en passant somewhere and the piece is allowed to capture en passant
                if can_capture_en_passant and self.board.FEN.en_passant != "-":

                    #if it's capturing the en passant square
                    if PositionToIndex(self.board.FEN.en_passant) == current_pos:

                        #if it not capturing it's own piece
                        en_passant_start = self.board.board_height - 4 if self.colour == "w" else 3
                        if self.position[0] == en_passant_start:
                            move = Move(
                                *self.position,
                                *current_pos,
                                self.GetBoardValue(en_passant_start, current_pos[1]),
                                en_passant_start, current_pos[1],
                                FEN(self.board.FEN.__repr__()))

                            legal_moves.append(move)

                #moving to empty space
                elif not must_capture:
                    legal_moves.append(Move(
                        *self.position,
                        *current_pos,
                        self.GetBoardValue(*current_pos),
                        None, None,
                        FEN(self.board.FEN.__repr__())))
           
            #moving to own piece colour
            elif self.colour == current_pos_piece.colour:
                break

            #moving to opponent piece colour
            elif self.colour != current_pos_piece.colour:
                if can_capture:
                    legal_moves.append(Move(
                        *self.position,
                        *current_pos,
                        self.board.GetBoardValue(*current_pos),
                        *current_pos,
                        FEN(self.board.FEN.__repr__())
                        ))
                break
           
            checks += 1
        
        if current_turn == True:
            index = 0
            while index < len(legal_moves):
                self.board.MakeMove(legal_moves[index])
                if self.board.IsInCheck(self.colour):
                    legal_moves.pop(index)
                else:
                    index += 1
                self.board.UnmakeMove()

        return legal_moves
   
    @property
    def row(self):
        return self.position[0]
   
    @property
    def column(self):
        return self.position[1]
   
    def __repr__(self):
        letter = self.board.pieces_letters[self.board.pieces_names.index(self.type)]
        return letter.upper() if self.colour == "w" else letter
   
    def IsEmpty(self, row, column):
        return True if self.GetBoardValue(row, column) == None else False
   
    def GetBoardValue(self, row, column):
        return self.board.GetBoardValue(row, column)
   
    @property
    def direction(self):
        if self.colour == "w":
            return 1
        else:
            return -1
    
    @property
    def enemy_colour(self):
        if self.colour == "w":
            return "b"
        else:
            return "w"
    
    def CheckCorrectTurn(self):
        return True if self.colour == self.board.FEN.turn else False
    
    def IsChecking(self):
        enemy_king_pos = self.board.FindKing(self.enemy_colour)
        moves = self.LegalMoves(current_turn = False)
        for move in moves:
            if move == enemy_king_pos:
                return True
        return False

class Pawn(Piece):

    def __init__(self, colour, position, board):
        super().__init__(colour, position, board)
        self.type = "Pawn"
   
    def LegalMoves(self, current_turn = True):
        legal_moves = []
        print(self.position)
        on_starting_row = self.row == 1 and self.colour == 'w' or self.row == self.board.board_height - 2 and self.colour == "b"
       
        #forwards
        limit = 2 if on_starting_row else 1
        legal_moves += self.CheckForCollision((self.direction, 0), limit, can_capture = False, current_turn=current_turn)
       
        #capturing
        capturing_directions = ((self.direction, 1), (self.direction, -1))
        legal_moves += self.SimilarMoves(capturing_directions, 1, must_capture = True, can_capture_en_passant = True, current_turn=current_turn)

        return legal_moves

class Rook(Piece):
    directions = ((1, 0), (0, 1), (-1, 0), (0, -1))

    def __init__(self, colour, position, board):
        super().__init__(colour, position, board)
        self.type = "Rook"
   
    def LegalMoves(self, current_turn = True):
       
        legal_moves = self.SimilarMoves(self.directions, 0, current_turn=current_turn)
       
        return legal_moves

class Knight(Piece):
    directions = ((2, 1), (1, 2), (-1, 2), (-2, 1), (-2, -1), (-1, -2), (1, -2), (2, -1))

    def __init__(self, colour, position, board):
        super().__init__(colour, position, board)
        self.type = "Knight"
   
    def LegalMoves(self, current_turn = True):
       
        legal_moves = self.SimilarMoves(self.directions, 1, current_turn=current_turn)

        return legal_moves

class Bishop(Piece):
    directions = ((1, 1), (-1, 1), (-1, -1), (1, -1))

    def __init__(self, colour, position, board):
        super().__init__(colour, position, board)
        self.type = "Bishop"

    def LegalMoves(self, current_turn = True):
       
        legal_moves = self.SimilarMoves(self.directions, 0, current_turn=current_turn)

        return legal_moves

class King(Piece):
    directions = Rook.directions + Bishop.directions

    def __init__(self, colour, position, board):
        super().__init__(colour, position, board)
        self.type = "King"

    def LegalMoves(self, current_turn = True):
       
        legal_moves = self.SimilarMoves(self.directions, 1, current_turn=current_turn)
       
        return legal_moves      

class Queen(Piece):
    directions = Rook.directions + Bishop.directions

    def __init__(self, colour, position, board):
        super().__init__(colour, position, board)
        self.type = "Queen"

    def LegalMoves(self, current_turn = True):
       
        legal_moves = self.SimilarMoves(self.directions, 0, current_turn=current_turn)

        return legal_moves

def main():
   
    entered_FEN_valid = False
    while not entered_FEN_valid:
        try:
            player_FEN = input("Would you like to play with a custom FEN. If so put it here, or press enter for a default board: ")
            if not player_FEN:
                player_FEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
           
            b = Board(player_FEN)
            entered_FEN_valid = True

        except Exceptions.InvalidFENError:
            print("Invalid FEN")

    #no longer needed as I added Smith --> print("Moves are entered in the form \"{row} {column}\" with row and column being numbers from 0-7. Bottom left is 0 0. Example input: \"1 4\"")
    debug = False # True if input("Debug Mode? (Y/N): ").lower() == "y" else False
    player_colour = "w"
    player_won = False
    apawn = b.board[1][0]
    while not player_won:
        #if len(b.GetAllLegalMoves()) == 0:
        #    player_won = True
        #    print(b.IsInCheck("b"))
        #    if b.IsInCheck(b.FEN.turn):
        #        player_who_won = "w" if b.FEN.turn == "b" else "b"
        #    else:
        #        player_who_won = "no one"
        #    break

        try:
            if True: #b.FEN.turn == player_colour:
                b.DisplayBoard(debug)
                #print(Engine.Search(b, depth = 20))
                #print("\n".join([x.__repr__() for x in b.move_list]))
                #print([[IndexToPosition(position) for position in move] for move in b.GetAllLegalMoves()])
                #print(len(b.GetAllLegalMoves()))
                start = input("\nEnter start: ")
                
                if start.lower()[:4] == "undo":
                    b.UnmakeMove(int(start[-1]))
                else:
                    start = PositionToIndex(start)
                    if b.GetBoardValue(*start) == None:
                        raise Exceptions.NoPieceThereError
                   
                    moves = b.GetBoardValue(*start).LegalMoves()
                    if moves == []:
                        raise Exceptions.WrongPieceColour
                   
                    print("Legal moves: \n" + str([IndexToPosition((x.end_row, x.end_column)) for x in moves]))
                    end = PositionToIndex(input("\nEnter end: "))
                    print(start, end)
                    b.MakeMove(b.FindMove(
                        moves = moves,
                        start_row = start[0],
                        start_column = start[1],
                        end_row = end[0],
                        end_column = end[1]))

            else:
                Engine.ChooseComputerMove(b)

        except Exceptions.NoPieceThereError:
            print("There is no piece there")
           
        except Exceptions.WrongPieceColour:
            print("That is not the correct piece colour")
           
        except Exceptions.InvalidMove:
            print("That is not a legal move")
           
        #except Exceptions.InvalidInput:
            #   print("Invalid input")
           
        #except:
            #   print("Unknown error")
        
    print(player_who_won, "won!")

if __name__ == "__main__":
    #main()
    b = Board()
    print(b.GetAllLegalMoves())
4

1 回答 1

0

所以我发现碎片的位置一直在随机变化,所以我只是把它变成了 Piece 类的属性,每次都能找到它

class Piece():

    @property
    def position(self):
        for rowi, row in enumerate(self.board.board):
            for celli, cell in enumerate(row):
                if cell == self:
                    return (rowi, celli)
于 2021-06-25T12:01:28.710 回答