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 :)

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

\$\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\$
    – yfr
    Nov 20, 2020 at 21:26
  • \$\begingroup\$ Are you using Python 3? \$\endgroup\$
    – Reinderien
    Nov 20, 2020 at 21:27
  • \$\begingroup\$ Yup! Python 3.8 (64-bit) \$\endgroup\$
    – yfr
    Nov 20, 2020 at 21:29
0
\$\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)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.