RCRPG/Go: Difference between revisions

From Rosetta Code
Content added Content deleted
m (Typo)
(library changes)
Line 2: Line 2:


import (
import (
"container/vector"
"fmt"
"fmt"
"io"
"io"
"math/rand"
"os"
"os"
"rand"
"sort"
"sort"
"strings"
"strings"
Line 482: Line 481:
name := strings.ToLower(args[0])
name := strings.ToLower(args[0])
switch name {
switch name {
case "north", "south", "east", "west", "up", "down", "attack", "drop", "take", "inventory", "name", "equip", "alias":
case "north", "south", "east", "west", "up", "down", "attack",
"drop", "take", "inventory", "name", "equip", "alias":
fmt.Printf("Can't overwrite the %q command.\n", name)
fmt.Printf("Can't overwrite the %q command.\n", name)
return
return
Line 544: Line 544:
// Resolves aliases and dispatches to the correct handler
// Resolves aliases and dispatches to the correct handler
func (g *Game) Dispatch(args []string) {
func (g *Game) Dispatch(args []string) {
argv := vector.StringVector(args)
seen := make(map[string]bool)
seen := make(map[string]bool)
name := argv.At(0)
name := args[0]
for {
seen[name] = true
seen[name] = true
prefix, ok := g.Aliases[name]
prefix, ok := g.Aliases[name]
for ok {
if !ok {
argv.Cut(0, 1)
break
prefixv := vector.StringVector(prefix)
}
argv.InsertVector(0, &prefixv)
args = append(prefix, args[1:]...)
name = argv.At(0)
name = args[0]
if seen[name] {
if seen[name] {
fmt.Print("Recursive alias.\n")
fmt.Print("Recursive alias.\n")
return
return
}
}
seen[name] = true
prefix, ok = g.Aliases[name]
}
}
cb := g.Commands[name]
cb := g.Commands[name]
Line 566: Line 564:
return
return
}
}
cb(argv[1:])
cb(args[1:])
return
return
}
}
Line 627: Line 625:
// Read a single line of input
// Read a single line of input
func readLine(in io.Reader) (string, bool) {
func readLine(in io.Reader) (string, bool) {
var line []string
line := new(vector.StringVector)
buf := []byte{0}
buf := []byte{0}
_, err := in.Read(buf)
_, err := in.Read(buf)
for err == nil && buf[0] != '\n' && buf[0] != '\r' {
for err == nil && buf[0] != '\n' && buf[0] != '\r' {
line.Push(string(buf))
line = append(line, string(buf))
_, err = in.Read(buf)
_, err = in.Read(buf)
}
}
Line 637: Line 635:
in.Read(buf)
in.Read(buf)
}
}
return strings.Join(line.Copy(), ""), err == nil
return strings.Join(line, ""), err == nil
}
}


// Returns a sorted array of command and alias names
// Returns a sorted array of command and alias names
func (g *Game) GetCommands() []string {
func (g *Game) GetCommands() []string {
var commandNames []string
commandNames := new(vector.StringVector)
for name := range g.Commands {
for name := range g.Commands {
commandNames.Push(name)
commandNames = append(commandNames, name)
}
}
for name := range g.Aliases {
for name := range g.Aliases {
commandNames.Push(name)
commandNames = append(commandNames, name)
}
}
sort.Sort(commandNames)
sort.Strings(commandNames)
return commandNames.Copy()
return commandNames
}
}


// Main game loop
// Main game loop
func (g *Game) Run() {
func (g *Game) Run() {
fmt.Printf("Welcome to RC Minimalist RPG, Go version %d.%d.\n", VERSION_MAJOR, VERSION_MINOR)
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.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(), ", "))
fmt.Printf("Commands: %s\n", strings.Join(g.GetCommands(), ", "))
Line 666: Line 665:
}
}
for ok {
for ok {
tokens := strings.Split(line, " ", -1)
tokens := strings.Split(line, " ")
g.Dispatch(tokens)
g.Dispatch(tokens)
fmt.Print("\n> ")
fmt.Print("\n> ")

Revision as of 22:48, 5 December 2011

<lang go>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() }</lang>