This project is in chapter 5 introduced me to a whole new world of dictionary data structure. To put it briefly, I want to know whether the choice I took between lists, tuples, and dictionaries could've been justifiable calls or not. I'm also not that confident about where putting many return statements correctly and many other beginners' common mistakes that I probably have overlooked.
The project's description and the code are shown below.
import pprint, copy
# Argument from Automate BS with Python
chessgood = {
'1h': 'bking', '6c': 'wqueen', '2g': 'bbishop', '5h': 'bqueen', '3e': 'wking'}
# Argument when key/value is ok but its representative value/key pair raises an error
chessbad = {'9z': 'bemperor', '10a': 'wking', '3a': 'bprince'}
# Argument when a piece is more than allowed (e.g., wpawn more than 8)
chessbad2 = {'4a': 'wpawn', '4b': 'wpawn', '4c': 'wpawn','4d': 'wpawn',
'4e': 'wpawn', '4f': 'wpawn', '4g': 'wpawn', '4h': 'wpawn', '5a': 'wpawn',
'5i': 'wpawn'}
def isvcb(dctnry):
'''
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.
'''
ver = ('a','b','c','d','e','f','g','h')
hor = (8, 7, 6, 5, 4, 3, 2, 1)
bd = dict()
for i in hor:
for j in ver:
bd.setdefault(str(i) + j, str(i) + j)
bdcopy = copy.copy(bd)
#pprint.pprint(bdu)
side = ('b','w')
piece = {'pawn': 8, 'knight': 2, 'bishop': 2, 'rook': 2, 'queen' : 1, 'king': 1}
pcs = dict()
for m in side:
for n in piece:
pcs.setdefault(m + n, piece[n])
temp = dict()
for k,v in dctnry.items():
temp.setdefault(v, 0)
temp[v] += 1
if k in bd and v in pcs and temp[v] <= pcs[v]:
bd[k] = v
print('Input OK: ', k)
elif k not in bd:
bd = bdcopy
print('Key(s) not in definition: ', k )
elif v not in pcs:
bd = bdcopy
print('Value(s) not in definition: ', v )
elif temp[v] > pcs[v]:
bd = bdcopy
print('Value of this piece exceeds threshold: ', v )
pprint.pprint(bd)
if bd == bdcopy:
return False
else:
return True
print('chessgood: ', isvcb(chessgood), '\n')
print('chessbad: ', isvcb(chessbad), ' \n')
print('chessbad2: ', isvcb(chessbad2), '\n')
I believe I covered all of the possible errors by error-checking using different arguments when calling the function, where:
- Key of the passed arguments are not on the matrix 1a - 8h
- Value of the passed arguments are outside from a combination of (white & black) and ('pawn', 'knight', 'bishop', 'rook', 'queen', 'king')
- All of the value from passed arguments don't exceed the threshold of each piece so that each piece will have aat most 16 pieces (e.g. wpawn is notmore than 8, bking is no more than 1)
- Show all the invalid input on the command line
If there are any other subtle restrictions that I haven't overseen. Please let me know :)
The results are shown below:
Input OK: 1h
Input OK: 6c
Input OK: 2g
Input OK: 5h
Input OK: 3e
{'1a': '1a',
'1b': '1b',
'1c': '1c',
'1d': '1d',
'1e': '1e',
'1f': '1f',
'1g': '1g',
'1h': 'bking',
'2a': '2a',
'2b': '2b',
'2c': '2c',
'2d': '2d',
'2e': '2e',
'2f': '2f',
'2g': 'bbishop',
'2h': '2h',
'3a': '3a',
'3b': '3b',
'3c': '3c',
'3d': '3d',
'3e': 'wking',
'3f': '3f',
'3g': '3g',
'3h': '3h',
'4a': '4a',
'4b': '4b',
'4c': '4c',
'4d': '4d',
'4e': '4e',
'4f': '4f',
'4g': '4g',
'4h': '4h',
'5a': '5a',
'5b': '5b',
'5c': '5c',
'5d': '5d',
'5e': '5e',
'5f': '5f',
'5g': '5g',
'5h': 'bqueen',
'6a': '6a',
'6b': '6b',
'6c': 'wqueen',
'6d': '6d',
'6e': '6e',
'6f': '6f',
'6g': '6g',
'6h': '6h',
'7a': '7a',
'7b': '7b',
'7c': '7c',
'7d': '7d',
'7e': '7e',
'7f': '7f',
'7g': '7g',
'7h': '7h',
'8a': '8a',
'8b': '8b',
'8c': '8c',
'8d': '8d',
'8e': '8e',
'8f': '8f',
'8g': '8g',
'8h': '8h'}
chessgood: True
Key(s) not in definition: 9z
Key(s) not in definition: 10a
Value(s) not in definition: bprince
{'1a': '1a',
'1b': '1b',
'1c': '1c',
'1d': '1d',
'1e': '1e',
'1f': '1f',
'1g': '1g',
'1h': '1h',
'2a': '2a',
'2b': '2b',
'2c': '2c',
'2d': '2d',
'2e': '2e',
'2f': '2f',
'2g': '2g',
'2h': '2h',
'3a': '3a',
'3b': '3b',
'3c': '3c',
'3d': '3d',
'3e': '3e',
'3f': '3f',
'3g': '3g',
'3h': '3h',
'4a': '4a',
'4b': '4b',
'4c': '4c',
'4d': '4d',
'4e': '4e',
'4f': '4f',
'4g': '4g',
'4h': '4h',
'5a': '5a',
'5b': '5b',
'5c': '5c',
'5d': '5d',
'5e': '5e',
'5f': '5f',
'5g': '5g',
'5h': '5h',
'6a': '6a',
'6b': '6b',
'6c': '6c',
'6d': '6d',
'6e': '6e',
'6f': '6f',
'6g': '6g',
'6h': '6h',
'7a': '7a',
'7b': '7b',
'7c': '7c',
'7d': '7d',
'7e': '7e',
'7f': '7f',
'7g': '7g',
'7h': '7h',
'8a': '8a',
'8b': '8b',
'8c': '8c',
'8d': '8d',
'8e': '8e',
'8f': '8f',
'8g': '8g',
'8h': '8h'}
chessbad: False
Input OK: 4a
Input OK: 4b
Input OK: 4c
Input OK: 4d
Input OK: 4e
Input OK: 4f
Input OK: 4g
Input OK: 4h
Value of this piece exceeds threshold: wpawn
Key(s) not in definition: 5i
{'1a': '1a',
'1b': '1b',
'1c': '1c',
'1d': '1d',
'1e': '1e',
'1f': '1f',
'1g': '1g',
'1h': '1h',
'2a': '2a',
'2b': '2b',
'2c': '2c',
'2d': '2d',
'2e': '2e',
'2f': '2f',
'2g': '2g',
'2h': '2h',
'3a': '3a',
'3b': '3b',
'3c': '3c',
'3d': '3d',
'3e': '3e',
'3f': '3f',
'3g': '3g',
'3h': '3h',
'4a': '4a',
'4b': '4b',
'4c': '4c',
'4d': '4d',
'4e': '4e',
'4f': '4f',
'4g': '4g',
'4h': '4h',
'5a': '5a',
'5b': '5b',
'5c': '5c',
'5d': '5d',
'5e': '5e',
'5f': '5f',
'5g': '5g',
'5h': '5h',
'6a': '6a',
'6b': '6b',
'6c': '6c',
'6d': '6d',
'6e': '6e',
'6f': '6f',
'6g': '6g',
'6h': '6h',
'7a': '7a',
'7b': '7b',
'7c': '7c',
'7d': '7d',
'7e': '7e',
'7f': '7f',
'7g': '7g',
'7h': '7h',
'8a': '8a',
'8b': '8b',
'8c': '8c',
'8d': '8d',
'8e': '8e',
'8f': '8f',
'8g': '8g',
'8h': '8h'}
chessbad2: False
I believe my code is way longer than necessary but I also believe that the result being true should be kept as the first priority. So as someone who has no background in coding. Any input is greatly appreciated. Thank you :)
-
3\$\begingroup\$ Just a quick note: it's possible to have up to 9 queens per side, due to pawn promotion. \$\endgroup\$Roland Illig– Roland Illig2020年11月20日 06:24:19 +00:00Commented Nov 20, 2020 at 6:24
-
\$\begingroup\$ Just wanted to throw in that this is an excellent amount of effort & thoughtfulness for someone without a background in coding. Keep with it :D \$\endgroup\$n1c9– n1c92020年11月20日 18:34:31 +00:00Commented Nov 20, 2020 at 18:34
-
1\$\begingroup\$ I'll try to improve it further. Thank you for the input! \$\endgroup\$yfr– yfr2020年11月20日 21:30:34 +00:00Commented Nov 20, 2020 at 21:30
2 Answers 2
Naming
Let's focus on a single variable, dctnry
- a somewhat nasty abbreviation of dictionary. We of course shouldn't name it dict
because that's a built-in, but neither name actually says what this is - a chess board that should simply be called board
. You can indicate that it's a dictionary, but there's a better way to do it - type hints, like board: Dict[str, str]
.
isvcb
is likewise impenetrable and should instead be is_valid
or is_valid_board
. Also, the textbook made a poor decision in suggesting isValidChessBoard
- the Python standard is lower_snake_case, i.e. is_valid_chess_board
.
Don't use the name bd
- call it perhaps full_board
, and pieces
instead of pcs
. temp
needs a better name as well.
Later down, your piece
dictionary should be pieces
because it's a collection.
Range
hor
should not use a manual tuple, and should instead use range(8, 0, -1)
.
Boolean expressions
if bd == bdcopy:
return False
else:
return True
should simply be
return bd != bdcopy
Dictionary literals
pcs = dict()
for m in side:
for n in piece:
pcs.setdefault(m + n, piece[n])
can simply be
pieces = {
m + n: piece_value
for m in side
for n, piece_value in piece.items()
}
I don't see a need for setdefault
, since I think the indexes being visited will be unique.
-
\$\begingroup\$ Thank you for the input! I wrote
def is_valid_board(board_step: Dict[str, str]):
and it gave me aNameError: name 'Dict' is not defined
even after importing module typing. This error seems to be solved right after I wrotedef is_valid_board(board_step: typing.Dict[str, str]):
. I changed parameters fromdctnry
toboard_step
, as you suggested. \$\endgroup\$yfr– yfr2020年11月20日 21:26:58 +00:00Commented Nov 20, 2020 at 21:26 -
\$\begingroup\$ Are you using Python 3? \$\endgroup\$Reinderien– Reinderien2020年11月20日 21:27:34 +00:00Commented Nov 20, 2020 at 21:27
-
\$\begingroup\$ Yup! Python 3.8 (64-bit) \$\endgroup\$yfr– yfr2020年11月20日 21:29:20 +00:00Commented Nov 20, 2020 at 21:29
This is my attempt. Comments are welcome! I couldn't figure out, how to check if the keys in the board are unique (because two pieces cannot be placed in the same field which would also give an invalid board). I tried with the len() and set() functions, but that didn't help.
board = {'1b': 'bking',
'6a': 'wqueen',
'3f': 'brook',
'4h': 'bknight',
'3e': 'wking',
'6d': 'wbishop',
'2g': 'wbishop',
'5c': 'bpawn',
'8g': 'bpawn',
'7b': 'bpawn'}
fieldInt = ['1', '2', '3', '4', '5', '6', '7', '8']
fieldChar = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
pieceColor = ['b', 'w']
pieces = ['king', 'queen', 'knight', 'rook', 'bishop', 'pawn']
def boardInventory(board):
# Makes an inventory of the board to be evaluated.
count = {}
for value in board.values():
count.setdefault(value, 0)
count[value] += 1
return count
def boardCounter(board):
# Checks if amounts of pieces are valid.
count = boardInventory(board)
item_total = 0
for value in count.values():
item_total += value
if count['bking'] == 1 and \
count['wking'] == 1 and \
0 <= count.get('bpawn', 0) <= 8 and \
0 <= count.get('wpawn', 0) <= 8 and \
count.get('brook', 0) <= 2 and \
count.get('wrook', 0) <= 2 and \
count.get('bknight', 0) <= 2 and \
count.get('wknight', 0) <= 2 and \
count.get('bbishop', 0) <= 2 and \
count.get('wbishop', 0) <= 2 and \
item_total <= 32:
return True
else:
return False
def fieldValidator(board):
# Checks if the board contains valid fields.
keyOK = 0
for key in board.keys():
if key[0] in fieldInt and key[1] in fieldChar:
keyOK += 1
else:
return False
if keyOK == len(board):
return True
def pieceValidator(board):
# Checks if the pieces are spelled correctly.
valueOK = 0
for value in board.values():
if value[0] in pieceColor and value[1:] in pieces:
valueOK += 1
else:
return False
if valueOK == len(board):
return True
def boardValidator(board):
# Checks if the board is valid, depending on the tests above. Prints
# an error message when board is invalid.
if fieldValidator(board) and pieceValidator(board) and \
boardCounter(board):
print('This is a valid chess board!')
elif fieldValidator(board) == False:
print('Invalid chess board! There is a wrong field in your \
board!')
elif pieceValidator(board) == False:
print('Invalid chess board! There is a wrong piece in your \
board!')
elif boardCounter(board) == False:
print('Invalid chess board! There is something wrong with the \
allowed amount of pieces!')
boardValidator(board)
Explore related questions
See similar questions with these tags.