I'm working on modernizing Rosetta Code's infrastructure. Starting with communications. Please accept this time-limited open invite to RC's Slack.. --Michael Mol (talk) 20:59, 30 May 2020 (UTC)


From Rosetta Code
RCRPG/Unicon is part of RCRPG. You may find other members of RCRPG at Category:RCRPG.

The following program implements the RCRPG game in Unicon.

It was fun and fairly fast to write. After some initial simple bugs, I had to deal with relatively few oversights. The most major were forgetting to back link rooms and overwriting the prize room after tunneling into it.

This version was developed from the blog description and some skimming of other solutions for messaging and consistency in handling under specified features. I believe it's a bit chattier than some of the other versions.

There are a couple embellishments.

  • Pre-loading simple aliases for ease of use.
  • A magic word for when all hope is lost. Every good adventure like game needs a magic word.
  • If you name a room but forget to give a name, a random one will be chosen.
  • Things like a low probability of breaking a sledge and taking multiple hits to break through were probably stolen from other variants.

There are quirks, probably some undiscovered ones at that. Most notably the alias command only works on the command verb. The facility could be extended but would need some safeties to prevent you from doing things like redefining north.

Programming style is slightly lazy. Locals aren't declared, most procedures fail rather than return a value even when they do the write thing. Only a few globals are used.

The implementation uses a number of minor Unicon extensions over Icon. The main extension(s) used is probably the extended delete function to remove list elements. It may reference records by subscripts but I think that is removed.

The main program follows:

record roominfo(x,y,z,name,items,n,s,e,w,u,d)    # about rooms
record urinfo(here,carrying,using) # about your character
global Start, Finish, Directions, UR, Aliases, Command
invocable all # needed for command verbs
procedure main() #: main procedure for RCRPG
Start := AddRoom(0,0,0,"the starting room","sledge")
Finish := AddRoom(1,1,5,"the prize room","gold")
Directions := table()
every x := "north"|"east"|"south"|"west"|"up"|"down" & Directions[x|x[1]] := x
UR := urinfo(Start,[])
write("Welcome to RCRPG (Icon/Unicon version)!\n\n_
Your goal is make your way to room 1,1,5.\n_
Hint: You'll need the sledge hammer.\n"
(\Alias_Setup)() # optional
repeat Describe() & DoCommand()

The following implement the navigation procedures:

procedure AddRoom(x,y,z,name,item[])             #: Create room w/item(s)
if *item = 0 then push(item, \?[&null,"sledge","ladder","gold"]) # randomize
return roominfo(x,y,z,\name|"",item)
procedure Attack() #: Attack
whereto := Directions[Command[2]]
if /NextRoom(whereto) then
if \UR.using == "sledge" then {
if ?0 > .1 then { # 90% chance of success
x := UR.here.x + ((whereto=="east",1) | (whereto=="west",-1) | 0)
y := UR.here.y + ((whereto=="north",1) | (whereto=="south",-1) | 0)
z := UR.here.z + ((whereto=="up",1) | (whereto=="down",-1) | 0)
r := if x = Finish.x & y = Finish.y & z = Finish.z then Finish
else AddRoom(x,y,z)
NextRoom(whereto) := r
case whereto of { "up":r.d; "down":r.u;
"north":r.s; "east":r.w; "south":r.n; "west":r.e } := UR.here
write("You created a tunnel to another room.")
else write("You're almost through, try again.")
if ?0 < .1 then { # 10% chance of breakage
UR.using := &null
put(UR.here.items,"broken hammer")
write("You broke your sledge hammer and left it on the ground.")
write(if \Abracadabra then "There is a hint of magic in the air."
else "Hope you found another.")
else write("You have no digging equipment.")
else if not NextRoom(whereto) then write("I don't know that direction.")
else write("There is already an opening in that direction.")
procedure Go() #: Go in a direction
if room := NextRoom(whereto := \Directions[Command[2]]) then {
(UR.here := \room) | write("There is no opening going ",whereto)
else write("I don't know that direction.")
procedure NextRoom(whereto) #: return next room or fail
return case Directions[whereto] of {
"up" : UR.here.u
"down" : UR.here.d
"north" : UR.here.n
"east" : UR.here.e
"south" : UR.here.s
"west" : UR.here.w

The following are the command processing and non-navigational commands:

procedure Alias()                                #: make a command alias
/Aliases := table()
if Aliases[Command[2]] := Command[3] then
write(Command[2]," is now an alias for ",Command[3])
else write("What did you want me to alias?")
procedure Describe() #: Describe where U R
static lastroom
if lastroom ~===:= UR.here then Look() # don't repeat description
if UR.here === Finish then stop("Congratulations! You won.\nCome again.")
return # needed for main loop
procedure Drop() #: drop item
room := UR.here
if item := Command[2] then
if item == "all" then {
while put(room.items, get(UR.carrying))
put(room.items,\UR.using) & UR.using := &null
write("Everything you had is on the floor.")
else if item == \UR.using then {
UR.using := &null
write("You dropped the ",item)
else if item == !UR.carrying then {
write("You dropped the ",item)
else write("You don't have any ",item)
else write("What do you want me to drop?")
procedure DoCommand(command) #: Do the command
static verbs
initial {
verbs := table()
every p := ![Alias,Attack,Drop,Equip,Go,Help,Inventory,Look,Name,Take,Abracadabra] do
verbs[map(image(\p))[*"procedure X":0]] := p
Command := []
( writes("> "), trim(pretrim(read()))) ? until pos(0) do {
put(Command,tab(upto(' \t')|0))
tab(many(' \t'))
repeat {
if \Directions[\Command[1]] then push(Command,"go")
if p := \verbs[\Command[1]] then {
p() # call command
else if Command[1] := \(\Aliases)[Command[1]] then next # aliased, retry
else if Command[1] == "quit" then stop("Quitting.")
else break write("I;m sorry I don't know that command.")
procedure Equip() #: Equip
if item := Command[2] then
if item == (\UR.using | !UR.carrying) then {
put(UR.carrying, \UR.using) # save equip
delete(UR.carrying,index(UR.carrying,UR.using:=item)) # move to using
write("You are now equipped with a ",UR.using)
else write("You aren't carrying a ",item)
else write("Equip with what?")
procedure HandleLadder(whereto) #: Handle ladder
if whereto == "up" then { # only for up
if (item := "ladder") == \UR.using then
put(UR.here.items,item) & UR.using := &null
else if item == !UR.carrying then
put(UR.here.items,item) & delete(UR.carrying,index(UR.carrying,item))
if item == !UR.here.items then return
else write("You can't go up without a ladder in the room.")
procedure Help() #: give help
(go) north, south, east, west, up, down - \n_
\tmoves you in the direction specified if there is an exit\n_
\ta ladder is required to go up or down\n_
look - take a look around\n_
inventory - Show everything you're carrying\n_
equip (item name) - Equip the item in question\n_
attack (direction) - attack in the direction specified\n_
drop (item name)|all - Drop the item specified or everything you'e carrying\n_
take (item name)|all - Take the item specified or everything in the room\n_
name (name) - Rename the room to whatever you want to call it\n_
alias (existing command) (new name) -\n_
\tCreate an alias for an existing command.\n_
\tYou can't redefine an existing command.\n_
quit - useful if you broke your only sledge hammer\n"
end #
procedure Inventory() #: show inventory
if /UR.using & (*UR.carrying = 0) then
write("You haven't got anything with you.")
else {
write("You have ",ShowList((*UR.carrying>0,UR.carrying))," in your pack.")
write("You are holding a ",\UR.using," in your hand.")
procedure Look() #: Look around and describe
r := UR.here
write(sprintf("You are in the %s(%d,%d,%d) room.",r.name,r.x,r.y,r.z))
if *r.items>0 then write("On the ground you can see " || ShowList(r.items))
every insert(dl := set(), (\NextRoom(d := !Directions), d))
if *dl>0 then write("There are openings in the directions ", ShowList(dl))
else write("There are no openings.")
procedure Name() #: Name the current room
if UR.here.name := \Command[2] then
write("The room is now called ",UR.here.name)
else write("You didn't provide a name. I think I shall name it ",
UR.here.name := ?["Fred","Ringo","George","John","Paul","Ozzy",
"Hamlet","Kermit","Spot","Minka", "Archie"])
procedure Take() #: take item
room := UR.here
if item := Command[2] then
if item == "all" then {
while put(UR.carrying,get(room.items))
write("You have everything in the room greedy!")
else if delete(room.items,index(room.items,item)) then {
write("You now have the ",item)
else write("I see no ",item)
else write("What do you want me to take?")

The following are miscellaneous supprt procedures:

procedure ShowList(X,c)                          #: return c separated elements
/c := ", "
every (s := "") ||:= !X || c
return s[1:-*c]
procedure index(L, x) #: generate indices for x
every if x === L[i := 1 to *L] then suspend i

The following are minor embellishments that are silently linked. If they are left out, the remaining code should be able to continue on unaffected.

procedure Abracadabra()                          #: magic word
static uses
initial uses := 3
write("That is very old magic, I'm not sure it still works ...")
if 0 < (uses -:= 1) then {
write("... Poof! A sledge appears in a cloud of purple smoke")
else if uses = 0 then write("... No. Nothing happens.")
else if uses = -1 then write("... You now have a fuchsai tail!!")
else write("... Thankfully nothing else happens.")
procedure Alias_Setup() #: ease of use aliases
/Aliases := table()
every a := key(Directions) | !["inventory","attack","take","look"] do
Aliases[a[1]] := a
every Aliases[!["hocus","pocus","shazzam","walla","xyzzy"]] := "abracadabra"