suggest_side_quests skill v1 #100
@@ -314,28 +314,37 @@ func drop(q SideQuest, regulars regularNames, known *harness.Deduper, kept []Sid
|
||||
|
||||
// named returns the Regulars text names, by one rule. A mention of a
|
||||
// Regular is a place where text spells its name, ignoring case and spacing,
|
||||
// with the name's own outer punctuation optional ("Easy Beef Chili!" is
|
||||
// mentioned by "Easy Beef Chili." too) and no letter or digit just outside
|
||||
// it: the start or end of text, white space or punctuation must border it,
|
||||
// so "Beef Chili-inspired" and "Beef Chili's" mention "Beef Chili" while
|
||||
// "Beef Chilis" does not. A mention names its Regular unless a mention of a
|
||||
// different Regular covers the same text or more, so "Your Easy Beef
|
||||
// Chili." names "Easy Beef Chili" and not "Beef Chili", and text that two
|
||||
// Regulars spell alike names neither there.
|
||||
// with no letter, mark or digit just outside it: the start or end of text,
|
||||
// white space, punctuation or a symbol must border it, so "Beef
|
||||
// Chili-inspired" and "Beef Chili's" mention "Beef Chili" while "Beef
|
||||
// Chilis" does not, nor does "Cafe" with a combining accent after it
|
||||
// mention "Cafe". Punctuation at either end of the name, with any spacing
|
||||
// beside it, is optional, and a mention takes it in only where text spells
|
||||
// it and the border still holds after it: "Easy Beef Chili!" is mentioned
|
||||
// by "Easy Beef Chili." too, and by "Easy Beef Chili!x" without its "!". A
|
||||
// combining mark is part of its letter, never punctuation. A mention names
|
||||
// its Regular unless a mention of a different Regular covers the same text
|
||||
// or more, so "Your Easy Beef Chili." names "Easy Beef Chili" and not "Beef
|
||||
// Chili", and text that two Regulars spell alike names neither there.
|
||||
func (r regularNames) named(text string) []string {
|
||||
type mention struct {
|
||||
name string
|
||||
start, end int
|
||||
}
|
||||
text = exactName(text)
|
||||
bordered := func(start, end int) bool {
|
||||
before, _ := utf8.DecodeLastRuneInString(text[:start])
|
||||
after, _ := utf8.DecodeRuneInString(text[end:])
|
||||
return (start == 0 || !isWord(before)) && (end == len(text) || !isWord(after))
|
||||
}
|
||||
var mentions []mention
|
||||
for _, name := range r.all {
|
||||
full := exactName(name)
|
||||
core := strings.TrimFunc(full, isNotWord)
|
||||
core := strings.TrimFunc(full, isOuter)
|
||||
if core == "" {
|
||||
continue
|
||||
}
|
||||
lead := full[:len(full)-len(strings.TrimLeftFunc(full, isNotWord))]
|
||||
lead := full[:len(full)-len(strings.TrimLeftFunc(full, isOuter))]
|
||||
trail := full[len(lead)+len(core):]
|
||||
for at := 0; ; {
|
||||
i := strings.Index(text[at:], core)
|
||||
@@ -344,15 +353,13 @@ func (r regularNames) named(text string) []string {
|
||||
}
|
||||
start, end := at+i, at+i+len(core)
|
||||
at = start + 1
|
||||
before, _ := utf8.DecodeLastRuneInString(text[:start])
|
||||
after, _ := utf8.DecodeRuneInString(text[end:])
|
||||
if (start > 0 && !isNotWord(before)) || (end < len(text) && !isNotWord(after)) {
|
||||
if !bordered(start, end) {
|
||||
continue
|
||||
}
|
||||
if strings.HasSuffix(text[:start], lead) {
|
||||
if strings.HasSuffix(text[:start], lead) && bordered(start-len(lead), end) {
|
||||
start -= len(lead)
|
||||
}
|
||||
if strings.HasPrefix(text[end:], trail) {
|
||||
if strings.HasPrefix(text[end:], trail) && bordered(start, end+len(trail)) {
|
||||
end += len(trail)
|
||||
}
|
||||
mentions = append(mentions, mention{name, start, end})
|
||||
@@ -370,5 +377,9 @@ func (r regularNames) named(text string) []string {
|
||||
return names
|
||||
}
|
||||
|
||||
// isNotWord reports whether c is neither a letter nor a digit.
|
||||
func isNotWord(c rune) bool { return !unicode.IsLetter(c) && !unicode.IsDigit(c) }
|
||||
// isWord reports whether c is a letter, a mark on one or a digit.
|
||||
func isWord(c rune) bool { return unicode.IsLetter(c) || unicode.IsMark(c) || unicode.IsDigit(c) }
|
||||
|
||||
// isOuter reports whether c may be left off the end of a name: punctuation
|
||||
// or white space.
|
||||
func isOuter(c rune) bool { return unicode.IsPunct(c) || unicode.IsSpace(c) }
|
||||
|
||||
@@ -252,6 +252,11 @@ func TestSuggestSideQuestsReasonMentionRule(t *testing.T) {
|
||||
"punctuation-only difference": {[]string{"Beef Chili!"}, "Your Beef Chili! over corn chips.", "Beef Chili!", ""},
|
||||
"bare name inside punctuated": {[]string{"Beef Chili!"}, "Your Beef Chili! over corn chips.", "Beef Chili", none},
|
||||
"spelled alike by both": {[]string{"Beef Chili!"}, "Corn chips under your Beef Chili.", "Beef Chili", none},
|
||||
"punctuation then a letter": {[]string{"Beef Chili!"}, "Your Beef Chili!X is our inspiration.", "Beef Chili!", none},
|
||||
"letter before punctuation": {[]string{"¡Beef Chili"}, "Your X¡Beef Chili is our inspiration.", "¡Beef Chili", none},
|
||||
"stored symbol left off": {[]string{"Green Chili 🌶"}, "Like your Green Chili, gone green.", "Green Chili 🌶", none},
|
||||
"stored accent left off": {[]string{"Chicken Cafe\u0301"}, "Like your Chicken Cafe, gone green.", "Chicken Cafe\u0301", none},
|
||||
"accent written after name": {[]string{"Chicken Cafe"}, "Like your Chicken Cafe\u0301, gone green.", "Chicken Cafe", none},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
in := household
|
||||
|
||||
Reference in New Issue
Block a user