RCRPG/D
< RCRPG
RCRPG/D is part of RCRPG. You may find other members of RCRPG at Category:RCRPG.
D version of RCRPG, with very basic input validation.
Code
import std.stdio, std.typecons, std.random, std.string, std.conv,
std.array, std.range, std.algorithm, std.traits, std.typetuple;
alias P3 = Tuple!(int, "x", int, "y", int, "z");
immutable P3[string] directions;
string[][string] aliases;
string[P3] roomNames;
static this() {
directions = ["north" : P3( 0,-1, 0),
"east" : P3( 1, 0, 0),
"south" : P3( 0, 1, 0),
"west" : P3(-1, 0, 0),
"up" : P3( 0, 0, 1),
"down" : P3( 0, 0,-1)];
aliases = ["north" : ["move","north"],
"south" : ["move","south"],
"east" : ["move","east"],
"west" : ["move","west"],
"up" : ["move","up"],
"down" : ["move","down"]];
roomNames = [P3(0, 0, 0): "the starting room",
P3(1, 1, 5): "the prize room"];
}
class Room {
string[] items;
bool[string] passages;
this(in string[] items = null) {
this.passages = directions.byKey.zip(repeat(false))
.assocArray;
this.items = items.dup;
}
string describe(in P3 location) const {
auto result = "You are at ";
if (location in roomNames)
result ~= roomNames[location];
else
result ~= format("%d,%d,%d", location.tupleof);
if (!this.items.empty)
result ~= "\nOn the ground you can see: " ~
this.items.join(", ");
result ~= "\nExits are: ";
string[] exits = this.passages.byKey
.filter!(k => passages[k])
.array;
if (exits.empty) exits = ["None"];
result ~= exits.map!capitalize.join(", ");
return result;
}
string[] take(in string target) {
string[] results;
if (target == "all") {
results = this.items.dup;
this.items.length = 0;
writeln("You now have everything in the room.");
} else {
immutable idx = this.items.countUntil(target);
if (idx != -1) {
this.items = this.items.remove(idx);
results = [target];
writeln("Taken ", target, ".");
} else {
writeln("Item not found.");
}
}
return results;
}
}
P3 get_new_coord(P3 oldCoord, string direction) pure nothrow {
return P3(oldCoord[0] + directions[direction][0],
oldCoord[1] + directions[direction][1],
oldCoord[2] + directions[direction][2]);
}
string opposite_dir(string direction) pure nothrow {
switch (direction) {
case "north" : return "south";
case "south" : return "north";
case "west" : return "east";
case "east" : return "west";
case "up" : return "down";
case "down" : return "up";
default:
throw new Error("No direction found: " ~ direction);
}
}
string[] make_random_items() {
return [[], ["sledge"], ["ladder"], ["gold"]][uniform(0, $)];
}
class World {
P3 currentPos = P3(0,0,0);
Room[P3] rooms;
string[] inv;
string equipped;
this() {
this.rooms = [P3(0,0,0): new Room(["sledge"])];
}
string look() {
return this.rooms[this.currentPos].describe(this.currentPos);
}
void move(in string direction) {
if (direction == "up" &&
!canFind(this.rooms[this.currentPos].items, "ladder"))
return writeln("You'll need a ladder in " ~
"this room to go up.");
if (direction !in directions)
return writeln("That's not a direction.");
if (this.rooms[this.currentPos].passages[direction])
currentPos = get_new_coord(this.currentPos, direction);
else
writeln("Can't go that way.");
}
void newalias(in string newAlias, in string[] command...) {
if (command.length == 2) {
if (command[0] in aliases) { // avoid recursive aliasing
writeln("You cannot alias an alias: " ~ command[0]);
} else {
aliases[newAlias] = command.dup;
writeln("Alias created.");
}
} else
writeln("Wrong number of arguments for alias.");
}
void inventory() {
if (this.inv.empty)
writeln("You aren't carrying anything.");
else
writeln("Carrying: ", this.inv.join(", "));
if (!this.equipped.empty)
writeln("Holding: ", this.equipped);
}
void take(in string target) {
this.inv ~= this.rooms[this.currentPos].take(target);
}
void drop(in string target) {
if (target == "all") {
this.rooms[this.currentPos].items ~= this.inv;
this.inv.length = 0;
writeln("Everything dropped.");
} else {
immutable idx = this.inv.countUntil(target);
if (idx != -1) {
this.inv = this.inv.remove(idx);
this.rooms[this.currentPos].items ~= target;
writeln("Dropped ", target , ".");
} else {
writeln("Could not find item in inventory.");
}
}
}
void equip(in string itemName) {
immutable idx = this.inv.countUntil(itemName);
if (idx != -1) {
if (!this.equipped.empty)
this.unequip;
this.inv = this.inv.remove(idx);
this.equipped = itemName;
writeln("Equipped ", itemName, ".");
} else
writeln("You aren't carrying that.");
}
void unequip() {
if (this.equipped.empty)
writeln("You aren't equipped with anything.");
else {
this.inv ~= this.equipped;
writeln("Unequipped ", this.equipped, ".");
this.equipped = null;
}
}
void name(in string[] newRoomNameTokens...) {
roomNames[this.currentPos] = newRoomNameTokens.join(' ');
}
void dig(in string direction) {
if (this.equipped != "sledge")
return writeln("You don't have a digging tool equipped.");
if (direction !in directions)
return writeln("That's not a direction.");
if (!this.rooms[this.currentPos].passages[direction]) {
this.rooms[this.currentPos].passages[direction] = true;
auto joinRoomPos = get_new_coord(this.currentPos,
direction);
if (joinRoomPos !in this.rooms)
this.rooms[joinRoomPos]= new Room(make_random_items);
this.rooms[joinRoomPos]
.passages[opposite_dir(direction)] = true;
writeln("You've dug a tunnel.");
} else
writeln("Already a tunnel that way.");
}
}
void processArguments(in string[] a, World w) {
foreach (f; __traits(derivedMembers, World)) {
static if (isCallable!(mixin("World." ~ f))) {
enum len = ParameterTypeTuple!(mixin("World." ~ f)).length;
enum var = variadicFunctionStyle!(mixin("World." ~ f));
static if (len == 0)
mixin("if (a[0] == f) w." ~ f ~ "();");
else static if (var && len == 1)
mixin("if(a[0] == f) w." ~ f ~ "(a[1 .. $]);");
else static if (var && len == 2)
mixin("if(a[0] == f) w." ~ f ~ "(a[1], a[2 .. $]);");
else static if (len == 1)
mixin("if (a[0] == f) w." ~ f ~ "(a[1]);");
}
}
}
void main() {
auto world = new World;
writeln("Welcome to the dungeon!\nGrab the sledge && make your" ~
" way to room 1,1,5 for a non-existent prize!");
while (true) {
writeln("\n", world.look);
write("> ");
auto tokens = readln.strip.toLower.split;
if (tokens[0] == "quit")
break;
if (tokens[0] == "alias")
tokens[0] = "newalias";
while (!tokens.empty) {
if (tokens[0] in aliases &&
tokens[0] != aliases[tokens[0]][0]) {
tokens = aliases[tokens[0]] ~ tokens[1 .. $];
continue;
}
try {
processArguments(tokens, world);
} catch (Throwable e) {
writeln("Invalid input.");
}
break;
}
}
writeln("Thanks for playing!");
}