feat: add text adventure shell (PLAN.md 3.4)
Zork-style dungeon crawler set in an abandoned data center / medieval dungeon. 11 rooms, 6 items, 3 puzzles (dark room, locked door, maintenance panel), standard text adventure parser with verb aliases and direction shortcuts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
106
internal/shell/adventure/parser.go
Normal file
106
internal/shell/adventure/parser.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package adventure
|
||||
|
||||
import "strings"
|
||||
|
||||
type parsedCommand struct {
|
||||
verb string
|
||||
object string
|
||||
}
|
||||
|
||||
// directionAliases maps shorthand and full direction names to canonical forms.
|
||||
var directionAliases = map[string]string{
|
||||
"n": "north",
|
||||
"s": "south",
|
||||
"e": "east",
|
||||
"w": "west",
|
||||
"u": "up",
|
||||
"d": "down",
|
||||
"north": "north",
|
||||
"south": "south",
|
||||
"east": "east",
|
||||
"west": "west",
|
||||
"up": "up",
|
||||
"down": "down",
|
||||
}
|
||||
|
||||
// verbAliases maps aliases to canonical verbs.
|
||||
var verbAliases = map[string]string{
|
||||
"look": "look",
|
||||
"l": "look",
|
||||
"examine": "look",
|
||||
"inspect": "look",
|
||||
"x": "look",
|
||||
"go": "go",
|
||||
"move": "go",
|
||||
"walk": "go",
|
||||
"take": "take",
|
||||
"get": "take",
|
||||
"grab": "take",
|
||||
"pick": "take",
|
||||
"drop": "drop",
|
||||
"put": "drop",
|
||||
"use": "use",
|
||||
"apply": "use",
|
||||
"inventory": "inventory",
|
||||
"inv": "inventory",
|
||||
"i": "inventory",
|
||||
"help": "help",
|
||||
"?": "help",
|
||||
"quit": "quit",
|
||||
"exit": "quit",
|
||||
"logout": "quit",
|
||||
"q": "quit",
|
||||
}
|
||||
|
||||
// articles are stripped from input.
|
||||
var articles = map[string]bool{
|
||||
"the": true,
|
||||
"a": true,
|
||||
"an": true,
|
||||
}
|
||||
|
||||
// parseCommand parses raw input into a verb and object.
|
||||
func parseCommand(input string) parsedCommand {
|
||||
input = strings.TrimSpace(strings.ToLower(input))
|
||||
if input == "" {
|
||||
return parsedCommand{}
|
||||
}
|
||||
|
||||
words := strings.Fields(input)
|
||||
|
||||
// Strip articles.
|
||||
var filtered []string
|
||||
for _, w := range words {
|
||||
if !articles[w] {
|
||||
filtered = append(filtered, w)
|
||||
}
|
||||
}
|
||||
if len(filtered) == 0 {
|
||||
return parsedCommand{}
|
||||
}
|
||||
|
||||
first := filtered[0]
|
||||
|
||||
// Bare direction → go <direction>.
|
||||
if dir, ok := directionAliases[first]; ok {
|
||||
return parsedCommand{verb: "go", object: dir}
|
||||
}
|
||||
|
||||
// Known verb alias.
|
||||
if verb, ok := verbAliases[first]; ok {
|
||||
object := ""
|
||||
if len(filtered) > 1 {
|
||||
object = strings.Join(filtered[1:], " ")
|
||||
// Resolve direction alias in object for "go north" etc.
|
||||
if verb == "go" {
|
||||
if dir, ok := directionAliases[object]; ok {
|
||||
object = dir
|
||||
}
|
||||
}
|
||||
}
|
||||
return parsedCommand{verb: verb, object: object}
|
||||
}
|
||||
|
||||
// Unknown verb — return as-is so game can give an error.
|
||||
return parsedCommand{verb: first, object: strings.Join(filtered[1:], " ")}
|
||||
}
|
||||
Reference in New Issue
Block a user