RCRPG/Go: Difference between revisions

From Rosetta Code
Content added Content deleted
(Created)
 
m (Fixed syntax highlighting.)
 
(5 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{collection|RCRPG}}
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
"fmt"
"container/vector"
"fmt"
"io"
"math/rand"
"io"
"os"
"os"
"rand"
"sort"
"strings"
"sort"
"strings"
)
)


const (
const (
VERSION_MAJOR = 1
VERSION_MAJOR = 1
VERSION_MINOR = 0
VERSION_MINOR = 0
)
)


Line 19: Line 19:


const (
const (
NORTH = Direction(iota)
NORTH = Direction(iota)
SOUTH
SOUTH
EAST
EAST
WEST
WEST
UP
UP
DOWN
DOWN
NUM_DIRECTIONS = iota
NUM_DIRECTIONS = iota
)
)


func (d Direction) Opposite() Direction {
func (d Direction) Opposite() Direction {
return d ^ 1
return d ^ 1
}
}


func (d Direction) String() string {
func (d Direction) String() string {
switch d {
switch d {
case NORTH: return "north"
case NORTH:
case SOUTH: return "south"
return "north"
case EAST: return "east"
case SOUTH:
case WEST: return "west"
return "south"
case UP: return "up"
case EAST:
case DOWN: return "down"
return "east"
case WEST:
}
return "west"
panic("Invalid direction!")
case UP:
return "up"
case DOWN:
return "down"
}
panic("Invalid direction!")
}
}


Line 47: Line 53:


const (
const (
NIL_ITEM = Item(iota)
NIL_ITEM = Item(iota)
GOLD
GOLD
LADDER
LADDER
SLEDGE
SLEDGE
NUM_ITEMS = iota
NUM_ITEMS = iota
)
)


func (item Item) String() string {
func (item Item) String() string {
switch item {
switch item {
case GOLD: return "Gold coin"
case GOLD:
case LADDER: return "Ladder"
return "Gold coin"
case LADDER:
case SLEDGE: return "Sledge hammer"
return "Ladder"
}
case SLEDGE:
panic("Invalid item!")
return "Sledge hammer"
}
panic("Invalid item!")
}
}


type Room struct {
type Room struct {
X,Y,Z int
X, Y, Z int
Name string
Name string
Passages [6]*Room
Passages [6]*Room
Items map[Item] int
Items map[Item]int
}
}


// Creates a new room
// Creates a new room
func GenerateRoom(X,Y,Z int) *Room {
func GenerateRoom(X, Y, Z int) *Room {
room := new(Room)
room := new(Room)
room.X,room.Y,room.Z = X,Y,Z
room.X, room.Y, room.Z = X, Y, Z
room.Name = ""
room.Name = ""
room.Items = make(map[Item] int)
room.Items = make(map[Item]int)
switch item := Item(rand.Intn(4)); item {
switch item := Item(rand.Intn(4)); item {
case GOLD, LADDER, SLEDGE:
case GOLD, LADDER, SLEDGE:
room.Items[item]++
room.Items[item]++
}
}
return room
return room
}
}


// Connects this room to another
// Connects this room to another
func (room *Room) Connect(dir Direction, other *Room) {
func (room *Room) Connect(dir Direction, other *Room) {
room.Passages[dir] = other
room.Passages[dir] = other
other.Passages[dir.Opposite()] = room
other.Passages[dir.Opposite()] = room
}
}


// The room name
// The room name
func (room *Room) String() string {
func (room *Room) String() string {
if len(room.Name) == 0 {
if len(room.Name) == 0 {
return fmt.Sprintf("Room %d,%d,%d", room.X, room.Y, room.Z)
return fmt.Sprintf("Room %d,%d,%d", room.X, room.Y, room.Z)
}
}
return room.Name
return room.Name
}
}


// Counts the number of items are laying in this room
// Counts the number of items are laying in this room
func (room *Room) NumItems() int {
func (room *Room) NumItems() int {
total := 0
total := 0
for _, count := range room.Items {
for _, count := range room.Items {
total += count
total += count
}
}
return total
return total
}
}


