Trackback from Sally Cruikshank: Bot Code (for Jesse) or anyone else who likes code
Aside from length, I trackback this because I can go to "Edit Html" and use <pre> tags to keep my code indentation intact from here. :)
So anyways, for Sally, I recommend a code approach like this if you are up to it.
This code is Javascript mind you; you can copy it and save it as an html file and test that in your browser if you'd like to make sure it works.
I am pretty confident it will become working Actionscript 3 if you only sprinkle in the right :type specifiers (the hard part 4me), replace "alert" with "trace", and remove the <script></script> tags. (oh yeah, and I use "prompt" near the end so.. hard code something? ;)
As this grows, you'll want to keep the Corpus in one or more seperate .as files I think, just to make life easier. But there should be no trouble compiling them straight in with the actionscript.
Ok: so this code is helpful because:
• It keeps the Pattern labels (ALICE/AIML calls them "categories") together with the regex patterns and together with the answer collections.
• It lets you easily use groups matched from the regex in the answer strings: just call upon $1, $2 etc
• If you want to call upon the entire match, $& might work (Perl told me it would lol) but if not you can just put () around the whole pattern. :)
• Finally, maybe best of all, it allows you to — at your own discretion — mix live functions with string answers in the corpus. So your bot can call out to a search engine, or obtain the time, or play chess.
There are some other ideas that could expand the mental powers further, but this should help get the osprey in the air. :)
GL Sally
<script>
// Other AI projects call "the list of facts known by the bot" a "corpus" of knowledge.
var corpus = {
// These labels like "rg0" (or whatever are arbitrary. Just don't let them overlap.
// Down the road this should ease in any design decisions where "ASK-AGE" might need to defer response to "ASK-IMPOLITE-QUESTION"
'rg0': {
'regexp': /goomba/i,
'answers': ["goomba is nice","GOOMBA?","goomba-never"]
},
// Should I be asking Whinsey about Goombas?? She plays Mario Bro now?
'rg1': {
'regexp': /It is/i,
'answers': ["It certainly is","is what?"]
},
'rg2': {
'regexp': /clean/i,
'answers': ["It's clean already","how clean","cleaner than dirt","like, Mr. Clean?"]
},
// Inspired by "clean your room" I am surmising?
'name': {
'regexp': /My name is (.*[a-z])/i,
'answers': ["Pleased to meet you, $1", "$1 is such a nice name!"]
},
// Regular expression memory demonstrated here
'pickanumber': {
'regexp': /Pick a number between (\d+) and (\d+)/i,
'answers': [
"Sorry, had a fly in my ear. Between what and what now?",
function(result) {
var a = parseInt(result[1]);
var b = parseInt(result[2]);
var m = Math.floor(Math.random() * (b-a))+a;
return("I choose "+ m);
}
]
},
// Function call outs demonstrated here. As well as sass.
// Javascript "anonymous functions" are endlessly entertaining to play with I think. :)
'default': {
'regexp': /./,
'answers': ["Whatchu talkin bout, willis?", "[insert cheezy pickup line here]"]
}
// I'm using a regexp to include a customizable default reply into the corpus.
}
// I challenge you to answer my question...
function challenge(testline)
{
var i;
// for(x in y) is a great construct for iterating over a hash structure. It's like your for(i=0; i<answers.length; i++)
// except who needs to use meaningless numbers for labels?
// Although I don't know if Actionscript randomizes the order for hashes. Some languages do, some don't.
// if that happens to you I'll write up a fix.
// So here, "i" iterates through every string label, like "rg0" or "pickanumber". i is a string.
for(i in corpus)
{
// if the regexp for the corpus line we are studying matches, we'll invoke "respond" and return..
// so we don't check any other options after that.
if(testline.search(corpus[i]['regexp'])>=0)
{
return(respond(testline, corpus[i], i));
}
}
// In case the corpus somehow lacks a customizable default reply, I've got a hardcoded one here.
return("Wow, I didn't see that coming!");
}
// respond gets called with the testline, the corpus segment matched, and the label for that segment.
function respond(testline, category, label)
{
// Your random code almost exactly... not bad to factor out into a helper function if you use it twice.
// you know, like I did in this example! (lazy++ ;)
var m = Math.floor(Math.random() * category.answers.length);
// So we choose an answer.. which is either a string or an anonymous function.
var ans = category.answers[m];
// We prep "tmp" as a result set for our match itself.
var tmp = testline.match(category.regexp);
if(typeof(ans)=='string')
// If ans is a string, we do something exciting. We match our regex against tmp[0],
// replacing it with ans, and return the result! tmp[0] is identical to testline
// except it lines up with our regexp so the regexp does not need ^ or $
// in order for the replacement to be complete.
// This is the magic engine that allows the $1..$9 tricks (and others) to work
// at no extra cost.
{ return(tmp[0].replace(category.regexp, ans)); }
// ans isn't a string? Hmm. Must be a function then.
// Call the function, pass in the results of the match as the only argument.
// Whatever the function returns becomes our answer.
else { return(ans(tmp)); }
}
var testline = prompt("String?", "");
alert(challenge(testline));
</script>
3 comments:
Hey Jesse, Could you please send me the code as a js file? Some of it is cut off in the blog. Thanks- funonmars at gmail.com
Your blog keeps getting better and better! Your older articles are not as good as newer ones you have a lot more creativity and originality now keep it up!
Wow, you know while I am kind of flattered by the most recent comment, I have reason to believe it is a crafty spam without any payload links to other sites. :/
It's anonymous, and gives praise which would fit in virtually any environment. Except perhaps for here, since everyone knows I am not "creative" per se! Especially not lately. ;D
Post a Comment