Introduction
This is a very simple Eliza like chat bot. I call it Meliza Sharp. I wrote it for having fun learning the F# language shipped with Visual Studio 2010. There are some very good implementations of the Eliza chat bot written in LISP out there, in case you want to make your own chat bot. Search for it.
Meliza Thinking Code
The Meliza algorithm is based upon rules and pattern matching. And this is one of the powerful features of F#.
The following function defines the Pattern Identifiers: Wish
, Positive
, Negative
, and None
, and also when an input
string will match these pattern identifiers.
If the input
string matches with "want"
, "desire"
, or "need"
, then input
will match with the Wish
pattern identifier.
If the input
string matches with "certainly"
, "yes"
, "ok"
, or "right"
, then input
will match with the Positive
pattern identifier. And so on with the rest of the pattern identifiers.
This is what F# developers call active recognizers. Active recognizers act as sort of discriminators that will divide the input in different categories, in this case, in to different 'subjects' of conversation.
let (|Wish|Positive|Negative|None|) input =
match input with
| "want" | "desire" | "need"
-> Wish
| "certainly" | "yes" | "true" | "ok" | "right"
-> Positive
| "not" | "no" | "don't" | "false" | "wrong"
-> Negative
| _ -> None
In this other function, the exception handling try ... with
construction acts as a conditional. A match of the input
string with the pattern identifier Number
will be possible only if the conversion Convert.ToDouble(input)
succeeds.
let (|Number|None|) (input:string) =
try let value = Convert.ToDouble(input)
Number
with
| _ -> None
Once all the active recognizers we want for matching the string tokens are defined, we create the recursive response
function which takes the first token
of the string and the rest of it as parameters.
We search for a match with token
and the active recognizers defined above. That is, we find in which defined category or 'subject' the token
falls into.
If we find the token falls into one of the predefined categories, then we return the response for that category by calling the corresponding function response. And this will end the recursive process.
If a match is found with | None when (str.IndexOf(" ") > 0)
, we recursively pass the next token and the rest of the string to the response function. That is, we haven't found a category for the current token, and there is still more tokens in the string, so we call the response function with the next token and the remaining string str
, to be processed recursively.
If a match is found with | None when (str.IndexOf(" ") < 0)
, that is, we haven't found a category for the current token, and there is no more tokens in the string, we call the response function with the next token and the remaining string empty.
The when
statement sets a guard or condition to restrict the possible matches of token
with None
.
let rec response (token: string) (str: string) =
match token with
| Hello
-> hello_response ()
| Bye
-> good_bye_response ()
| Personal
-> personal_response str
| Question
-> question_response str
| Answer
-> answer_response str
| Wish
-> wish_response str
| Negative
-> negative_response str
| Positive
-> positive_response str
| Math
-> math_response str
| ""
-> none_response str
| None when (str.IndexOf(" ") > 0)
-> response (str.Substring(0,str.IndexOf(" ")))
(str.Substring(str.IndexOf(" ")+1))
| None when (str.IndexOf(" ") < 0)
-> response str ""
| None
-> math_response str
Finally, we create a set of functions which randomly select the responses on the 'subject' recognized by the response function and the active patterns.
Here are two of them:
let hello_response () =
let n = rand.Next(10)
match n with
| 0 -> "How do you do."
| 1 -> "Is nice talking to you."
| 2 -> "Tell me something new."
| 3 -> "Nice to meet you."
| 4 -> "My pleasure."
| 5 -> "Hi."
| 6 -> "Hello."
| 7 -> "Good day."
| 8 -> "Salutation!"
| 9 -> "Welcome!"
let good_bye_response () =
let n = rand.Next(10)
match n with
| 0 -> "Talk to you soon."
| 1 -> "It was nice talking to you."
| 2 -> "Good bye."
| 3 -> "Stay a bit longer."
| 4 -> "Adios amigo."
| 5 -> "Bye."
| 6 -> "Adios."
| 7 -> "See you."
| 8 -> "Please don't go"
| 9 -> "Why are you leaving me alone?"
How to Create the Visual F# Application and the Windows Form
- File -> New -> Project
Create a New Eliza Visual F# Application.
Add the Windows Components References:
- In Solution Explorer, right cick References -> Add Reference...
- Click on the .NET tab and select
System.Drawing
and System.Windows.Form
from the list. - Click the OK button.
Design the Form
There is no form designer within Visual Studio for F#, so what I recommend is to use the designer for another .NET language like VB.NET.
- Close the solution, or open another instance of Visual Studio.
- File -> New -> Project
Create a New Temporal Visual Basic, Windows Forms Application.
- Drag a
TextBox
from the Toolbox into the form. - Right click the TextBox within the form -> Properties.
- Set the
Name
property to text_box
. - Set the
Dock
property to Bottom
. - Drag another
TextBox
from the Toolbox into the form. - Right click the
TextBox
within the form -> Properties. - Set the
Name
property to conversation_box
. - Set the
Dock
property to Fill
. - Set the
Multiline
property to True
. - Save the Temporal project.
Translate the code into F#:
- Open the Eliza project.
- Go to File -> Open -> File...
- Browse to the Projects folders, Temporal project, open the Form1.Designer.vb file.
- Browse to
Private Sub InitializeComponent()
; in Form1.Designer.vb, there are the properties we must set for our form.
Add the code for the function to create our form:
let main_form title =
let text_box = new TextBox()
text_box.Dock <- DockStyle.Bottom
let conversation_text = new TextBox()
conversation_text.Dock <- DockStyle.Fill
conversation_text.Multiline <- true
conversation_text.WordWrap <- false
conversation_text.Font <- new Font(string("Arial"),float32(10))
conversation_text.ReadOnly <- true
let form = new Form(Text=title, Visible=true)
form.Size <- new Size(512, 512)
form.Controls.Add(conversation_text)
form.Controls.Add(text_box)
text_box.KeyDown.Add(enter_message text_box conversation_text)
let gotfocus = text_box.Focus()
form.KeyDown.Add(fun e -> if e.KeyCode = Keys.Escape then form.Close());
form
Build and run the application.
- Open the Eliza project.
- Go to Project -> Eliza Properties...
- In the Application tab, set Output type to Windows Applications.
- Press the F5 Key ;-)
Points of Interest
That's it!
History
Just posted.