Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 86 additions & 101 deletions internal/rooms/rooms.go
Original file line number Diff line number Diff line change
Expand Up @@ -1577,153 +1577,138 @@ func (r *Room) FindContainerByName(containerNameSearch string) string {
}

func (r *Room) FindNoun(noun string) (foundNoun string, nounDescription string) {

if len(r.Nouns) == 0 {
return ``, ``
return "", ""
}

// Flatten the room nouns and create single-word aliases for multi-word nouns
roomNouns := map[string]string{}

for originalNoun, originalDesc := range r.Nouns {
roomNouns[originalNoun] = originalDesc

if strings.Contains(originalNoun, ` `) {
for _, n := range strings.Split(originalNoun, ` `) {

if _, ok := r.Nouns[n]; ok {
if strings.Contains(originalNoun, " ") {
for _, part := range strings.Split(originalNoun, " ") {
if _, exists := r.Nouns[part]; exists {
continue
}

if _, ok := roomNouns[n]; ok {
if _, exists := roomNouns[part]; exists {
continue
}

roomNouns[n] = `:` + originalNoun

roomNouns[part] = ":" + originalNoun
}
}

}

// Build candidate noun list
testNouns := util.SplitButRespectQuotes(noun)
ct := len(testNouns)
for i := 0; i < ct; i++ {

if splitCount := strings.Split(testNouns[i], ` `); len(splitCount) > 1 {

for _, n2 := range splitCount {

if len(n2) < 2 {
continue
}

testNouns = append(testNouns, n2)
for i := 0; i < len(testNouns); i++ {
if strings.Contains(testNouns[i], " ") {
for _, part := range strings.Split(testNouns[i], " ") {
testNouns = append(testNouns, strings.ToLower(strings.TrimSpace(part)))
}

}
}

// If it created more than one word, put the original back on as a full string to test
if len(testNouns) > 1 {
testNouns = append(testNouns, noun)
testNouns = append(testNouns, strings.ToLower(strings.TrimSpace(noun)))
}

for _, newNoun := range testNouns {
// Try each candidate: exact, singular/plural, alias-aware
for _, cand := range testNouns {
newNoun := strings.ToLower(strings.TrimSpace(cand))

// Direct match or single-level alias
if desc, ok := roomNouns[newNoun]; ok {
if desc[0:1] == `:` {
return desc[1:], roomNouns[desc[1:]]
if strings.HasPrefix(desc, ":") {
target := desc[1:]
if targetDesc, ok2 := roomNouns[target]; ok2 && !strings.HasPrefix(targetDesc, ":") {
return target, targetDesc
}
// alias->alias or missing target => ignore
} else {
return newNoun, desc
}
return newNoun, desc
}

if len(newNoun) < 2 {
continue
}

// If ended in `s`, strip it and add a new word to the search list
if newNoun[len(newNoun)-1:] == `s` {

testNoun := newNoun[:len(newNoun)-1]
if desc, ok := roomNouns[testNoun]; ok {
if desc[0:1] == `:` {
return desc[1:], roomNouns[desc[1:]]
// Strip "es"
if strings.HasSuffix(newNoun, "es") {
tn := strings.TrimSuffix(newNoun, "es")
if desc, ok := roomNouns[tn]; ok {
if strings.HasPrefix(desc, ":") {
target := desc[1:]
if targetDesc, ok2 := roomNouns[target]; ok2 && !strings.HasPrefix(targetDesc, ":") {
return target, targetDesc
}
} else {
return tn, desc
}
return testNoun, desc
}

} else {

testNoun := newNoun + `s`
if desc, ok := roomNouns[testNoun]; ok { // `s`` at end
if desc[0:1] == `:` {
return desc[1:], roomNouns[desc[1:]]
// Add "es"
tn := newNoun + "es"
if desc, ok := roomNouns[tn]; ok {
if strings.HasPrefix(desc, ":") {
target := desc[1:]
if targetDesc, ok2 := roomNouns[target]; ok2 && !strings.HasPrefix(targetDesc, ":") {
return target, targetDesc
}
} else {
return tn, desc
}
return testNoun, desc
}

}

// Switch ending of `y` to `ies`
if newNoun[len(newNoun)-1:] == `y` {

testNoun := newNoun[:len(newNoun)-1] + `ies`
if desc, ok := roomNouns[testNoun]; ok { // `ies` instead of `y` at end
if desc[0:1] == `:` {
return desc[1:], roomNouns[desc[1:]]
// "ies" -> "y"
if strings.HasSuffix(newNoun, "ies") {
tn := strings.TrimSuffix(newNoun, "ies") + "y"
if desc, ok := roomNouns[tn]; ok {
if strings.HasPrefix(desc, ":") {
target := desc[1:]
if targetDesc, ok2 := roomNouns[target]; ok2 && !strings.HasPrefix(targetDesc, ":") {
return target, targetDesc
}
} else {
return tn, desc
}
return testNoun, desc
}

}

if len(newNoun) < 3 {
continue
}
}

// Strip 'es' such as 'torches'
if newNoun[len(newNoun)-2:] == `es` {

testNoun := newNoun[:len(newNoun)-2]
if desc, ok := roomNouns[testNoun]; ok {
if desc[0:1] == `:` {
return desc[1:], roomNouns[desc[1:]]
}
return testNoun, desc
}

} else {

testNoun := newNoun + `es`
if desc, ok := roomNouns[testNoun]; ok { // `es` at end
if desc[0:1] == `:` {
return desc[1:], roomNouns[desc[1:]]
// Multi-word noun match
for full, desc := range roomNouns {
if strings.Contains(full, " ") {
for _, part := range testNouns {
if strings.Contains(full, part) {
if strings.HasPrefix(desc, ":") {
target := desc[1:]
if td, ok := roomNouns[target]; ok && !strings.HasPrefix(td, ":") {
return target, td
}
} else {
return full, desc
}
}
return testNoun, desc
}

}

if len(newNoun) < 4 {
continue
}
}

// Strip 'es' such as 'torches'
if noun[len(newNoun)-3:] == `ies` {

testNoun := newNoun[:len(newNoun)-3] + `y`
if desc, ok := roomNouns[testNoun]; ok { // `y` instead of `ies` at end
if desc[0:1] == `:` {
return desc[1:], roomNouns[desc[1:]]
// Single-word match for multi-word nouns
for full, desc := range roomNouns {
if !strings.Contains(full, " ") {
for _, part := range testNouns {
if part == full {
if strings.HasPrefix(desc, ":") {
target := desc[1:]
if td, ok := roomNouns[target]; ok && !strings.HasPrefix(td, ":") {
return target, td
}
} else {
return full, desc
}
}
return testNoun, desc
}

}

}

return ``, ``
return "", ""
}

func (r *Room) FindExitByName(exitNameSearch string) (exitName string, exitRoomId int) {
Expand Down
42 changes: 42 additions & 0 deletions internal/rooms/rooms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ func TestFindNoun(t *testing.T) {
"mystery": "just a riddle",
"secret": ":mystery", // chain alias -> :mystery
"projector screen": "a wide, matte-white screen", // multi-word matching
"island": "a tropical paradise",
"rocks": ":island",
"rocky island": ":island",
"feet": ":hands",
"hands": ":feet",
},
}

Expand Down Expand Up @@ -209,6 +214,43 @@ func TestFindNoun(t *testing.T) {
wantFoundNoun: "",
wantDesc: "",
},
{
name: "Multi-word input, first word valid (island)",
inputNoun: "island",
wantFoundNoun: "island",
wantDesc: "a tropical paradise",
},
// Issue #356: Fix Noun aliases
{
name: "Multi-word input, first word valid (rocks)",
inputNoun: "rocks",
wantFoundNoun: "island",
wantDesc: "a tropical paradise",
},
{
name: "Multi-word input, first word valid (rocky island)",
inputNoun: "rocky island",
wantFoundNoun: "island",
wantDesc: "a tropical paradise",
},
{
name: "Multi-word input, first word valid (rocky)",
inputNoun: "rocky",
wantFoundNoun: "island",
wantDesc: "a tropical paradise",
},
{
name: "circular references (hands)",
inputNoun: "hands",
wantFoundNoun: "",
wantDesc: "",
},
{
name: "circular references (feet)",
inputNoun: "feet",
wantFoundNoun: "",
wantDesc: "",
},
}

// Run each sub-test.
Expand Down
Loading