RCRPG/Nim
This Nim version proposes the following commands:
– alias <alias name> <command>: define an alias.
– aliases: list aliases.
– attack <direction>: use sledge to dig a tunnel in the given direction.
– drop all | <item>: drop all items or a specified item.
– east: move to East.
– equip <item>: equip an item present in inventory.
– help: display the list of commands and directions.
– inventory: list items in the inventory.
– look: display location, items and exits.
– name <name>: give a name to the current room.
– north: move to North.
– south: move to South.
– quit: quit game.
– take all | <item>: take all items or a specified item.
– unequip: unequip the equipped item.
– west: move to West.
Directions are: north, east, south, west, up, down.
Code
import random
import strformat
import strutils
import tables
type
Direction = enum nowhere, north, east, south, west, up, down
Item = enum none, sledge, ladder, gold, all
Position = tuple[x, y, z: int]
Delta = Position
Room = ref object
game: Game
pos: Position
items: seq[Item]
passages: array[Direction, bool]
Game = ref object
pos: Position
rooms: Table[Position, Room]
roomNames: Table[Position, string]
aliases: Table[string, string]
inventory: seq[Item]
equipped: Item
const
IncPos: array[Direction, Delta] = [(0, 0, 0), (0, -1, 0), (1, 0, 0),
(0, 1, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1)]
OppositeDir: array[Direction, Direction] = [nowhere, south, west, north, east, down, up]
# Command names and number of tokens required for this command.
Commands = {"alias": 3, "aliases": 1, "quit": 1, "name": 2, "look": 1,
"help": 1, "north": 1, "east": 1, "south": 1, "west": 1,
"up": 1, "down": 1, "attack": 2, "inventory": 1,
"take": 2, "drop": 2, "equip": 2, "unequip": 1}.toTable()
StartRoomPos: Position = (0, 0, 0)
PrizeRoomPos: Position = (1, 1, 5)
####################################################################################################
# Miscellaneous.
func nextPos(oldPos: Position; direction: Direction): Position =
## Compute new coordinates from current position and direction.
(oldPos.x + IncPos[direction].x,
oldPos.y + IncPos[direction].y,
oldPos.z + IncPos[direction].z)
#___________________________________________________________________________________________________
proc randomItems(): seq[Item] =
## Select randomly the items for a room.
## At most one sledge, one ladder and one gold.
for item in [sledge, ladder, gold]:
if rand(1) != 0:
result.add(item)
#___________________________________________________________________________________________________
template toItem(token: string): Item =
## Return item from token.
parseEnum[Item](token.toLower, none)
#___________________________________________________________________________________________________
template toDirection(token: string): Direction =
## Return direction from token.
parseEnum[Direction](token.toLower, nowhere)
#___________________________________________________________________________________________________
proc displayHelp() =
## Display help about commands.
echo "Commands:"
echo "– alias <alias name> <command>: define an alias."
echo "– aliases: list aliases."
echo "– attack <direction>: use sledge to dig a tunnel in the given direction."
echo "– drop all | <item>: drop all items or a specified item."
echo "– east: move to East."
echo "– equip <item>: equip an item present in inventory."
echo "– help: display this help."
echo "– inventory: list items in the inventory."
echo "– look: display location, items and exits."
echo "– name <name>: give a name to the current room."
echo "– north: move to North."
echo "– south: move to South."
echo "– quit: quit game."
echo "– take all | <item>: take all items or a specified item."
echo "– unequip: unequip the equipped item."
echo "– west: move to West."
echo "\nDirections:"
echo "– north, east, south, west, up, down"
####################################################################################################
# Room.
func description(room: Room): string =
## Return the description of a room.
result = "You are at "
if room.pos in room.game.roomNames:
result.add(room.game.roomNames[room.pos])
else:
result.add(fmt"{room.pos.x}, {room.pos.y}, {room.pos.z}")
if room.items.len > 0:
result.add("\nOn the ground you can see: " & room.items.join(", "))
result.add("\nExits are: ")
var exits: seq[string]
for dir, passage in room.passages:
if passage:
exits.add(($dir).capitalizeAscii())
result.add(if exits.len > 0: exits.join(", ") else: "None")
#___________________________________________________________________________________________________
proc take(room: Room; item: Item): seq[Item] =
## Take an item in a room.
if item == all:
result = room.items
room.items.setLen(0)
echo "You now have everything in the room."
else:
let idx = room.items.find(item)
if idx >= 0:
room.items.delete(idx)
result = @[item]
echo "Taken ", item, "."
else:
echo "Item not found."
####################################################################################################
# Game.
proc newGame(): Game =
## Initialize a game.
new(result)
# Create rooms.
result.rooms[StartRoomPos] = Room(game: result, pos: StartRoomPos, items: @[sledge])
result.rooms[PrizeRoomPos] = Room(game: result, pos: PrizeRoomPos,
items: @[gold, gold, gold, gold, gold])
# Create room names.
result.roomNames = {StartRoomPos: "the starting room",
PrizeRoomPos: "the prize room"}.toTable()
# Create aliases.
result.aliases = {"n": "north", "e": "east", "s": "south", "w": "west", "u": "up",
"d": "down", "a": "attack", "i": "inventory", "l": "look"}.toTable()
#___________________________________________________________________________________________________
proc createAlias(game: Game; newAlias, command: string) =
## Create an alias for a command.
if command in game.aliases:
echo "You cannot alias an alias."
return
game.aliases[newAlias] = command
echo "Alias created."
#___________________________________________________________________________________________________
proc listAliases(game: Game) =
## List the defined aliases.
for alias, command in game.aliases.pairs:
echo fmt"{alias}: {command}"
#___________________________________________________________________________________________________
proc look(game: Game) =
## Look at current room and display description.
echo game.rooms[game.pos].description()
#___________________________________________________________________________________________________
proc move(game: Game; direction: Direction) =
## Move in a direction.
if direction == up and ladder notin game.rooms[game.pos].items:
echo "You’ll need a ladder in this room to go up."
return
if game.rooms[game.pos].passages[direction]:
game.pos = nextPos(game.pos, direction)
else:
echo "Can’t go that way."
#___________________________________________________________________________________________________
proc displayInventory(game: Game) =
## Display the inventory.
if game.inventory.len == 0:
echo "You aren’t carrying anything."
else:
echo "Carrying: ", join(game.inventory, ", "), "."
if game.equipped != none:
echo "Holding: ", game.equipped, "."
#___________________________________________________________________________________________________
proc take(game: Game; item: Item) =
## Take an item.
game.inventory &= game.rooms[game.pos].take(item)
#___________________________________________________________________________________________________
proc drop(game: Game; item: Item) =
## Drop an item.
if item == all:
game.rooms[game.pos].items.add(game.inventory)
game.inventory.setLen(0)
echo "Everything dropped."
else:
let idx = game.inventory.find(item)
if idx >= 0:
game.inventory.delete(idx)
game.rooms[game.pos].items.add(item)
echo "Dropped ", item, "."
else:
echo "Could not find item in inventory."
#___________________________________________________________________________________________________
proc unequip(game: Game) =
## Unequip an item.
if game.equipped == none:
echo "You aren’t equipped with anything."
else:
game.inventory.add(game.equipped)
echo "Unequipped ", game.equipped, "."
game.equipped = none
#___________________________________________________________________________________________________
proc equip(game: Game; item: Item) =
## Equip an item.
let idx = game.inventory.find(item)
if idx >= 0:
if game.equipped != none:
game.unequip()
game.inventory.delete(idx)
game.equipped = item
echo "Equipped ", item, "."
else:
echo "You aren’t carrying that."
#___________________________________________________________________________________________________
proc name(game: Game; newRoomNameTokens: varargs[string]) =
## Name a room.
game.roomNames[game.pos] = newRoomNameTokens.join(" ")
#___________________________________________________________________________________________________
proc dig(game: Game; direction: Direction) =
## Dig in a direction.
if game.equipped != sledge:
echo "You don’t have a digging tool equipped."
return
if not game.rooms[game.pos].passages[direction]:
game.rooms[game.pos].passages[direction] = true
let newRoomPos = nextPos(game.pos, direction)
if newRoomPos notin game.rooms:
game.rooms[newRoomPos] = Room(game: game, pos: newRoomPos, items: randomItems())
game.rooms[newRoomPos].passages[OppositeDir[direction]] = true
echo "You have dug a tunnel."
else:
echo "There’s already a tunnel that way."
####################################################################################################
# Main.
randomize() # Initialize the random generator.
let game = newGame()
echo "Welcome to the dungeon!"
echo "Grab the sledge and make your way to room 1,1,5 for treasure!"
while true:
game.look()
if game.pos == PrizeRoomPos:
echo "You have found the prize room and the treasure!"
break
stdout.write("> ")
var line: string
if not stdin.readLine(line):
break # End of file.
var tokens = line.strip().toLower.split()
# Replace alias by the command.
if tokens[0] in game.aliases:
tokens[0] = game.aliases[tokens[0]]
# Check command.
if tokens[0] notin Commands:
echo "Invalid command."
continue
if tokens.len != Commands[tokens[0]]:
echo "Wrong number of parameters."
continue
# Process command.
case tokens[0]
of "alias":
game.createAlias(tokens[1], tokens[2])
of "aliases":
game.listAliases()
of "name":
game.name(tokens[1])
of "look":
game.look()
of "north", "east", "south", "west", "up", "down":
game.move(tokens[0].toDirection)
of "inventory":
game.displayInventory()
of "take":
game.take(tokens[1].toItem)
of "drop":
game.drop(tokens[1].toItem)
of "equip":
game.equip(tokens[1].toItem)
of "unequip":
game.unequip()
of "attack":
let dir = tokens[1].toDirection
if dir == nowhere:
echo "Wrong direction."
else:
game.dig(dir)
of "help":
displayHelp()
of "quit":
break
echo "Thanks for playing"