def _fuzzily_matches(response, candidate):
    '''return True if response fuzzily matches candidate'''
    r_words = response.split()
    c_words = candidate.split()
    # match whole words first
    for word in r_words:
        if word in c_words:
            r_words.remove(word)
            c_words.remove(word)
    # match partial words, fewest matches first
    match_pairs = []
    for partial in sorted(r_words, key=lambda p: len(p), reverse=True):
        matches = [w for w in c_words if partial in w]
        match_pairs.append((partial, matches))
    # if all items can be uniquly matched, the match is passed
    while len(match_pairs):
        min_pair = min(match_pairs, key=lambda x:len(x[1]))
        # this is the partial and matches with the shortest match list
        # if there are ever no matches for something, the match is failed
        if len(min_pair[1]) == 0:
            return False
        # choose the match with the fewest matches to remaining partials.
        # that way we leave more options for more partials, for the best
        # chance of a full match
        partials_left = [p[0] for p in match_pairs]
        min_option = min(min_pair[1], key=lambda x:len([p for p in partials_left if x in p]))
        # remove the current pair - we've matched it now
        match_pairs.remove(min_pair)
        # remove the matched option from all pairs' options so it won't be matched again
        for pair in match_pairs:
            pair_options = pair[1]
            if min_option in pair_options:
                pair_options.remove(min_option)
    # if all the items in the response were matched, this is match
    return True
return True if response fuzzily matches candidate