def create_move(board, from_location, to_location, promotion_class=None):
    """ Create a move based on user from and to location as well as an optional promotion value

    :param board: Board class
    :param from_location: Location to move piece from
    :param to_location: Location to move piece to
    :param promotion_class: class of piece for pawn promotion
    :return: Move
    """
    # try to get the piece at origin location
    p = board.piece(from_location, board.current_player)
    if p is None:
        raise IOError('{} has no piece at {}'.format(board.current_player, from_location))

    # check all moves for a match in from and to location as well as promotion and promotion class
    for move in p.moves():
        if move.old_location == from_location and move.new_location == to_location:
            if (promotion_class is None and not move.promotion) or \
                    (promotion_class and move.promotion and move.promotion_piece_class == promotion_class):
                # check to make sure the move is valid
                if board.valid_move(move):
                    return move
                else:
                    raise IOError('move from {} to {} results in check'.format(from_location, to_location))

    # customize error message depending on if promotion is specified
    if promotion_class is None:
        raise IOError('move from {} to {} is not possible'.format(from_location, to_location))
    raise IOError('move from {} to {} with {} promotion is not possible'
                  .format(from_location, to_location, promotion_class.__name__.lower()))