aboutsummaryrefslogtreecommitdiff
path: root/internal/game/hacking/wumpus.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-20 03:20:01 -0400
committerhistoria <[not public]>2026-06-20 03:20:01 -0400
commite4400c5f1e84c18d2126f2cb502ae10685977cbb (patch)
tree0523e9d68f1cfdc6f81b862e3068fe12c1a85054 /internal/game/hacking/wumpus.go
parent5ea082ab4e82409aebc889b496f43e52b003d4f7 (diff)
downloadthehouseoficarus-e4400c5f1e84c18d2126f2cb502ae10685977cbb.tar.gz
refactor: combined all station crafting
Diffstat (limited to 'internal/game/hacking/wumpus.go')
-rw-r--r--internal/game/hacking/wumpus.go286
1 files changed, 286 insertions, 0 deletions
diff --git a/internal/game/hacking/wumpus.go b/internal/game/hacking/wumpus.go
new file mode 100644
index 0000000..91b9243
--- /dev/null
+++ b/internal/game/hacking/wumpus.go
@@ -0,0 +1,286 @@
+package hacking
+
+import (
+ "fmt"
+ "math/rand"
+ "strconv"
+ "strings"
+)
+
+var dodecahedron = [20][3]int{
+ {1, 4, 7},
+ {0, 2, 9},
+ {1, 3, 11},
+ {2, 4, 13},
+ {0, 3, 5},
+ {4, 6, 14},
+ {5, 7, 16},
+ {0, 6, 8},
+ {7, 9, 17},
+ {1, 8, 10},
+ {9, 11, 18},
+ {2, 10, 12},
+ {11, 13, 19},
+ {3, 12, 14},
+ {5, 13, 15},
+ {14, 16, 19},
+ {6, 15, 17},
+ {8, 16, 18},
+ {10, 17, 19},
+ {12, 15, 18},
+}
+
+type WumpusGame struct {
+ player int
+ wumpus int
+ pits [2]int
+ ice [2]int
+ probes int
+ gameOver bool
+ won bool
+}
+
+func NewWumpusGame() *WumpusGame {
+ return &WumpusGame{}
+}
+
+func (w *WumpusGame) Name() string {
+ return "Hunt the Wumpus"
+}
+
+func (w *WumpusGame) Init(level int) string {
+ taken := make(map[int]bool)
+ w.player = rand.Intn(20)
+ taken[w.player] = true
+
+ w.wumpus = randFree(taken)
+ taken[w.wumpus] = true
+
+ w.pits[0] = randFree(taken)
+ taken[w.pits[0]] = true
+ w.pits[1] = randFree(taken)
+ taken[w.pits[1]] = true
+
+ w.ice[0] = randFree(taken)
+ taken[w.ice[0]] = true
+ w.ice[1] = randFree(taken)
+
+ w.probes = 5
+ w.gameOver = false
+ w.won = false
+
+ var sb strings.Builder
+ sb.WriteString("=== HUNT THE ROGUE AI ===\n")
+ sb.WriteString("\n")
+ sb.WriteString("You've jacked into an abandoned network. Somewhere in this maze of\n")
+ sb.WriteString("20 nodes, a rogue AI lurks. You have 5 probes to find and neutralize it.\n")
+ sb.WriteString("\n")
+ sb.WriteString("Hazards:\n")
+ sb.WriteString(" - Rogue AI: Moves to an adjacent node if you enter its node. Kills you.\n")
+ sb.WriteString(" - Data Traps: Fall in and you're fried. 2 in the network.\n")
+ sb.WriteString(" - ICE: Grabs you and dumps you in a random node. 2 in the network.\n")
+ sb.WriteString("\n")
+ sb.WriteString("Commands:\n")
+ sb.WriteString(" move <node> - Move to an adjacent node (1-20)\n")
+ sb.WriteString(" shoot <node> - Launch a probe into an adjacent node (1-20)\n")
+ sb.WriteString(" status - Show your current status\n")
+ sb.WriteString(" map - Show network map\n")
+ sb.WriteString(" jack out - Disconnect (forfeit)\n")
+ sb.WriteString("\n")
+ sb.WriteString(w.roomDesc())
+ return sb.String()
+}
+
+func (w *WumpusGame) HandleInput(input string) (string, bool, bool) {
+ input = strings.TrimSpace(strings.ToLower(input))
+ parts := strings.Fields(input)
+
+ if len(parts) == 0 {
+ return "Unknown command.", false, false
+ }
+
+ cmd := parts[0]
+
+ switch cmd {
+ case "move", "m":
+ if len(parts) < 2 {
+ return "Move where? (e.g., move 3)", false, false
+ }
+ n, err := strconv.Atoi(parts[1])
+ if err != nil || n < 1 || n > 20 {
+ return "Invalid node. Use 1-20.", false, false
+ }
+ return w.doMove(n - 1)
+ case "shoot", "probe", "launch", "s":
+ if len(parts) < 2 {
+ return "Shoot where? (e.g., shoot 3)", false, false
+ }
+ n, err := strconv.Atoi(parts[1])
+ if err != nil || n < 1 || n > 20 {
+ return "Invalid node. Use 1-20.", false, false
+ }
+ return w.doShoot(n - 1)
+ case "status":
+ return w.doStatus(), false, false
+ case "map":
+ return w.doMap(), false, false
+ default:
+ n, err := strconv.Atoi(cmd)
+ if err == nil && n >= 1 && n <= 20 {
+ return w.doMove(n - 1)
+ }
+ return "Commands: move <N>, shoot <N>, status, map, jack out", false, false
+ }
+}
+
+func (w *WumpusGame) doMove(target int) (string, bool, bool) {
+ if !w.hasAdjacent(w.player, target) {
+ adj := w.adjStrings(w.player)
+ return fmt.Sprintf("Can't move there. Adjacent nodes: %s", strings.Join(adj, ", ")), false, false
+ }
+ w.player = target
+ return w.checkRoom()
+}
+
+func (w *WumpusGame) doShoot(target int) (string, bool, bool) {
+ if !w.hasAdjacent(w.player, target) {
+ adj := w.adjStrings(w.player)
+ return fmt.Sprintf("Can't shoot there. Adjacent nodes: %s", strings.Join(adj, ", ")), false, false
+ }
+ w.probes--
+ if target == w.wumpus {
+ w.gameOver = true
+ w.won = true
+ return "Your probe hits the rogue AI! It destabilizes and crashes.\n\nConnection terminated.", true, true
+ }
+ var sb strings.Builder
+ sb.WriteString(fmt.Sprintf("Your probe finds nothing in node %d.", target+1))
+ if w.probes == 0 {
+ sb.WriteString("\nYou're out of probes. The rogue AI detects your presence and terminates your connection.")
+ w.gameOver = true
+ w.won = false
+ return sb.String(), true, false
+ }
+ if rand.Float64() < 0.75 {
+ w.wumpus = w.randomAdjacent(w.wumpus)
+ sb.WriteString("\nYou hear something shift in the network...")
+ }
+ sb.WriteString("\n")
+ sb.WriteString(w.roomDesc())
+ return sb.String(), false, false
+}
+
+func (w *WumpusGame) checkRoom() (string, bool, bool) {
+ var sb strings.Builder
+ cur := w.player
+ if cur == w.wumpus {
+ if rand.Float64() < 0.75 {
+ newRoom := w.randomAdjacent(w.wumpus)
+ w.wumpus = newRoom
+ sb.WriteString("--- Node " + fmt.Sprint(cur+1) + " ---\n")
+ sb.WriteString("The rogue AI stirs and relocates to a nearby node...\n")
+ sb.WriteString("\n")
+ sb.WriteString(w.roomDesc())
+ return sb.String(), false, false
+ }
+ sb.WriteString("The rogue AI devours your connection!\n\nConnection terminated.")
+ w.gameOver = true
+ w.won = false
+ return sb.String(), true, false
+ }
+ for _, p := range w.pits {
+ if cur == p {
+ sb.WriteString("You've fallen into a data trap! Your connection is severed.")
+ w.gameOver = true
+ w.won = false
+ return sb.String(), true, false
+ }
+ }
+ for _, i := range w.ice {
+ if cur == i {
+ sb.WriteString("ICE detected! You're relocated to a random node...\n")
+ w.player = rand.Intn(20)
+ chained, _, _ := w.checkRoom()
+ sb.WriteString(chained)
+ return sb.String(), false, false
+ }
+ }
+ sb.WriteString(w.roomDesc())
+ return sb.String(), false, false
+}
+
+func (w *WumpusGame) roomDesc() string {
+ cur := w.player
+ var sb strings.Builder
+ sb.WriteString(fmt.Sprintf("--- Node %d ---\n", cur+1))
+ adj := w.adjStrings(cur)
+ sb.WriteString(fmt.Sprintf("Tunnels lead to: %s\n", strings.Join(adj, ", ")))
+
+ for _, a := range dodecahedron[cur] {
+ if a == w.wumpus {
+ sb.WriteString("{196}You detect corrupted data nearby...{/}\n")
+ }
+ for _, p := range w.pits {
+ if a == p {
+ sb.WriteString("{208}You sense a void in the network...{/}\n")
+ }
+ }
+ for _, i := range w.ice {
+ if a == i {
+ sb.WriteString("{226}You hear static crackling...{/}\n")
+ }
+ }
+ }
+ return sb.String()
+}
+
+func (w *WumpusGame) doStatus() string {
+ return fmt.Sprintf("Node: %d | Probes: %d/5", w.player+1, w.probes)
+}
+
+func (w *WumpusGame) doMap() string {
+ var sb strings.Builder
+ sb.WriteString("=== Network Map ===\n")
+ for i := 0; i < 20; i++ {
+ marker := " "
+ if i == w.player {
+ marker = "*"
+ }
+ adj := dodecahedron[i]
+ sb.WriteString(fmt.Sprintf(" [%s] Node %2d -> %2d, %2d, %2d\n",
+ marker, i+1, adj[0]+1, adj[1]+1, adj[2]+1))
+ }
+ sb.WriteString(fmt.Sprintf("\nYou are at Node %d.\n", w.player+1))
+ return sb.String()
+}
+
+func (w *WumpusGame) hasAdjacent(from, to int) bool {
+ for _, a := range dodecahedron[from] {
+ if a == to {
+ return true
+ }
+ }
+ return false
+}
+
+func (w *WumpusGame) randomAdjacent(r int) int {
+ adj := dodecahedron[r]
+ return adj[rand.Intn(len(adj))]
+}
+
+func (w *WumpusGame) adjStrings(r int) []string {
+ var out []string
+ for _, a := range dodecahedron[r] {
+ out = append(out, fmt.Sprint(a+1))
+ }
+ return out
+}
+
+func randFree(taken map[int]bool) int {
+ for {
+ r := rand.Intn(20)
+ if !taken[r] {
+ return r
+ }
+ }
+}