Tommorow begins the submission month for this year’s Loebner Prize. While I doubt anyone other than me can get together a ChatScript-based entry in time, I thought I would provide some code that would assist in that direction and teach esoteric features of ChatScript (not that you need esoterica yet, since you are still digesting the basics).
The ancestor of ChatScript, CHAT-L, had a built-in parser. It was too slow for long sentences and had been designed for newspaper English, not chat, so I did not include a parser in ChatScript, preferring to build in a POS tagger instead (still a work in progress). One can do some pseudo-parsey things with ChatScript, however.
Last years’ Loebner qualifying exam had a mixture of easy and hard questions. Suzette got a score of 11 (out of 22 I think) and the next nearest scores were 7.5, 7, and 6.5 . Among the hard questions Suzette missed was: John is taller than Mary and Mary is taller than Sue. Who is shorter, John or Sue? Suzette acquired the facts but wasn’t ready for the change of adjective frame of reference. So this year I had to improve the code for Rosette, my new chatbot.
Anyone can write rules to handle that specific question. The trick is to write few rules that are general. The following esoteric use of ChatScript illustrates principles and functions for behaving a bit like a parser. The goal is to handle incoming data in one or more sentences, across one or more volleys, and locally handling pronoun references.
This includes as input:
Tom is taller than Mary who is taller than Sarah.
Tom is fatter than Joan but she is thinner than Harry.
Tom is taller than Mary but shorter than Sarah.
Tom is taller than Mary. Sarah is shorter than Tom. …
The code below handles acquiring the facts (not answering the questions) but organizes the data for easy retrieval. It aims for generality. It also uses “^retry()” a function I find I failed to add into the manual, which will be there next release. Retry reexecutes the rule again. It also uses the table of “opposites” that ships with ChatScript.
concept: ~extensions (and but although yet still)
concept: ~than (then than)
topic: ~compare_test system repeat ()
# set local pronoun
s: ( ~propername * [who she he]) refine()
a: ( _~propername *~2 _who ) $$who = ‘_0
a: ( _~femalename * _she ) $she = ‘_0
a: ( _~malename * _he ) $he = ‘_0
# resolve pronouns
s: ( [who she he]) refine()
a: ( _who $$who) mark(~propername _0)
a: ( _he $he) mark(~propername _0)
a: ( _she $she) mark(~propername _0)
s: ( _~propername * ~propername *~2 _~extensions {be} {less more} ~adjective ~than ~propername)
mark(~propername _1 ) $$and = ‘_0
#! Tom is more tall than Mary
#! Tom is taller than Mary and Tom is shorter than Joan.
#! Tom is less tall than Mary
#! Tom is taller than harry but shorter than Joan.
s: FACTER ( _~propername {be} {more} _{less least} _~adjective ~than _~propername )
$$order = 1
if (_1) { $$order = $$order * -1 } # flip order
if ($adj)
{
if ($adj != _2 ) # they differ
{
if (query(direct_svo _2 opposite $adj ) ) # its the opposite
{
$$order = $$order * -1 # flip order
}
else {$adj = null} # accept new adjective
}
}
# adjust pronouns
if (_0 == who) { _0 = $$who}
else if (_0 == he) { _0 = $he}
else if (_0 == she) { _0 = $she}
else if (_0 ? ~extensions) { _0 = $$and}
if (_2 == who) { _2 = $$who}
else if (_2 == he) { _2 = $he}
else if (_2 == she) { _2 = $she}
if (!$adj)
{
$adj = _2
if ($$order == 1) { ^createfact(_0 $adj _3)}
else {^createfact(_3 $adj _0)}
}
else # already have an adjective to run with
{
if ($$order == 1)
{
^createfact(_0 _2 _3)
}
else
{
^createfact(_3 $adj _0)
}
}
unmark( ~propername _0)
unmark(~adjective _2 )
unmark(~propername _3 )
$$who = _3
retry()
A responder for handling questions given 2 people is:
#! who is taller, Tom or Harry?
#! Of Tom and harry, who is taller?
#! who is less tall, Tom or Harry?
#! who is the taller of Mary and Harry
#! Of Tom and harry, who is least tall?
?: COMPARE ([which who what] be {the} {more most} _{less least} _~adjective < * _~propername {and or } _~propername)
# 0=less 1=adj 2 = person1 3 = person2
$$order = 1
if (_0) { $$order = $$order * -1 } # flip order
if ($adj)
{
if ($adj != _1 and query(direct_svo _1 opposite $adj ) ) # they differ
{
$$order = $$order * -1
}
}
# find if _3 is more than _2
nofail(RULE eval(query(direct_vo ? $adj _2))) # who is taller than _2
_7 = @0subject
loop()
{
if (_7 == _3) {FAIL(rule)} # now matched
query(direct_vo ? $adj _7) # who is taller than this
_7 = @0subject
}
if ( $$order == 1) # normal order
{
if (@0subject) {_3 is.}
else {_2 is.}
}
else # inverse order
{
if (@0subject) {_2 is.}
else {_3 is.}
}
The tricks involve how ChatScript manages pattern matching. Stage one is to preprocess the input to mark in the dictionary where every word and ALL of its concept and dictionary inheritances occur in the sentence. So if word 1 of the sentence is “tiger”, then tiger and animal and mammal and ~animals and ~zoo might all get marked as occurring at word 1. So when a pattern tries to match, it can find any of those as being at position 1. But script can also mark word positions, and it can unmark them as well. So the FACTER rule matches a series of items and after creating a fact of them, erases their mark so a rescan of the same rule can try to find a new series of items.
The system builds facts in a specific order, like (X taller Y) and if a shorter comes it it flips the order and rewrites using common adjective notation.