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 - Move to an adjacent node (1-20)\n") sb.WriteString(" shoot - 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 , shoot , 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 } } }