// Counts the number of exits out of this room
// Counts the number of exits out of this room
func (room *Room) NumExits() int {
func (room *Room) NumExits() int {
total := 0
total := 0
for _, other := range room.Passages {
for _, other := range room.Passages {
if other != nil {
if other != nil {
total++
total++
}
}
}
}
return total
return total
}
}


// Prints a description of the current room
// Prints a description of the current room
func (room *Room) Describe() {
func (room *Room) Describe() {
fmt.Printf("You are at %s\n", room)
fmt.Printf("You are at %s\n", room)
if room.NumItems() > 0 {
if room.NumItems() > 0 {
fmt.Print("On the ground you can see: ")
fmt.Print("On the ground you can see: ")
first := true
first := true
for item := NIL_ITEM; item < NUM_ITEMS; item++ {
for item := NIL_ITEM; item < NUM_ITEMS; item++ {
count := room.Items[item]
count := room.Items[item]
if count <= 0 { continue }
if count <= 0 {
continue
if !first { fmt.Print(", ") }
}
first = false
if count == 1 {
if !first {
fmt.Printf("a %s", item)
fmt.Print(", ")
}
} else {
first = false
fmt.Printf("%d %ss", count, item)
if count == 1 {
}
fmt.Printf("a %s", item)
}
} else {
fmt.Println()
fmt.Printf("%d %ss", count, item)
}
}
if room.NumExits() == 0 {
}
fmt.Print("There are no exits.\n")
fmt.Println()
} else {
}
fmt.Print("Exits are:\n")
if room.NumExits() == 0 {
for dir := Direction(0); dir < NUM_DIRECTIONS; dir++ {
fmt.Print("There are no exits.\n")
other := room.Passages[dir]
} else {
if other != nil {
fmt.Printf(" %s: %s\n", dir, other)
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 {
type Game struct {
// Three level mapping of the rooms
// Three level mapping of the rooms
Rooms map[int] map[int] map[int] *Room
Rooms map[int]map[int]map[int]*Room
// Number of items of each type
// Number of items of each type
Inventory map[Item] int
Inventory map[Item]int
// Currently equipped item
// Currently equipped item
Equipped Item
Equipped Item
// Current room
// Current room
Room *Room
Room *Room
// All commands
// All commands
Commands map[string] func(args []string)
Commands map[string]func(args []string)
// All aliases
// All aliases
Aliases map[string] []string
Aliases map[string][]string
// Help texts
// Help texts
HelpText map[string] string
HelpText map[string]string
}
}


// Counts the number of items you have in your inventory
// Counts the number of items you have in your inventory
func (g *Game) NumItems() int {
func (g *Game) NumItems() int {
total := 0
total := 0
for _, count := range g.Inventory {
for _, count := range g.Inventory {
total += count
total += count
}
}
return total
return total
}
}


// Creates or returns the room at specified coordinates.
// Creates or returns the room at specified coordinates.
func (g *Game) GetRoomAt(X,Y,Z int) *Room {
func (g *Game) GetRoomAt(X, Y, Z int) *Room {
if g.Rooms == nil {
if g.Rooms == nil {
g.Rooms = make(map[int] map[int] map[int] *Room)
g.Rooms = make(map[int]map[int]map[int]*Room)
}
}
if g.Rooms[X] == nil {
if g.Rooms[X] == nil {
g.Rooms[X] = make(map[int] map[int] *Room)
g.Rooms[X] = make(map[int]map[int]*Room)
}
}
if g.Rooms[X][Y] == nil {
if g.Rooms[X][Y] == nil {
g.Rooms[X][Y] = make(map[int] *Room)
g.Rooms[X][Y] = make(map[int]*Room)
}
}
room := g.Rooms[X][Y][Z]
room := g.Rooms[X][Y][Z]
if room == nil {
if room == nil {
room = GenerateRoom(X,Y,Z)
room = GenerateRoom(X, Y, Z)
g.Rooms[X][Y][Z] = room
g.Rooms[X][Y][Z] = room
}
}
return room
return room
}
}


// north, south, east, west, up, down: Moves around
// north, south, east, west, up, down: Moves around
func (g *Game) Move(args []string) {
func (g *Game) Move(args []string) {
if len(args) != 1 {
if len(args) != 1 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
return
return
}
}
var dir Direction
var dir Direction
switch strings.ToLower(args[0]) {
switch strings.ToLower(args[0]) {
default:
default:
fmt.Printf("Invalid direction: %q\n", args[0])
fmt.Printf("Invalid direction: %q\n", args[0])
return
return
case "north": dir = NORTH
case "north":
case "south": dir = SOUTH
dir = NORTH
case "east": dir = EAST
case "south":
case "west": dir = WEST
dir = SOUTH
case "up": dir = UP
case "east":
case "down": dir = DOWN
dir = EAST
case "west":
}
dir = WEST
other := g.Room.Passages[dir]
case "up":
if other == nil {
dir = UP
fmt.Printf("You can't move %s. There is a wall in the way.\n", dir)
case "down":
} else if dir == UP && g.Room.Items[LADDER] == 0 {
dir = DOWN
fmt.Printf("You can't move %s. There are no %s.\n", dir, LADDER)
}
} else {
g.Room = other
other := g.Room.Passages[dir]
if other == nil {
other.Describe()
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.
// attack: Breaks a wall.
func (g *Game) Sledge(args []string) {
func (g *Game) Sledge(args []string) {
if len(args) != 1 {
if len(args) != 1 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
return
return
}
}
if g.Equipped != SLEDGE {
if g.Equipped != SLEDGE {
fmt.Printf("You need a %s equipped to break the wall.\n", SLEDGE)
fmt.Printf("You need a %s equipped to break the wall.\n", SLEDGE)
return
return
}
}
X,Y,Z := g.Room.X,g.Room.Y,g.Room.Z
X, Y, Z := g.Room.X, g.Room.Y, g.Room.Z
var dir Direction
var dir Direction
switch strings.ToLower(args[0]) {
switch strings.ToLower(args[0]) {
default:
default:
fmt.Printf("Invalid direction: %q\n", args[0])
fmt.Printf("Invalid direction: %q\n", args[0])
return
return
case "north": dir = NORTH; Y--
case "north":
dir = NORTH
case "south": dir = SOUTH; Y++
Y--
case "east": dir = EAST; X++
case "west": dir = WEST; X--
case "south":
case "up": dir = UP; Z++
dir = SOUTH
Y++
case "down": dir = DOWN; Z--
case "east":
}
dir = EAST
other := g.Room.Passages[dir]
X++
if other != nil {
case "west":
fmt.Print("There is already a passage there.\n")
dir = WEST
return
X--
}
case "up":
other = g.GetRoomAt(X,Y,Z)
dir = UP
g.Room.Connect(dir,other)
Z++
fmt.Printf("Made a passage %s to %s\n", dir, other)
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
// drop: Puts items down
func (g *Game) DropItem(args []string) {
func (g *Game) DropItem(args []string) {
if len(args) != 1 {
if len(args) != 1 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
return
return
}
}
var item Item
var item Item
switch strings.ToLower(args[0]) {
switch strings.ToLower(args[0]) {
default:
default:
fmt.Printf("You don't have any %q.\n", args[0])
fmt.Printf("You don't have any %q.\n", args[0])
return
return
case "all":
case "all":
if g.NumItems() == 0 {
if g.NumItems() == 0 {
fmt.Print("You don't have any items.\n")
fmt.Print("You don't have any items.\n")
return
return
}
}
fmt.Print("You drop: ")
fmt.Print("You drop: ")
first := true
first := true
for item := NIL_ITEM; item < NUM_ITEMS; item++ {
for item := NIL_ITEM; item < NUM_ITEMS; item++ {
count := g.Inventory[item]
count := g.Inventory[item]
if count <= 0 { continue }
if count <= 0 {
continue
if !first { fmt.Print(", ") }
}
first = false
if count == 1 {
if !first {
fmt.Printf("a %s", item)
fmt.Print(", ")
}
} else {
first = false
fmt.Printf("%d %ss", count, item)
if count == 1 {
}
fmt.Printf("a %s", item)
g.Inventory[item] = 0
} else {
g.Room.Items[item] += count
fmt.Printf("%d %ss", count, item)
}
}
fmt.Println()
g.Inventory[item] = 0
return
case "gold": item = GOLD
g.Room.Items[item] += count
}
case "ladder": item = LADDER
fmt.Println()
case "sledge": item = SLEDGE
return
}
case "gold":
total := g.Inventory[item]
item = GOLD
if total == 0 {
case "ladder":
fmt.Printf("You don't have any %ss.\n", item)
item = LADDER
return
case "sledge":
}
item = SLEDGE
g.Inventory[item]--
}
g.Room.Items[item]++
total := g.Inventory[item]
fmt.Printf("You drop a %s.\n", 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
// take: Picks up items
func (g *Game) TakeItem(args []string) {
func (g *Game) TakeItem(args []string) {
if len(args) != 1 {
if len(args) != 1 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
return
return
}
}
var item Item
var item Item
switch strings.ToLower(args[0]) {
switch strings.ToLower(args[0]) {
default:
default:
fmt.Printf("You don't see any %q.\n", args[0])
fmt.Printf("You don't see any %q.\n", args[0])
return
return
case "all":
case "all":
if g.Room.NumItems() == 0 {
if g.Room.NumItems() == 0 {
fmt.Print("You don't see any items.\n")
fmt.Print("You don't see any items.\n")
return
return
}
}
fmt.Print("You take: ")
fmt.Print("You take: ")
first := true
first := true
for item := NIL_ITEM; item < NUM_ITEMS; item++ {
for item := NIL_ITEM; item < NUM_ITEMS; item++ {
count := g.Room.Items[item]
count := g.Room.Items[item]
if count <= 0 { continue }
if count <= 0 {
continue
if !first { fmt.Print(", ") }
}
first = false
if count == 1 {
if !first {
fmt.Printf("a %s", item)
fmt.Print(", ")
}
} else {
first = false
fmt.Printf("%d %ss", count, item)
if count == 1 {
}
fmt.Printf("a %s", item)
g.Room.Items[item] = 0
} else {
g.Inventory[item] += count
fmt.Printf("%d %ss", count, item)
}
}
fmt.Println()
g.Room.Items[item] = 0
return
case "gold": item = GOLD
g.Inventory[item] += count
}
case "ladder": item = LADDER
fmt.Println()
case "sledge": item = SLEDGE
return
}
case "gold":
total := g.Room.Items[item]
item = GOLD
if total == 0 {
case "ladder":
fmt.Printf("You don't see any %ss.\n", item)
item = LADDER
return
case "sledge":
}
item = SLEDGE
g.Room.Items[item]--
}
g.Inventory[item]++
total := g.Room.Items[item]
fmt.Printf("You take a %s.\n", 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
// inventory: Shows what you are carrying
func (g *Game) ShowInventory(args []string) {
func (g *Game) ShowInventory(args []string) {
if len(args) != 0 {
if len(args) != 0 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
return
return
}
}
if g.Equipped != NIL_ITEM {
if g.Equipped != NIL_ITEM {
fmt.Printf("You have a %s equipped.\n", g.Equipped)
fmt.Printf("You have a %s equipped.\n", g.Equipped)
}
}
if g.NumItems() == 0 {
if g.NumItems() == 0 {
fmt.Print("You are not carrying anything.\n")
fmt.Print("You are not carrying anything.\n")
return
return
}
}
fmt.Print("You are carrying: ")
fmt.Print("You are carrying: ")
first := true
first := true
for item := NIL_ITEM; item < NUM_ITEMS; item++ {
for item := NIL_ITEM; item < NUM_ITEMS; item++ {
count := g.Inventory[item]
count := g.Inventory[item]
if count <= 0 { continue }
if count <= 0 {
continue
if !first { fmt.Print(", ") }
}
first = false
if count == 1 {
if !first {
fmt.Printf("a %s", item)
fmt.Print(", ")
}
} else {
first = false
fmt.Printf("%d %ss", count, item)
}
if count == 1 {
fmt.Printf("a %s", item)
}
} else {
fmt.Println()
fmt.Printf("%d %ss", count, item)
}
}
fmt.Println()
}
}


// name: Renames the current room
// name: Renames the current room
func (g *Game) RenameRoom(args []string) {
func (g *Game) RenameRoom(args []string) {
if len(args) != 1 {
if len(args) != 1 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
return
return
}
}
g.Room.Name = args[0]
g.Room.Name = args[0]
fmt.Printf("This room is now called %q\n", args[0])
fmt.Printf("This room is now called %q\n", args[0])
g.Room.Describe()
g.Room.Describe()
}
}


// equip: Equips an item
// equip: Equips an item
func (g *Game) EquipItem(args []string) {
func (g *Game) EquipItem(args []string) {
if len(args) != 1 {
if len(args) != 1 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
return
return
}
}
var item Item
var item Item
switch strings.ToLower(args[0]) {
switch strings.ToLower(args[0]) {
default:
default:
fmt.Printf("You don't have any %q.\n", args[0])
fmt.Printf("You don't have any %q.\n", args[0])
return
return
case "gold": item = GOLD
case "gold":
case "ladder": item = LADDER
item = GOLD
case "sledge": item = SLEDGE
case "ladder":
item = LADDER
}
case "sledge":
count := g.Inventory[item]
item = SLEDGE
if count <= 0 {
}
fmt.Printf("You don't have any %ss.\n", item)
count := g.Inventory[item]
return
} else if item == g.Equipped {
if count <= 0 {
fmt.Printf("You already have a %s equipped.\n", item)
fmt.Printf("You don't have any %ss.\n", item)
return
return
} else if item == g.Equipped {
}
fmt.Printf("You already have a %s equipped.\n", item)
if g.Equipped != NIL_ITEM {
return
fmt.Printf("You unequip the %s. ", g.Equipped)
}
g.Inventory[g.Equipped]++
if g.Equipped != NIL_ITEM {
}
fmt.Printf("You equip a %s.\n", item)
fmt.Printf("You unequip the %s. ", g.Equipped)
g.Inventory[item]--
g.Inventory[g.Equipped]++
}
g.Equipped = item
fmt.Printf("You equip a %s.\n", item)
g.Inventory[item]--
g.Equipped = item
}
}


// alias: Creats an alias
// alias: Creats an alias
func (g *Game) CreateAlias(args []string) {
func (g *Game) CreateAlias(args []string) {
if len(args) < 1 {
if len(args) < 1 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
return
return
}
}
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
}
}
if len(args) == 1 {
if len(args) == 1 {
g.Commands[name] = nil
g.Commands[name] = nil
fmt.Printf("Removed the %q alias.\n", name)
fmt.Printf("Removed the %q alias.\n", name)
return
return
}
}
g.Aliases[name] = args[1:]
g.Aliases[name] = args[1:]
fmt.Printf("Created the %q alias.\n", name)
fmt.Printf("Created the %q alias.\n", name)
}
}


// help: Shows information about commands
// help: Shows information about commands
func (g *Game) Help(args []string) {
func (g *Game) Help(args []string) {
if len(args) == 0 {
if len(args) == 0 {
fmt.Print("Commands:\n")
fmt.Print("Commands:\n")
for _, cmd := range g.GetCommands() {
for _, cmd := range g.GetCommands() {
fmt.Printf(" %s\n", cmd)
fmt.Printf(" %s\n", cmd)
}
}
} else if len(args) == 1 {
} else if len(args) == 1 {
name := args[0]
name := args[0]
arr, ok := g.Aliases[name]
arr, ok := g.Aliases[name]
if ok {
if ok {
fmt.Printf("Alias for: %q\n", strings.Join(arr," "))
fmt.Printf("Alias for: %q\n", strings.Join(arr, " "))
return
return
}
}
_, ok = g.Commands[name]
_, ok = g.Commands[name]
if ok {
if ok {
fmt.Printf("Command: %s\n", name)
fmt.Printf("Command: %s\n", name)
descr, ok := g.HelpText[name]
descr, ok := g.HelpText[name]
if ok {
if ok {
fmt.Printf(" %s\n", descr)
fmt.Printf(" %s\n", descr)
}
}
return
return
}
}
fmt.Printf("Unknown command: %q\n", name)
fmt.Printf("Unknown command: %q\n", name)
} else if len(args) > 1 {
} else if len(args) > 1 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
}
}
}
}


// look: Looks around
// look: Looks around
func (g *Game) Look(args []string) {
func (g *Game) Look(args []string) {
if len(args) != 0 {
if len(args) != 0 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
return
return
}
}
g.Room.Describe()
g.Room.Describe()
}
}


// quit: Ends the game. The End. Final.
// quit: Ends the game. The End. Final.
func (g *Game) Quit(args []string) {
func (g *Game) Quit(args []string) {
if len(args) != 0 {
if len(args) != 0 {
fmt.Print("Invalid number of arguments\n")
fmt.Print("Invalid number of arguments\n")
return
return
}
}
os.Exit(0)
os.Exit(0)
}
}


// 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) {
seen := make(map[string]bool)
argv := vector.StringVector(args)
name := args[0]
seen := make(map[string]bool)
for {
name := argv.At(0)
seen[name] = true
seen[name] = true
prefix, ok := g.Aliases[name]
prefix, ok := g.Aliases[name]
for ok {
if !ok {
break
argv.Cut(0,1)
}
prefixv := vector.StringVector(prefix)
args = append(prefix, args[1:]...)
argv.InsertVector(0,&prefixv)
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]
if cb == nil {
}
fmt.Printf("Invalid command: %q\n", name)
cb := g.Commands[name]
return
if cb == nil {
}
fmt.Printf("Invalid command: %q\n", name)
cb(args[1:])
return
return
}
cb(argv.Data()[1:])
return
}
}


// Set everything up
// Set everything up
func (g *Game) Initialize() *Game {
func (g *Game) Initialize() *Game {
startRoom := g.GetRoomAt(0,0,0)
startRoom := g.GetRoomAt(0, 0, 0)
startRoom.Name = "Start room"
startRoom.Name = "Start room"
startRoom.Items[SLEDGE] = 1
startRoom.Items[SLEDGE] = 1
goalRoom := g.GetRoomAt(1,1,5)
goalRoom := g.GetRoomAt(1, 1, 5)
goalRoom.Name = "Prize room"
goalRoom.Name = "Prize room"
g.Room = startRoom
g.Room = startRoom
g.Inventory = make(map[Item] int)
g.Inventory = make(map[Item]int)
g.Commands = map[string] func(args []string) {
g.Commands = map[string]func(args []string){
"move": func(args []string) { g.Move(args) },
"move": func(args []string) { g.Move(args) },
"attack": func(args []string) { g.Sledge(args) },
"attack": func(args []string) { g.Sledge(args) },
"drop": func(args []string) { g.DropItem(args) },
"drop": func(args []string) { g.DropItem(args) },
"take": func(args []string) { g.TakeItem(args) },
"take": func(args []string) { g.TakeItem(args) },
"inventory": func(args []string) { g.ShowInventory(args) },
"inventory": func(args []string) { g.ShowInventory(args) },
"name": func(args []string) { g.RenameRoom(args) },
"name": func(args []string) { g.RenameRoom(args) },
"equip": func(args []string) { g.EquipItem(args) },
"equip": func(args []string) { g.EquipItem(args) },
"alias": func(args []string) { g.CreateAlias(args) },
"alias": func(args []string) { g.CreateAlias(args) },
"quit": func(args []string) { g.Quit(args) },
"quit": func(args []string) { g.Quit(args) },
"look": func(args []string) { g.Look(args) },
"look": func(args []string) { g.Look(args) },
"help": func(args []string) { g.Help(args) },
"help": func(args []string) { g.Help(args) },
}
}
g.Aliases = map[string] []string {
g.Aliases = map[string][]string{
"north": []string{"move","north"},
"north": []string{"move", "north"},
"south": []string{"move","south"},
"south": []string{"move", "south"},
"east": []string{"move","east"},
"east": []string{"move", "east"},
"west": []string{"move","west"},
"west": []string{"move", "west"},
"up": []string{"move","up"},
"up": []string{"move", "up"},
"down": []string{"move","down"},
"down": []string{"move", "down"},
"n": []string{"north"},
"n": []string{"north"},
"s": []string{"south"},
"s": []string{"south"},
"e": []string{"east"},
"e": []string{"east"},
"w": []string{"west"},
"w": []string{"west"},
"u": []string{"up"},
"u": []string{"up"},
"d": []string{"down"},
"d": []string{"down"},
"i": []string{"inventory"},
"i": []string{"inventory"},
"inv": []string{"inventory"},
"inv": []string{"inventory"},
"a": []string{"attack"},
"a": []string{"attack"},
}
}
g.HelpText = map[string] string {
g.HelpText = map[string]string{
"move": "Moves you trough passages.",
"move": "Moves you through passages.",
"attack": "Attacks a wall with the equipped Sledge hammer.",
"attack": "Attacks a wall with the equipped Sledge hammer.",
"drop": "Drops items.",
"drop": "Drops items.",
"take": "Picks up items.",
"take": "Picks up items.",
"inventory": "Shows your inventory.",
"inventory": "Shows your inventory.",
"name": "Rename a room.",
"name": "Rename a room.",
"equip": "Equip an item.",
"equip": "Equip an item.",
"alias": "Creates an alias.",
"alias": "Creates an alias.",
"quit": "Exits the game.",
"quit": "Exits the game.",
"look": "Describes the room.",
"look": "Describes the room.",
"help": "Help about commands.",
"help": "Help about commands.",
}
}
return g
return g
}
}


// 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)
}
}
if buf[0] == '\r' {
if buf[0] == '\r' {
in.Read(buf)
in.Read(buf)
}
}
return strings.Join(line.Data(), ""), 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 {
commandNames := new(vector.StringVector)
var commandNames []string
for name := range g.Commands {
for name := range g.Commands {
commandNames = append(commandNames, name)
commandNames.Push(name)
}
}
for name := range g.Aliases {
for name := range g.Aliases {
commandNames = append(commandNames, name)
commandNames.Push(name)
}
}
sort.Sort(commandNames)
sort.Strings(commandNames)
return commandNames.Data()
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(), ", "))
g.Room.Describe()
g.Room.Describe()
fmt.Print("\n> ")
fmt.Print("\n> ")
line, ok := readLine(os.Stdin)
line, ok := readLine(os.Stdin)
for ok && len(line) == 0 {
for ok && len(line) == 0 {
fmt.Print("\n> ")
fmt.Print("\n> ")
line, ok = readLine(os.Stdin)
line, ok = readLine(os.Stdin)
}
}
for ok {
for ok {
tokens := strings.Split(line, " ", 0)
tokens := strings.Split(line, " ")
g.Dispatch(tokens)
g.Dispatch(tokens)
fmt.Print("\n> ")
fmt.Print("\n> ")
line, ok = readLine(os.Stdin)
line, ok = readLine(os.Stdin)
for ok && len(line) == 0 {
for ok && len(line) == 0 {
fmt.Print("\n> ")
fmt.Print("\n> ")
line, ok = readLine(os.Stdin)
line, ok = readLine(os.Stdin)
}
}
}
}
}
}


func main() {
func main() {
g := new(Game).Initialize()
g := new(Game).Initialize()
g.Run()
g.Run()
}</lang>
}</syntaxhighlight>

Latest revision as of 11:56, 30 August 2022

RCRPG/Go is part of RCRPG. You may find other members of RCRPG at Category: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()
}