Organization
It's a bit difficult to follow the logic of your function due to the large inner function is_valid_p() cutting it in half. Plus, most of the lines above this inner function are only used within it. So, I would move the inner function outside to make it more clear where work is being done.
def is_valid_p(current_pieces): #create a dictionary with all the pieces of each color as the key # and the valid starting number of the pieces as value pieces = {'pawn': 8,'knight': 2,'bishop': 2,'rook': 2,'queen': 1,'king': 1} colors = ['w','b'] all_pieces = {} #the valid dict to compare pieces and quantities to for color in colors: for p, num in pieces.items(): all_pieces[color + p] = num all_pieces[''] = 0 #no piece is valid if empty quare #accepts a list, checking if the pieces of the chessboard are valid if 'bking' not in current_pieces or 'wking' not in current_pieces: return False for c_p in current_pieces: #checking if the pieces' names are valid if c_p not in all_pieces: return False for p in all_pieces: if p in current_pieces and p != '': #looping through all the valid p in valid dict, if p is present #on the chessboard, count and compare to the valid number of the piece if current_pieces.count(p) > all_pieces[p]: return False return True def isValidChessboard(chessboard): # function accept a dict current_pieces = list(chessboard.values()) #get a list of all the pieces present on the chessboard valid = True for position in chessboard: #checking coordinates of the square if len(position) != 2: valid = False break elif int(position[0]) not in range(1, 9) or position[1] not in ['a','b','c','d','e','f','g','h']: valid = False break else: if not is_valid_p(current_pieces): #checking the pieces itself valid = False if not valid: print("Your board is invalid") else: print("Your board is valid")
Next, the logic of the isValidChessboard() can be cleaned up by removing the print() statements and having the function return True or False (most functions and methods that start with is___() return boolean values). Instead of using value to store the result, we can return once we know the answer.
def isValidChessboard(chessboard): # function accept a dict current_pieces = list(chessboard.values()) #get a list of all the pieces present on the chessboard for position in chessboard: #checking coordinates of the square if len(position) != 2: return False elif int(position[0]) not in range(1, 9) or position[1] not in ['a','b','c','d','e','f','g','h']: return False else: if not is_valid_p(current_pieces): #checking the pieces itself return False return True if not isValidChessboard(chessboard): print("Your board is invalid") else: print("Your board is valid")
Some last bits:
- Since there's no more
break within this for loop, the else clause will always happen, so let's save some indentation. - Instead of a
list of letters for the valid files, we can use a str to save space. - Let's move
current_pieces near to where it's used. (Side note: .values() already returns a list, so there's no need to convert it.) - Finally, since it's the last thing the function checks, we can return the result of
is_valid_p()
def isValidChessboard(chessboard): # function accept a dict for position in chessboard: #checking coordinates of the square if len(position) != 2: return False elif int(position[0]) not in range(1, 9) or position[1] not in 'abcdefgh': return False current_pieces = chessboard.values() return is_valid_p(current_pieces): #checking the pieces itself
Following the requirements of the task
There are three requirements on the number of pieces on the board:
- Exactly one white king and one black king.
- No more than 8 pawns of either color.
- No more than 16 pieces of either color.
There is no restriction on the number of any other piece. Because of pawn promotion, a pawn can be converted into any other non-king piece. It is legal for there to be 2, 3, 4, or even 9 white queens on the board (unlikely, but legal). So, the line where you check the number of each type of piece if current_pieces.count(p) > all_pieces[p]: in is_valid_p() is incorrect.
First, let's change the pieces variable to a list and handle the counts later.
def is_valid_p(current_pieces): #create a dictionary with all the pieces of each color as the key # and the valid starting number of the pieces as value pieces = ['pawn', 'knight', 'bishop', 'rook', 'queen', 'king'] colors = ['w', 'b'] all_pieces = [] for color in colors: for p in pieces: all_pieces.append(color + p) all_pieces.append('') #no piece is valid if empty quare #accepts a list, checking if the pieces of the chessboard are valid if 'bking' not in current_pieces or 'wking' not in current_pieces: return False for c_p in current_pieces: #checking if the pieces' names are valid if c_p not in all_pieces: return False # Still needs fixing for p in all_pieces: if p in current_pieces and p != '': #looping through all the valid p in valid dict, if p is present #on the chessboard, count and compare to the valid number of the piece if current_pieces.count(p) > all_pieces[p]: return False return True
At the point where we reach the comment # Still needs fixing, we know that there is one white king and one black king, and that all of the piece names are valid. So, we can use simple counts on the piece list to check pawns and other pieces. The task description doesn't mention blank squares, so I don't see a use for the inclusion of '' in all_pieces.
def isValidPieceSet(current_pieces): #create a dictionary with all the pieces of each color as the key # and the valid starting number of the pieces as value pieces = ['pawn', 'knight', 'bishop', 'rook', 'queen', 'king'] colors = ['w', 'b'] all_pieces = [] for color in colors: for p in pieces: all_pieces.append(color + p) #accepts a list, checking if the pieces of the chessboard are valid if 'bking' not in current_pieces or 'wking' not in current_pieces: return False for c_p in current_pieces: #checking if the pieces' names are valid if c_p not in all_pieces: return False if current_pieces.count('wpawn') > 8: return False if current_pieces.count('bpawn') > 8: return False # Count the white pieces if len([p for p in current_pieces if p.startswith('w')]) > 16: return False # Count the black pieces if len([p for p in current_pieces if p.startswith('b')]) > 16: return False return True
Let's put the script back together after giving is_valid_p() a clearer name and formatting to match the other function.
def isValidPieceSet(current_pieces): #create a dictionary with all the pieces of each color as the key # and the valid starting number of the pieces as value pieces = ['pawn', 'knight', 'bishop', 'rook', 'queen', 'king'] colors = ['w', 'b'] all_pieces = [] for color in colors: for p in pieces: all_pieces.append(color + p) #accepts a list, checking if the pieces of the chessboard are valid if 'bking' not in current_pieces or 'wking' not in current_pieces: return False for c_p in current_pieces: #checking if the pieces' names are valid if c_p not in all_pieces: return False if current_pieces.count('wpawn') > 8: return False if current_pieces.count('bpawn') > 8: return False # Count the white pieces if len([p for p in current_pieces if p.startswith('w')]) > 16: return False # Count the black pieces if len([p for p in current_pieces if p.startswith('b')]) > 16: return False return True def isValidChessboard(chessboard): # function accept a dict for position in chessboard: #checking coordinates of the square if len(position) != 2: return False elif int(position[0]) not in range(1, 9) or position[1] not in 'abcdefgh': return False current_pieces = chessboard.values() return isValidPieceSet(current_pieces): #checking the pieces itself if not isValidChessboard(chessboard): print("Your board is invalid") else: print("Your board is valid")
At this point, we have a script that's easy to read and easy to check for correctness. One further improvement would be to create a function for each requirement so that each function only does one thing, but that can come later.
One more note
The task description and this program both allow illegal board positions to be counted as valid: boards with 10 white queens, boards with pawns on the first and eighth ranks, boards with kings attacking each other, and many more. The reason for this is the problem of determining whether an arbitrary board position is legal is very hard. Such a program would have to implement all the rules of chess and then find a sequence of moves that results in the input board. Such a search could take literal millions of years. So, since this task is meant to be programming practice, the scope of the checks is restricted to what is easily programmable.
M-x untabify\$\endgroup\$