6
\$\begingroup\$

This is an exercise in the Automate The Boring Stuff book. I am supposed to create a function that takes a dictionary argument that represents a chess board (ex: {'1h': 'bking', '6c': 'wqueen', '2g': 'bbishop', etc.}) and returns True or False depending on if the board is valid.

A valid board will have (A) exactly one black king and exactly one white king. (B) Each player can only have at most 16 pieces, (C) at most 8 pawns, and (D) all pieces must be on a valid space from '1a' to '8h'; that is, a piece can’t be on space '9z'. (E) 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.

#Example dictionary of coordinates (keys) and pieces (values)
#my_board = {"1a":"wking", "2a":"bking", "8b":"wpawn", "3a":"wpawn"}
def isValidChessBoard(board):
 #Creating a dictionary of pieces (keys) and the number of pieces (values) 
 num_pieces = {}
 list_pieces = ["bpawn", "bking", "bqueen", "brook", "bbishop", "bknight", "wpawn", "wking", "wqueen", "wrook", "wbishop", "wknight"]
 for v in board.values():
 num_pieces.setdefault(v, 0)
 num_pieces[v] = num_pieces[v] + 1
 for i in list_pieces:
 if i not in num_pieces:
 num_pieces.setdefault(i, 0)
 #Making sure there is exactly one king on each side
 if (num_pieces["wking"] != 1) or (num_pieces["bking"] != 1):
 return False
 #Making sure there are no more than 8 pawns on each side
 if ("bpawn" or "wpawn" in num_pieces) and (num_pieces["bpawn"] or num_pieces["wpawn"]) > 8:
 return False
 #Making sure every piece is apprpriately named (b/w + king/queen/rook/pawn/knight/bishop)
 for j in num_pieces:
 if j not in list_pieces:
 return False
 #Making sure there are no more than 16 pieces on each side
 if num_pieces["bpawn"] + num_pieces["bking"] + num_pieces["bqueen"] + num_pieces["brook"] + num_pieces["bbishop"] + num_pieces["bknight"] > 16:
 return False
 if num_pieces["wpawn"] + num_pieces["wking"] + num_pieces["wqueen"] + num_pieces["wrook"] + num_pieces["wbishop"] + num_pieces["wknight"] > 16:
 return False
 #Making sure that the board coordinates are all between (a-h, and 1-8)
 in_range = 0
 possible_coordinates = []
 for i in range(1, 9):
 for j in ["a", "b", "c", "d", "e", "f", "g", "h"]:
 possible_coordinates.append(str(i)+str(j))
 for k in board.keys():
 if k in possible_coordinates:
 in_range = in_range + 1 
 if len(board) != in_range:
 return False
 return True
if isValidChessBoard(my_board) == True:
 print("Valid Board")
else:
 print("Invalid Board")

I know that the code works, but I was wondering if there were any faux-pas that I am implementing, or if there is anything I could obviously do to make it cleaner. Thanks!

200_success
146k22 gold badges190 silver badges479 bronze badges
asked Dec 19, 2020 at 19:46
\$\endgroup\$
2
  • 5
    \$\begingroup\$ There are much more restrictions. For example, if all 8 pawns are intact, the side cannot have more than 2 Knights, or more than 2 Rooks, etc. \$\endgroup\$ Commented Dec 19, 2020 at 20:58
  • \$\begingroup\$ forgive me as i'm a noob at this but what does the line for i in list_pieces: if i not in num_pieces: num_pieces.setdefault(i, 0) mean? I understand what everything else means thanks \$\endgroup\$ Commented Feb 4, 2022 at 18:54

3 Answers 3

5
\$\begingroup\$

As you said the code works - there might be additional conditions for a valid board, that you are not checking for which you will be able to expand on using the same structure. I looked up the chapter from the book you mention and your implementation meets the requirements outlined there.

Since this is Python, there are more than one way to implement the requirements, at a minimum I'd recommend refactoring the code to be self documenting instead of relying on comments. This will require only minor refactoring of your code.

Then building on that may be consider taking a functional approach and utilizing python builtins. YMMV - here is my implementation, it is not perfect, but that is not the intent here.

def checkCriteria(pieces, fn, cnt):
 if len(list(filter(fn, pieces))) > cnt:
 return False
 
 return True
def checkValidPositions(input_pos):
 positions = set()
 for col in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']:
 for row in range(1, 9):
 positions.add(str(row) + col)
 return all(pos in positions for pos in input_pos)
def checkMaxPiecesByColor(pieces):
 return all(checkCriteria(pieces, lambda piece: len(piece) > 0 and piece[0] == ch, 16) for ch in ['w', 'b'])
def checkKingCount(pieces):
 return all(checkCriteria(pieces, lambda piece: piece == king, 1) for king in ['bking', 'wking'])
def checkPawnCount(pieces):
 return all(checkCriteria(pieces, lambda piece: piece == pawn, 8) for pawn in ['bpawn', 'wpawn'])
def checkValidPieces(pieces):
 valid_pieces = set()
 for color in ['w', 'b']:
 for piece in ['pawn', 'knight', 'bishop', 'rook', 'queen', 'king']:
 valid_pieces.add(color + piece)
 return len(pieces - valid_pieces) == 0
def isValidChessBoard(board):
 pieces = board.values()
 positions = board.keys()
 if not checkValidPositions(positions):
 return False
 
 if not checkMaxPiecesByColor(pieces):
 return False
 
 if not checkKingCount(pieces):
 return False
 
 if not checkPawnCount(pieces):
 return False
 
 if not checkValidPieces(set(pieces) - set([''])):
 return False
 
 return True
answered Dec 20, 2020 at 17:14
\$\endgroup\$
5
\$\begingroup\$

This code is not correct

#Making sure there are no more than 8 pawns on each side
if ("bpawn" or "wpawn" in num_pieces) and (num_pieces["bpawn"] or num_pieces["wpawn"]) > 8:
 return False

For example, it fails if num_pieces = {"bpawn":2, "wpawn":11}.

("bpawn" or "wpawn" in num_pieces) evaluates to "bpawn".

(num_pieces["bpawn"] or num_pieces["wpawn"]) evaluates to 2.

"bpawn" and 2 > 8 evaluates to False, so the test passes even though num_pieces["wpawn"] is 11.

It should be something like:

if num_pieces.get("bpawn", 0) > 8 or num_pieces.get("wpawn", 0) > 8:
 return False
answered Dec 22, 2020 at 1:10
\$\endgroup\$
2
\$\begingroup\$

This code will not work if in the board multiple pieces have the same position.

For example, below board contains 2 white pawns in the same position "3a". Even though the board is an invalid board, the program will return True.

my_board = {"1a":"wking", "2a":"bking", "3a":"wpawn", "3a":"wpawn"}
Sᴀᴍ Onᴇᴌᴀ
29.5k16 gold badges45 silver badges202 bronze badges
answered Nov 22, 2023 at 3:06
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.