class ElizaDeducer {
/*
* this class populates a special chat dictionary
* based on the matches added via its addPhraseMatcher function
* see subclass ElizaDeducerInitializer for example:
* ElizaDeducer ed = new ElizaDeducerInitializer(2); // 2 = limit of replies per input
*/
var babble2: [PhraseMatcher] = []
private var patternIndex: [String: [PhraseMatcher]] = [:]
private var responseCache: [String: [AXKeyValuePair]] = [:]
private let ec2: EventChatV2 // chat dictionary, use getter for access. hardcoded replies can also be added
init(lim: Int) {
self.ec2 = EventChatV2(lim: lim)
}
func getEc2() -> EventChatV2 {
return ec2
}
func learn(_ msg: String) {
// populate EventChat dictionary
// Check cache first
if let cachedResponses = responseCache[msg] {
ec2.addKeyValues(cachedResponses)
}
// Search for matching patterns
let potentialMatchers = getPotentialMatchers(msg)
for pm in potentialMatchers {
if pm.matches(msg) {
let response = pm.respond(msg)
responseCache[msg] = response
ec2.addKeyValues(response)
}
}
}
func learnedBool(_ msg: String) -> Bool {
// same as learn method but returns true if it learned new replies
var learned = false
// populate EventChat dictionary
// Check cache first
if let cachedResponses = responseCache[msg] {
ec2.addKeyValues(cachedResponses)
learned = true
}
// Search for matching patterns
let potentialMatchers = getPotentialMatchers(msg)
for pm in potentialMatchers {
if pm.matches(msg) {
let response = pm.respond(msg)
responseCache[msg] = response
ec2.addKeyValues(response)
learned = true
}
}
return learned
}
func respond(_ str1: String) -> String {
return ec2.response(str1)
}
func respondLatest(_ str1: String) -> String {
// get most recent reply/data
return ec2.responseLatest(str1)
}
private func getPotentialMatchers(_ msg: String) -> [PhraseMatcher] {
var potentialMatchers: [PhraseMatcher] = []
for (key, matchers) in patternIndex {
if msg.contains(key) {
potentialMatchers.append(contentsOf: matchers)
}
}
return potentialMatchers
}
func addPhraseMatcher(_ pattern: String, _ kvPairs: String...) {
var kvs: [AXKeyValuePair] = []
for i in stride(from: 0, to: kvPairs.count, by: 2) {
kvs.append(AXKeyValuePair(key: kvPairs[i], value: kvPairs[i + 1]))
}
let matcher = PhraseMatcher(matcher: pattern, responses: kvs)
babble2.append(matcher)
indexPattern(pattern, matcher)
}
private func indexPattern(_ pattern: String, _ matcher: PhraseMatcher) {
for word in pattern.components(separatedBy: .whitespaces) {
if patternIndex[word] == nil {
patternIndex[word] = []
}
patternIndex[word]?.append(matcher)
}
}
class PhraseMatcher {
let matcher: NSRegularExpression
let responses: [AXKeyValuePair]
init(matcher: String, responses: [AXKeyValuePair]) {
self.matcher = try! NSRegularExpression(pattern: matcher)
self.responses = responses
}
func matches(_ str: String) -> Bool {
let range = NSRange(location: 0, length: str.utf16.count)
return matcher.firstMatch(in: str, options: [], range: range) != nil
}
func respond(_ str: String) -> [AXKeyValuePair] {
let range = NSRange(location: 0, length: str.utf16.count)
var result: [AXKeyValuePair] = []
if let match = matcher.firstMatch(in: str, options: [], range: range) {
let tmp = match.numberOfRanges - 1
for kv in self.responses {
var tempKV = AXKeyValuePair(key: kv.getKey(), value: kv.getValue())
for i in 0..<tmp {
if let groupRange = Range(match.range(at: i + 1), in: str) {
let s = String(str[groupRange])
tempKV.setKey(tempKV.getKey().replacingOccurrences(of: "{\(i)}", with: s).lowercased())
tempKV.setValue(tempKV.getValue().replacingOccurrences(of: "{\(i)}", with: s).lowercased())
}
}
result.append(tempKV)
}
}
return result
}
}
}