import std.stdio, std.typecons, std.random, std.string, std.conv,
std.array, std.range, std.algorithm;
alias T3 = Tuple!(int, int, int);
immutable T3[string] directions;
string[][string] aliases;
string[T3] roomNames;
static this() {
directions = ["north":T3(0,-1,0),
"east":T3(1,0,0),
"south":T3(0,1,0),
"west":T3(-1,0,0),
"up":T3(0,0,1),
"down":T3(0,0,-1)];
aliases = ["north": ["move","north"],
"south": ["move","south"],
"east": ["move","east"],
"west": ["move","west"],
"up": ["move","up"],
"down": ["move","down"]];
roomNames = [T3(0,0,0):"the starting room",
T3(1,1,5):"the prize room"];
}
class Room {
string[] items;
bool[string] passages;
this(string[] items=null) {
this.passages = directions.byKey.zip(repeat(false))
.assocArray();
this.items = items.dup;
}
string describe(T3 location) {
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 if (this.items.canFind(target)) {
this.items = this.items.remove(target);
results = [target];
writeln("Taken.");
} else
writeln("Item not found.");
return results;
}
}
T3 get_new_coord(T3 oldCoord, string direction) pure nothrow {
return T3(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 {
T3 currentPos = T3(0,0,0);
Room[T3] rooms;
string[] inv;
string equipped;
this() {
this.rooms = [T3(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(string newAlias, string[] command...) {
aliases[newAlias] = command.dup;
writeln("Alias created.");
}
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 if (this.inv.canFind(target)) {
this.inv = this.inv.remove(target);
this.rooms[this.currentPos].items ~= target;
writeln("Dropped.");
} else
writeln("Could not find item in inventory.");
}
void equip(in string itemName) {
if (this.inv.canFind(itemName)) {
if (!this.equipped.empty)
this.unequip();
this.inv = this.inv.remove(itemName);
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(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 main() {
auto world = new World();
writeln("Welcome to the dungeon!\nGrab the sledge && make your" ~
" way to room 1,1,5 for a non-existant prize!");
while (true) {
writeln("\n", world.look());
write("> ");
auto tokens = readln().strip().toLower().split();
if (tokens[0] == "quit")
break;
while (!tokens.empty) {
if (tokens[0] in aliases && tokens[0] != aliases[tokens[0]][0]) {
tokens[0] = aliases[tokens[0]];
continue;
}
/*
try:
getattr(world, tokens[0])(tokens[1 .. $])
except AttributeError as ex:
writeln("Command not found.")
except TypeError as ex:
writeln("Wrong number of args.")
*/
break;
}
}
writeln("Thanks for playing!");
}