RCRPG/D

From Rosetta Code
Revision as of 10:28, 16 February 2014 by rosettacode>Bearophile (Updated D entry)
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

Translation of: Python

<lang d>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!");

}</lang>