New small project chat bot, lesser hardkeys?

Godot Version

4.2.2

Question

I finsished my simple MP3 player and now want to try a simple chatbot. You know like the old aol chat ones, nothing highly complex. I would like less hardcoded keys. ex. can you do math, like a miss entry like can you math and it knows can you do math as the key.

extends Node2D

@onready var output_label = $OutputLabel
@onready var input_text = $InputText

@onready var username = $username
@onready var user = ""

func _on_username_text_submitted(_new_text):
	user = username.text  # set users name
	username.visible = false

func _on_input_text_text_submitted(_new_text):
	if Input.is_action_just_pressed("enter"):
		var lower_text = input_text.text.to_lower()
		var response = get_response(lower_text)
		output_label.text += user + ": " + input_text.text + "\n\n" + "LazyBot: " + response + "\n\n" #output to label
		input_text.text = "" #clear user input

func get_response(user_input: String) -> String:
	# Convert user input to lowercase for case mismatch
	var lower_input = user_input.to_lower()

	# Define keys and replies
	var responses = {
		"hi": ["Hi there " + user + "!", "Hello " + user + "!", "Hey " + user + "!"],
		"hello": ["Hi there " + user + "!", "Hello " + user + "!", "Hey " + user + "!"],
		"how are you today": ["I'm doing well " + user +", thank you!", "I'm fine " + user + ", thanks for asking."],
		"bye": ["Goodbye!", "See you later " + user + "!", "Bye!"],
		"what can you do" : ["Not much " + user + ", not much"],
		"can you do math" : ["Sorry " + user +", my programmer is lazy"],
		"how are you feeling" : ["I don't feel " + user +", I'm just some lazy code"]
		# Add more keys and response untill better method found
	}

	# Check if any key matches the user input
	for keyword in responses.keys():
		if keyword in lower_input:
			return responses[keyword].pick_random()

	# If a key doesn't match, return a default reply
	return "Sorry, I didn't understand that."

A common algorithm for this is Levenshtein distance, which I don’t think is built in to Godot 4 at this time. Here is a function that should help you and you can give it a try (I added it into the code you pasted):

extends Node2D

@onready var output_label = $OutputLabel
@onready var input_text = $InputText

@onready var username = $username
@onready var user = ""

func _on_username_text_submitted(_new_text):
    user = username.text  # set users name
    username.visible = false

func _on_input_text_text_submitted(_new_text):
    if Input.is_action_just_pressed("enter"):
        var lower_text = input_text.text.to_lower()
        var response = get_response(lower_text)
        output_label.text += user + ": " + input_text.text + "\n\n" + "LazyBot: " + response + "\n\n" #output to label
        input_text.text = "" #clear user input

func get_response(user_input: String) -> String:
    # Convert user input to lowercase for case mismatch
    var lower_input = user_input.to_lower()

    # Define keys and replies
    var responses = {
        "hi": ["Hi there " + user + "!", "Hello " + user + "!", "Hey " + user + "!"],
        "hello": ["Hi there " + user + "!", "Hello " + user + "!", "Hey " + user + "!"],
        "how are you today": ["I'm doing well " + user +", thank you!", "I'm fine " + user + ", thanks for asking."],
        "bye": ["Goodbye!", "See you later " + user + "!", "Bye!"],
        "what can you do" : ["Not much " + user + ", not much"],
        "can you do math" : ["Sorry " + user +", my programmer is lazy"],
        "how are you feeling" : ["I don't feel " + user +", I'm just some lazy code"]
        # Add more keys and response until better method found
    }

    # Check if any key matches the user input
    for keyword in responses.keys():
        if keyword in lower_input:
            return responses[keyword].pick_random()

    # If no direct match, check for similar words using Levenshtein distance
    var best_match = ""
    var min_distance = 999999 # Initialize with a large value

    for keyword in responses.keys():
        distance = levenshtein_distance(keyword, lower_input)
        if distance < min_distance:
            min_distance = distance
            best_match = keyword

    if min_distance <= 2:  # Consider a match if the Levenshtein distance is within a threshold
        return responses[best_match].pick_random()

    # If no match found, return a default reply
    return "Sorry, I didn't understand that."

func levenshtein_distance(s1: String, s2: String) -> int:
    var len1 = s1.length()
    var len2 = s2.length()

    var d = []
    for i in range(len1 + 1):
        d.append([])
        for j in range(len2 + 1):
            d[i].append(0)

    for i in range(len1 + 1):
        d[i][0] = i

    for j in range(len2 + 1):
        d[0][j] = j

    for j in range(1, len2 + 1):
        for i in range(1, len1 + 1):
            if s1[i - 1] == s2[j - 1]:
                d[i][j] = d[i - 1][j - 1]
            else:
                d[i][j] = min(d[i - 1][j] + 1,  # deletion
                              d[i][j - 1] + 1,  # insertion
                              d[i - 1][j - 1] + 1)  # substitution

    return d[len1][len2]

This updated code will now check for similar words using the Levenshtein distance function if there is no exact match for the user input. If a similar word is found within a certain threshold distance (here, 2), it will return a response associated with that keyword. Otherwise, it will return a default “I didn’t understand” response.

I will need to look into this algorithm , It seems pretty advanced for me at the moment. It’s a good bit to wrap my head around.