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.