⚠️ Warning: This is a draft ⚠️
This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.
If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.
{{collection|RCRPG}}
package main import ( "fmt" "io" "math/rand" "os" "sort" "strings" ) const ( VERSION_MAJOR = 1 VERSION_MINOR = 0 ) type Direction int const ( NORTH = Direction(iota) SOUTH EAST WEST UP DOWN NUM_DIRECTIONS = iota ) func (d Direction) Opposite() Direction { return d ^ 1 } func (d Direction) String() string { switch d { case NORTH: return "north" case SOUTH: return "south" case EAST: return "east" case WEST: return "west" case UP: return "up" case DOWN: return "down" } panic("Invalid direction!") } type Item int const ( NIL_ITEM = Item(iota) GOLD LADDER SLEDGE NUM_ITEMS = iota ) func (item Item) String() string { switch item { case GOLD: return "Gold coin" case LADDER: return "Ladder" case SLEDGE: return "Sledge hammer" } panic("Invalid item!") } type Room struct { X, Y, Z int Name string Passages [6]*Room Items map[Item]int } // Creates a new room func GenerateRoom(X, Y, Z int) *Room { room := new(Room) room.X, room.Y, room.Z = X, Y, Z room.Name = "" room.Items = make(map[Item]int) switch item := Item(rand.Intn(4)); item { case GOLD, LADDER, SLEDGE: room.Items[item]++ } return room } // Connects this room to another func (room *Room) Connect(dir Direction, other *Room) { room.Passages[dir] = other other.Passages[dir.Opposite()] = room } // The room name func (room *Room) String() string { if len(room.Name) == 0 { return fmt.Sprintf("Room %d,%d,%d", room.X, room.Y, room.Z) } return room.Name } // Counts the number of items are laying in this room func (room *Room) NumItems() int { total := 0 for _, count := range room.Items { total += count } return total } // Counts the number of exits out of this room func (room *Room) NumExits() int { total := 0 for _, other := range room.Passages { if other != nil { total++ } } return total } // Prints a description of the current room func (room *Room) Describe() { fmt.Printf("You are at %s\n", room) if room.NumItems() > 0 { fmt.Print("On the ground you can see: ") first := true for item := NIL_ITEM; item < NUM_ITEMS; item++ { count := room.Items[item] if count <= 0 { continue } if !first { fmt.Print(", ") } first = false if count == 1 { fmt.Printf("a %s", item) } else { fmt.Printf("%d %ss", count, item) } } fmt.Println() } if room.NumExits() == 0 { fmt.Print("There are no exits.\n") } else { fmt.Print("Exits are:\n") for dir := Direction(0); dir < NUM_DIRECTIONS; dir++ { other := room.Passages[dir] if other != nil { fmt.Printf(" %s: %s\n", dir, other) } } } } type Game struct { // Three level mapping of the rooms Rooms map[int]map[int]map[int]*Room // Number of items of each type Inventory map[Item]int // Currently equipped item Equipped Item // Current room Room *Room // All commands Commands map[string]func(args []string) // All aliases Aliases map[string][]string // Help texts HelpText map[string]string } // Counts the number of items you have in your inventory func (g *Game) NumItems() int { total := 0 for _, count := range g.Inventory { total += count } return total } // Creates or returns the room at specified coordinates. func (g *Game) GetRoomAt(X, Y, Z int) *Room { if g.Rooms == nil { g.Rooms = make(map[int]map[int]map[int]*Room) } if g.Rooms[X] == nil { g.Rooms[X] = make(map[int]map[int]*Room) } if g.Rooms[X][Y] == nil { g.Rooms[X][Y] = make(map[int]*Room) } room := g.Rooms[X][Y][Z] if room == nil { room = GenerateRoom(X, Y, Z) g.Rooms[X][Y][Z] = room } return room } // north, south, east, west, up, down: Moves around func (g *Game) Move(args []string) { if len(args) != 1 { fmt.Print("Invalid number of arguments\n") return } var dir Direction switch strings.ToLower(args[0]) { default: fmt.Printf("Invalid direction: %q\n", args[0]) return case "north": dir = NORTH case "south": dir = SOUTH case "east": dir = EAST case "west": dir = WEST case "up": dir = UP case "down": dir = DOWN } other := g.Room.Passages[dir] if other == nil { fmt.Printf("You can't move %s. There is a wall in the way.\n", dir) } else if dir == UP && g.Room.Items[LADDER] == 0 { fmt.Printf("You can't move %s. There are no %s.\n", dir, LADDER) } else { g.Room = other other.Describe() } } // attack: Breaks a wall. func (g *Game) Sledge(args []string) { if len(args) != 1 { fmt.Print("Invalid number of arguments\n") return } if g.Equipped != SLEDGE { fmt.Printf("You need a %s equipped to break the wall.\n", SLEDGE) return } X, Y, Z := g.Room.X, g.Room.Y, g.Room.Z var dir Direction switch strings.ToLower(args[0]) { default: fmt.Printf("Invalid direction: %q\n", args[0]) return case "north": dir = NORTH Y-- case "south": dir = SOUTH Y++ case "east": dir = EAST X++ case "west": dir = WEST X-- case "up": dir = UP Z++ case "down": dir = DOWN Z-- } other := g.Room.Passages[dir] if other != nil { fmt.Print("There is already a passage there.\n") return } other = g.GetRoomAt(X, Y, Z) g.Room.Connect(dir, other) fmt.Printf("Made a passage %s to %s\n", dir, other) } // drop: Puts items down func (g *Game) DropItem(args []string) { if len(args) != 1 { fmt.Print("Invalid number of arguments\n") return } var item Item switch strings.ToLower(args[0]) { default: fmt.Printf("You don't have any %q.\n", args[0]) return case "all": if g.NumItems() == 0 { fmt.Print("You don't have any items.\n") return } fmt.Print("You drop: ") first := true for item := NIL_ITEM; item < NUM_ITEMS; item++ { count := g.Inventory[item] if count <= 0 { continue } if !first { fmt.Print(", ") } first = false if count == 1 { fmt.Printf("a %s", item) } else { fmt.Printf("%d %ss", count, item) } g.Inventory[item] = 0 g.Room.Items[item] += count } fmt.Println() return case "gold": item = GOLD case "ladder": item = LADDER case "sledge": item = SLEDGE } total := g.Inventory[item] if total == 0 { fmt.Printf("You don't have any %ss.\n", item) return } g.Inventory[item]-- g.Room.Items[item]++ fmt.Printf("You drop a %s.\n", item) } // take: Picks up items func (g *Game) TakeItem(args []string) { if len(args) != 1 { fmt.Print("Invalid number of arguments\n") return } var item Item switch strings.ToLower(args[0]) { default: fmt.Printf("You don't see any %q.\n", args[0]) return case "all": if g.Room.NumItems() == 0 { fmt.Print("You don't see any items.\n") return } fmt.Print("You take: ") first := true for item := NIL_ITEM; item < NUM_ITEMS; item++ { count := g.Room.Items[item] if count <= 0 { continue } if !first { fmt.Print(", ") } first = false if count == 1 { fmt.Printf("a %s", item) } else { fmt.Printf("%d %ss", count, item) } g.Room.Items[item] = 0 g.Inventory[item] += count } fmt.Println() return case "gold": item = GOLD case "ladder": item = LADDER case "sledge": item = SLEDGE } total := g.Room.Items[item] if total == 0 { fmt.Printf("You don't see any %ss.\n", item) return } g.Room.Items[item]-- g.Inventory[item]++ fmt.Printf("You take a %s.\n", item) } // inventory: Shows what you are carrying func (g *Game) ShowInventory(args []string) { if len(args) != 0 { fmt.Print("Invalid number of arguments\n") return } if g.Equipped != NIL_ITEM { fmt.Printf("You have a %s equipped.\n", g.Equipped) } if g.NumItems() == 0 { fmt.Print("You are not carrying anything.\n") return } fmt.Print("You are carrying: ") first := true for item := NIL_ITEM; item < NUM_ITEMS; item++ { count := g.Inventory[item] if count <= 0 { continue } if !first { fmt.Print(", ") } first = false if count == 1 { fmt.Printf("a %s", item) } else { fmt.Printf("%d %ss", count, item) } } fmt.Println() } // name: Renames the current room func (g *Game) RenameRoom(args []string) { if len(args) != 1 { fmt.Print("Invalid number of arguments\n") return } g.Room.Name = args[0] fmt.Printf("This room is now called %q\n", args[0]) g.Room.Describe() } // equip: Equips an item func (g *Game) EquipItem(args []string) { if len(args) != 1 { fmt.Print("Invalid number of arguments\n") return } var item Item switch strings.ToLower(args[0]) { default: fmt.Printf("You don't have any %q.\n", args[0]) return case "gold": item = GOLD case "ladder": item = LADDER case "sledge": item = SLEDGE } count := g.Inventory[item] if count <= 0 { fmt.Printf("You don't have any %ss.\n", item) return } else if item == g.Equipped { fmt.Printf("You already have a %s equipped.\n", item) return } if g.Equipped != NIL_ITEM { fmt.Printf("You unequip the %s. ", g.Equipped) g.Inventory[g.Equipped]++ } fmt.Printf("You equip a %s.\n", item) g.Inventory[item]-- g.Equipped = item } // alias: Creats an alias func (g *Game) CreateAlias(args []string) { if len(args) < 1 { fmt.Print("Invalid number of arguments\n") return } name := strings.ToLower(args[0]) switch name { case "north", "south", "east", "west", "up", "down", "attack", "drop", "take", "inventory", "name", "equip", "alias": fmt.Printf("Can't overwrite the %q command.\n", name) return } if len(args) == 1 { g.Commands[name] = nil fmt.Printf("Removed the %q alias.\n", name) return } g.Aliases[name] = args[1:] fmt.Printf("Created the %q alias.\n", name) } // help: Shows information about commands func (g *Game) Help(args []string) { if len(args) == 0 { fmt.Print("Commands:\n") for _, cmd := range g.GetCommands() { fmt.Printf(" %s\n", cmd) } } else if len(args) == 1 { name := args[0] arr, ok := g.Aliases[name] if ok { fmt.Printf("Alias for: %q\n", strings.Join(arr, " ")) return } _, ok = g.Commands[name] if ok { fmt.Printf("Command: %s\n", name) descr, ok := g.HelpText[name] if ok { fmt.Printf(" %s\n", descr) } return } fmt.Printf("Unknown command: %q\n", name) } else if len(args) > 1 { fmt.Print("Invalid number of arguments\n") } } // look: Looks around func (g *Game) Look(args []string) { if len(args) != 0 { fmt.Print("Invalid number of arguments\n") return } g.Room.Describe() } // quit: Ends the game. The End. Final. func (g *Game) Quit(args []string) { if len(args) != 0 { fmt.Print("Invalid number of arguments\n") return } os.Exit(0) } // Resolves aliases and dispatches to the correct handler func (g *Game) Dispatch(args []string) { seen := make(map[string]bool) name := args[0] for { seen[name] = true prefix, ok := g.Aliases[name] if !ok { break } args = append(prefix, args[1:]...) name = args[0] if seen[name] { fmt.Print("Recursive alias.\n") return } } cb := g.Commands[name] if cb == nil { fmt.Printf("Invalid command: %q\n", name) return } cb(args[1:]) return } // Set everything up func (g *Game) Initialize() *Game { startRoom := g.GetRoomAt(0, 0, 0) startRoom.Name = "Start room" startRoom.Items[SLEDGE] = 1 goalRoom := g.GetRoomAt(1, 1, 5) goalRoom.Name = "Prize room" g.Room = startRoom g.Inventory = make(map[Item]int) g.Commands = map[string]func(args []string){ "move": func(args []string) { g.Move(args) }, "attack": func(args []string) { g.Sledge(args) }, "drop": func(args []string) { g.DropItem(args) }, "take": func(args []string) { g.TakeItem(args) }, "inventory": func(args []string) { g.ShowInventory(args) }, "name": func(args []string) { g.RenameRoom(args) }, "equip": func(args []string) { g.EquipItem(args) }, "alias": func(args []string) { g.CreateAlias(args) }, "quit": func(args []string) { g.Quit(args) }, "look": func(args []string) { g.Look(args) }, "help": func(args []string) { g.Help(args) }, } g.Aliases = map[string][]string{ "north": []string{"move", "north"}, "south": []string{"move", "south"}, "east": []string{"move", "east"}, "west": []string{"move", "west"}, "up": []string{"move", "up"}, "down": []string{"move", "down"}, "n": []string{"north"}, "s": []string{"south"}, "e": []string{"east"}, "w": []string{"west"}, "u": []string{"up"}, "d": []string{"down"}, "i": []string{"inventory"}, "inv": []string{"inventory"}, "a": []string{"attack"}, } g.HelpText = map[string]string{ "move": "Moves you through passages.", "attack": "Attacks a wall with the equipped Sledge hammer.", "drop": "Drops items.", "take": "Picks up items.", "inventory": "Shows your inventory.", "name": "Rename a room.", "equip": "Equip an item.", "alias": "Creates an alias.", "quit": "Exits the game.", "look": "Describes the room.", "help": "Help about commands.", } return g } // Read a single line of input func readLine(in io.Reader) (string, bool) { var line []string buf := []byte{0} _, err := in.Read(buf) for err == nil && buf[0] != '\n' && buf[0] != '\r' { line = append(line, string(buf)) _, err = in.Read(buf) } if buf[0] == '\r' { in.Read(buf) } return strings.Join(line, ""), err == nil } // Returns a sorted array of command and alias names func (g *Game) GetCommands() []string { var commandNames []string for name := range g.Commands { commandNames = append(commandNames, name) } for name := range g.Aliases { commandNames = append(commandNames, name) } sort.Strings(commandNames) return commandNames } // Main game loop func (g *Game) Run() { fmt.Printf("Welcome to RC Minimalist RPG, Go version %d.%d.\n", VERSION_MAJOR, VERSION_MINOR) fmt.Print("You start in room 0,0,0. Your goal is at 1,1,5. Good luck!\n") fmt.Printf("Commands: %s\n", strings.Join(g.GetCommands(), ", ")) g.Room.Describe() fmt.Print("\n> ") line, ok := readLine(os.Stdin) for ok && len(line) == 0 { fmt.Print("\n> ") line, ok = readLine(os.Stdin) } for ok { tokens := strings.Split(line, " ") g.Dispatch(tokens) fmt.Print("\n> ") line, ok = readLine(os.Stdin) for ok && len(line) == 0 { fmt.Print("\n> ") line, ok = readLine(os.Stdin) } } } func main() { g := new(Game).Initialize() g.Run() }