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:
2026-02-15 05:13:03 +01:00
parent 1a407ad4c2
commit aa569aac16
9 changed files with 1181 additions and 2 deletions

View 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:], " ")}
}