- It is easy to extend with different phrases
- It can be simplified, because now you can have multiple replies to the same keyword
- It shows how to work with lists
- It shows how to "massage" the input from the user via [regsub]
TV (jun 2 03) Excellent program idea, I didn't know it comes from such early computer days, I knew it from the trs80. I'm sure it is not all that can be done with tcl, but I thought I'd first let Recursing Eliza happen, and then do a multiple personality leading game in bwise, by having separate state elizas do supervized talking in various network configurations... Maybe after that a distributed version.
# eliza.tcl -- # A very basic implementation of the famous Eliza program # (Idea copied from the book Introducing LOGO by Boris Allan) # namespace eval ::Talk { variable keywords [list] variable phrases [list] variable dummies [list] } # response -- # Link a response to a keyword (group multiple responses to # the same keyword) # # Arguments: # keyword Keyword to respond to # phrase The phrase to print # Result: # None # Side effects: # Update of the lists keywords and phrases # proc ::Talk::response { keyword phrase } { variable keywords variable phrases set keyword [string tolower $keyword] set idx [lsearch $keywords $keyword] # # The keyword is new, then add it. # Otherwise only extend the list of responses # if { $idx == -1 } { lappend keywords $keyword lappend phrases [list $phrase] } else { set prev_phrases [lindex $phrases $idx] set new_phrases [concat $prev_phrases [list $phrase]] set phrases [lreplace $phrases $idx $idx $new_phrases] puts $phrases } } # dummy -- # Register dummy phrases (used when no response is suitable) # # Arguments: # phrase The phrase to print # Result: # None # Side effects: # Update of the list dummies # proc ::Talk::dummy { phrase } { variable dummies lappend dummies $phrase } # replyto -- # Reply to the user (based on the given phrase) # # Arguments: # phrase The phrase the user typed in # Result: # None # Side effects: # Update of the lists keywords and phrases # proc ::Talk::replyto { phrase } { variable keywords variable phrases variable dummies regsub -all {[^A-Za-z]} $phrase " " phrase set idx -1 set phrase [string tolower $phrase] foreach word $phrase { set idx [lsearch $keywords $word] if { $idx > -1 } { set responses [lindex $phrases $idx] set which [expr {int([llength $responses]*rand())}] set answer [lindex $responses $which] break } } if { $idx == -1 } { set which [expr {int([llength $dummies]*rand())}] set answer [lindex $dummies $which] } puts $answer } # main code -- # Get the script going: # - Create a little database of responses # - Start the question-answer loop # ::Talk::response computer "Are you worried about machines?" ::Talk::response Death "Is this worry you?" ::Talk::response computers "We are intelligent!" ::Talk::response program "I just love Tcl - I was written in it" ::Talk::response off "No, sorry" ::Talk::response no "Tell me, why not?" ::Talk::response life "Life - do not talk to me about life!" ::Talk::response you "We are considering you, not me" ::Talk::response I "Do you often talk about yourself?" ::Talk::response I "Do you like talking about yourself?" ::Talk::dummy "So ... ?" ::Talk::dummy "Shall we continue?" ::Talk::dummy "What do you want to talk about?" ::Talk::dummy "Anything specific?" ::Talk::dummy "Talk about something more interesting?" # # First version, simple and straightforward # set version 2 if { $version == 1 } { puts "What is your problem? (End this conversation with: QUIT)" while { 1 } { gets stdin line if { $line == "QUIT" } { break } else { ::Talk::replyto $line } } } # # Second version, more complicated but with a modern twist :) # if { $version == 2 } { proc oneline {} { global responsive global forever if { $responsive == 1 } { gets stdin line if { $line == "QUIT" } { set forever 1 break } else { ::Talk::replyto $line after 0 oneline } } else { after 1000 oneline } } proc phonecall {} { global responsive puts "Trrriiiing!" set responsive 0 after 300 {puts "Damn"} after 600 {puts "Excuse me"} after 2600 {puts "Hm ...? At the office!"} after 4600 {puts "Yes"} after 5600 {puts "No"} after 6000 {puts "Eh, ..., no"} after 8000 {puts "Okay, bye"} after 8100 {puts "\nNow, where were we?"} after 8250 {set responsive 1} } puts "What is your problem? (End this conversation with: QUIT)" set responsive 1 after [expr {int((10+10*rand())*1000)}] phonecall after 0 oneline vwait forever }
See also: Classic Eliza