5
\$\begingroup\$

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:

  1. Key of the passed arguments are not on the matrix 1a - 8h
  2. Value of the passed arguments are outside from a combination of (white & black) and ('pawn', 'knight', 'bishop', 'rook', 'queen', 'king')
  3. 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)
  4. 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 :)

asked Nov 20, 2020 at 5:47
\$\endgroup\$
3
  • 3
    \$\begingroup\$ Just a quick note: it's possible to have up to 9 queens per side, due to pawn promotion. \$\endgroup\$ Commented 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\$ Commented Nov 20, 2020 at 18:34
  • 1
    \$\begingroup\$ I'll try to improve it further. Thank you for the input! \$\endgroup\$ Commented Nov 20, 2020 at 21:30

2 Answers 2

5
\$\begingroup\$

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.

answered Nov 20, 2020 at 15:42
\$\endgroup\$
3
  • \$\begingroup\$ Thank you for the input! I wrote def is_valid_board(board_step: Dict[str, str]): and it gave me a NameError: name 'Dict' is not defined even after importing module typing. This error seems to be solved right after I wrote def is_valid_board(board_step: typing.Dict[str, str]):. I changed parameters from dctnry to board_step, as you suggested. \$\endgroup\$ Commented Nov 20, 2020 at 21:26
  • \$\begingroup\$ Are you using Python 3? \$\endgroup\$ Commented Nov 20, 2020 at 21:27
  • \$\begingroup\$ Yup! Python 3.8 (64-bit) \$\endgroup\$ Commented Nov 20, 2020 at 21:29
1
\$\begingroup\$

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)
answered Dec 7, 2020 at 11:33
\$\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.