First post, new to programming and having fun! All feedback on this post and my questions are welcome.
I’m working through Automate the Boring Stuff and attacking the first Chapter 5 problem Chess Dictionary Validator.
In this chapter, we used the dictionary value {‘1h’: ‘bking’, ‘6c’: ‘wqueen’, ‘2g’: ‘bbishop’, ‘5h’: ‘bqueen’, ‘3e’: ‘wking’} to represent a chess board. Write a function named isValidChessBoard() that takes a dictionary argument and returns True or False depending on if the board is valid.
A valid board will have exactly one black king and exactly one white king. Each player can only have at most 16 pieces, at most 8 pawns, and all pieces must be on a valid space from ‘1a’ to ‘8h’; that is, a piece can’t be on space ‘9z’. The piece names begin with either a ‘w’ or ‘b’ to represent white or black, followed by ‘pawn’, ‘knight’, ‘bishop’, ‘rook’, ‘queen’, or ‘king’. This function should detect when a bug has resulted in an improper chess board.
my questions and code:
- Is evaluating dictionary keys/values through these for loops + multiple if statements the “best practice”? It seems like a lot of code. Changing to include some elif caused issues if it followed with another if statement in the for loop.
- Line 23
if i[0] == 'b':
errors out because the chess spaces which are empty string values have no character at i[0]. What’s the best way to express/evaluate empty values? If it is with ”, should I add leading condition in the loop which evaluates value == ”, and then ‘continue’? - Why can I not collapse line 15 into 11 such that I have one statement:
if 'bking' or 'wking' not in board.values():
? If I try that, the statement result is True; however the dictionary contains both values so shouldn’t it evaluate to False and keep the code running?
def isValidChessBoard(board): while True: blackPieces = 0 whitePieces = 0 wpawn = 0 bpawn = 0 letterAxis = ('a','b','c','d','e','f','g','h') pieceColour = ('b','w') pieceType = ('pawn','knight','bishop','rook','queen','king') #one black king and one white king if 'bking' not in board.values(): print('KingError') return False break if 'wking' not in board.values(): print('KingError') return False break #each player has <= 16 pieces for i in board.values(): if i[0] == 'b': blackPieces+=1 if i[0] == 'w': whitePieces+=1 if whitePieces >= 17: print('TotalPieceError') return False break if blackPieces >= 17: print('TotalPieceError') return False break #each player has <= 8 pawns for i in board.values(): if i == 'wpawn': wpawn+=1 elif i == 'bpawn': bpawn+=1 if wpawn or bpawn >= 9: print('PawnError') return False break #all pieces must be on valid space from '1a' to '8h' for i in board.keys(): if int(i[0]) >= 9: print('SpacesError') return False break if i[1] not in letterAxis: print('yAxisError') return False break #piece names begin with 'w' or 'b' for i in board.values(): if i[0] not in pieceColour: print('WhiteOrBlackError') return False break #piece names must follow with 'pawn', 'knight', 'bishop', 'rook', 'queen', 'king' for i in board.values(): if i[1:] not in pieceType: print('PieceTypeError') return False return 'This board checks out' board = {'1a': 'bking','2a': 'bqueen','3a': 'brook','4a': 'brook', '5a': 'bknight','6a': 'bknight','7a':'bbishop','8a': 'bbishop', '1b': 'bpawn','2b': 'bpawn','3b': 'bpawn','4b':'bpawn', '5b': 'bpawn','6b': 'bpawn','7b': 'bpawn','8b': 'bpawn', '1c': 'wking','2c': 'wqueen','3c': 'wrook','4c': 'wrook', '5c': 'wbishop','6c': 'wbishop','7c': 'wknight','8c':'wknight', '1e': 'wpawn','2e': 'wpawn','3e': 'wpawn','4e': 'wpawn', '5e': 'wpawn','6e': 'wpawn','7e': 'wpawn','8e': 'wpawn', '1f': '','2f': '','3f': '','4f': '','5f': '','6f': '','7f': '','8f': '', '1g': '','2g': '','3g': '','4g': '','5g': '','6g': '','7g': '','8g': '', '1h': '','2h': '','3h': '','4h': '','5h': '','6h': '','7h': '','8h': '',} print(isValidChessBoard(board)) Error: Traceback (most recent call last): line 23, in isValidChessBoard if i[0] == 'b': IndexError: string index out of range
Advertisement
Answer
Edit: When you define valid count, it doesn’t take into consideration that you might get a pawn to the other end of the board and end up with two queens, though I don’t know how you could fix this without causing the program to accept wonky answers (such as two queens at the start of a game for instance). Any help here would be appreciated!
Since you asked if there was an alternative I present the following.
Steps
- Define the set of all chess pieces
- Define the valid count range of pieces by type
- Count the pieces on the board
- Check pieces counts in a valid range
- Check that positions are valid
- Check that pieces names are valid
Code
def isValidChessBoard(board): """Validate counts and location of pieces on board""" # Define pieces and colors pieces = ['king','queen','rook', 'knight','bishop', 'pawn'] colors = ['b', 'w'] # Set of all chess pieces all_pieces = set(color+piece for piece in pieces for color in colors) # Define valid range for count of chess pieces by type (low, high) tuples valid_counts = {'king': (1, 1), 'queen': (0, 1), 'rook': (0, 2), 'bishop': (0, 2), 'knight': (0, 2), 'pawn': (0, 8)} # Get count of pieces on the board piece_cnt = {} for v in board.values(): if v in all_pieces: piece_cnt.setdefault(v, 0) piece_cnt[v] += 1 # Check if there are a valid number of pieces for piece in all_pieces: cnt = piece_cnt.get(piece, 0) lo, hi = valid_counts[piece[1:]] if not lo <= cnt <= hi: # Count needs to be between lo and hi if lo != hi: print(f"There should between {lo} and {hi} {piece} but there are {cnt}") else: print(f"There should be {lo} {piece} but there are {cnt})") return False # Check if locations are valid for location in board.keys(): row = int(location[:1]) column = location[1:] if not ((1 <= row <= 8) and ('a' <= column <= "h")): print(f"Invaid to have {board[location]} at postion {location}") return False # Check if all pieces have valid names for loc, piece in board.items(): if piece: if not piece in all_pieces: print(f"{piece} is not a valid chess piece at postion {loc}") return False return True