RCRPG/C++11: Difference between revisions

From Rosetta Code
Content added Content deleted
(Created page with "{{collection|RCRPG}} C++11 version of RCRPG. The code can also be checked out and contributed to on [https://github.com/pistacchio/rosettacode.clojure....")
 
No edit summary
Line 1: Line 1:
{{collection|RCRPG}}
{{collection|RCRPG}}
[[C++11]] version of [[:Category:RCRPG|RCRPG]]. The code can also be checked out and contributed to on [https://github.com/pistacchio/rosettacode.clojure.rcrpg github] .
[[Javascript]] (node) version of [[:Category:RCRPG|RCRPG]]. The code can also be checked out and contributed to on [https://github.com/pistacchio/rcrpgjs github] .


==Code==
==Code==
<lang cpp>
<lang javascript>
const _ = require('lodash'),
//
readline = require('readline');
// main.cpp
// RCRPGCpp
//
// Created by pistacchio on 19/03/14.
//
//


#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <string>
#include <random>
#include <regex>
#include <vector>


///////////////////
// Initialize the randomization engine
// LODASH IMPORT //
std::default_random_engine rnd_eng(std::random_device{}());
///////////////////


// import all lodash functions to the main namespace, but isNaN not to cause conflicts
//////////////////
_.each(_.keys(_), k => global[k === 'isNaN' ? '_isNaN' : k] = _[k]);
// String utils //
//////////////////


///////////////
// Concats string in a nice readable format adding "," and "and"
// CONSTANTS //
std::string descriptive_join(const std::vector<std::string>& items) {
///////////////
std::string desc;
std::vector<std::string> itms {items.begin(), items.end()};
int items_size {(int)items.size()};
if (items_size > 1) {
for (int i {0}; i < items_size - 1; i++) {
itms.insert(itms.begin() + i * 2 + 1, i == items_size -2 ? " and " : ", ");
}
}
std::for_each(itms.begin(), itms.end(), [&](std::string s) {
desc += s;
});
return desc;
}


const DIRECTIONS = {
//////////
n: [ 0, -1, 0],
// Item //
s: [ 0, 1, 0],
//////////
e: [-1, 0, 0],

w: [ 1, 0, 0],
enum Item {LADDER, SLEDGE, GOLD};
u: [ 0, 0, -1],
static const std::map<Item, std::string> Item_strings {
{LADDER, "a ladder"},
d: [ 0, 0, 1]
{SLEDGE, "a sledge"},
{GOLD, "some gold"}
};
static const std::map<std::string, Item> String_items {
{"ladder", LADDER},
{"sledge", SLEDGE},
{"gold", GOLD}
};
};


/////////////
/////////////
// Aliases //
// HELPERS //
/////////////
/////////////


function findDirection (world, dir, longName=false) {
std::map<std::string, std::string> Aliases {
const maybeDirection = find(world.commands, c => contains(first(c), dir)),
{"i", "inventory"},
dirLetter = first(first(maybeDirection));
{"n", "north"},

{"e", "east"},
if (maybeDirection && has(DIRECTIONS, dirLetter)) {
{"w", "west"},
return longName ? first(maybeDirection)[1] : dirLetter;
{"s", "south"},
}
{"get", "take"},
{"u", "up"},
{"l", "look"},
{"d", "down"}
};
std::string unalias(std::string term) {
if (Aliases.find(term) == Aliases.end()) {
return term;
} else {
return Aliases.find(term)->second;
}
}
}

void list_aliases() {
function makeRoom(location) {
std::cout << "Current aliases are:" << std::endl;
return [location, null, compact([
for_each(Aliases.begin(), Aliases.end(), [](std::pair<std::string, std::string> alias){
random(0, 2) === 0 ? 'sledge': null,
std::cout << alias.first << " -> " << alias.second << std::endl;
random(0, 2) === 0 ? 'ladder': null,
});
random(0, 2) === 0 ? 'gold': null
])];
}
}
void add_alias(std::string alias, std::string command) {
if (Aliases.find(alias) == Aliases.end()) {
Aliases[alias] = command;
}


function listToDescriptiveString (lst) {
if (!lst) return;
if (lst.length === 1) return first(lst);
return dropRight(lst).join(', ') + ' and ' + last(lst);
}

function itemsToDescriptiveItems (items) {
return map(items, i => {
switch (i) {
case 'sledge': return 'a sledge';
case 'ladder': return 'a ladder';
case 'gold': return 'some gold';
}
});
}
}


//////////////
//////////////
// Location //
// MESSAGES //
//////////////
//////////////


function help () {
struct Location {
return `You need a sledge to dig rooms and ladders to go upwards.
int x;
Valid commands are: directions (north, south...), dig, take, drop, equip, inventory and look.
int y;
Additionally you can tag rooms with the 'name' command and alias commands with 'alias'.
int z;
Have fun!
`;
Location (int xval, int yval, int zval): x(xval), y(yval), z(zval) {}
}


function welcome () {
Location& operator+= (Location const& other) {
console.log(`Welcome to the dungeon!
*this = *this + other;
Grab the sledge and make your way to room 1,1,5 for a non-existant prize!
return *this;
`);
console.log(help());
}

/////////////
// ACTIONS //
/////////////

function gotoRoom (direction, world) {
const wantedRoom = zipWith(world.player.location, DIRECTIONS[direction], add),
room = find(world.rooms, r => isEqual(first(r), world.player.location));

if (direction === 'u' && !contains(room[2], 'ladder')) {
console.log("You can't go upwards without a ladder!");
} else {
if (find(world.rooms, r => isEqual(first(r), wantedRoom))) {
world.player.location = wantedRoom;
look(world);
} else {
console.log("There's no exit in that direction!");
}
}
}
Location operator+ (const Location& other) {

return Location(this->x + other.x, this->y + other.y, this->z + other.z);
}
return world
};
bool operator< (const Location& left, const Location& right) {
return std::to_string(left.x) + '-' + std::to_string(left.y) + '-' + std::to_string(left.z) <
std::to_string(right.x) + '-' + std::to_string(right.y) + '-' + std::to_string(right.z);
}
}


function dig (world, [direction]) {
///////////////
let dir,
// Direction //
dirLongName,
///////////////
wantedRoom;


if (!direction) {
static const std::map<std::string, Location> Direction {
console.log('Where do you want to dig?');
{"north", Location( 0, -1, 0)},
return world
{"south", Location( 0, 1, 0)},
}
{"east", Location(-1, 0, 0)},
if (!(dir = findDirection(world, direction))) {
{"west", Location( 1, 0, 0)},
console.log('That is not a direction I recognize');
{"up", Location( 0, 0, -1)},
return world;
{"down", Location( 0, 0, 1)}
};
}
if (!contains(world.player.inventory, 'sledge')) {
console.log('With your bare hands?');
return world;
}


dirLongName = findDirection(world, direction, true);
//////////////////////////////
wantedRoom = zipWith(world.player.location, DIRECTIONS[dir], add);
// Interface Item Container //
//////////////////////////////


if (find(world.rooms, r => isEqual(first(r), wantedRoom))) {
// Inherited by Player and Room, both need a way to contain items
console.log('There is already an exit, there!');
class IItemContainer {
return world;
public:
}
bool has_item(const Item item) const {
return items.find(item) != items.end();
}
bool add_item(Item item) {
if (items.find(item) == items.end()) {
items.insert(item);
return true;
} else {
return false;
}
}
std::set<Item> add_all_items(std::set<Item> new_items) {
items.insert(new_items.begin(), new_items.end());
return items;
}
bool remove_item(Item item) {
if (items.find(item) == items.end()) {
return false;
} else {
items.erase(item);
return true;
}
}
std::set<Item> remove_all_items() {
std::set<Item> removed_items {items.begin(), items.end()};
items = std::set<Item> {};
return removed_items;
}
protected:
std::set<Item> items;
};


world.rooms.push(makeRoom(wantedRoom));
console.log(`There is now an exit ${dirLongName}ward`);
return world;
}


function look (world) {
//////////
const room = find(world.rooms, r => isEqual(first(r), world.player.location)),
// Room //
//////////


itemsOnFloor = itemsToDescriptiveItems(room[2]),
class Room : public IItemContainer {
itemsOnFloorStr = isEmpty(itemsOnFloor)
public:
? ''
Room () = default;
: (itemsOnFloor.length === 1
Room (std::string description): description(description) {
? itemsOnFloor[0]
std::uniform_int_distribution<int> gen(0, 2);
: listToDescriptiveString(itemsOnFloor)),


if (gen(rnd_eng) == 0) {
exits = chain(DIRECTIONS)
items.insert(LADDER);
.map((d, kd) => [kd, zipWith(d, first(room), add)])
.filter(r => find(world.rooms, rr => isEqual(first(rr), r[1])))
}
if (gen(rnd_eng) == 0) {
.map(d => find(world.commands, c => {
items.insert(SLEDGE);
return contains(first(c), first(d))
}
})[0][1])
if (gen(rnd_eng) == 0) {
.value(),
items.insert(GOLD);
exitsStr = isEmpty(exits)
}
? ''
: (exits.length === 1
}
? `There is one exit: ${exits[0]}.`
const std::string describe (std::vector<std::string> adjacent_directions) const {
: `You can see the following exits: ${listToDescriptiveString(exits)}.`);
std::string items_on_floor;
std::string exits;
if (items.size() > 0) {
items_on_floor = "\nOn the floor you can see " + describe_items();
}
if (adjacent_directions.size() == 1) {
exits = "There is one exit: " + adjacent_directions.front();
} else if (adjacent_directions.size() > 1) {
exits = "\nYou can see the following directions: ";


console.log(
exits += descriptive_join(adjacent_directions) + ".";
(room[1] ? room[1] : `Room at ${world.player.location[0]}, ${world.player.location[1]}, ${world.player.location[2]}`) + '\n' +
}
(!itemsOnFloorStr ? '' : `On the floor you can see ${itemsOnFloorStr}.\n`) +
exitsStr
return description + items_on_floor + exits + "\n";
}
);
private:
std::string description;
std::string describe_items () const {
std::vector<std::string> itms;
std::for_each(items.begin(), items.end(), [&](Item i) {
itms.push_back(Item_strings.find(i)->second);
});
return descriptive_join(itms) + ".";
}
};


return world;
}


function inventory (world) {
///////////
if (world.player.inventory.length === 0) {
// World //
console.log('You are not carrying anything');
///////////
} else {
console.log(`You are carrying: ${listToDescriptiveString(itemsToDescriptiveItems(world.player.inventory))}`);
}


return world;
class World {
}
public:
World () {
rooms[Location(0, 0, 0)] = Room{"The room where it all started..."};
rooms[Location(1, 1, 5)] = Room{"You found it! Lots of gold!"};
}
bool room_exists (const Location room) const {
return rooms.find(room) != rooms.end();
}
Room& room_at (const Location location) {
return rooms[location];
}
const void dig (Location location) {
rooms[location] = Room{};
}
const std::vector<std::string> adjacent_directions (Location location) {
std::vector<std::string> result {};


function take (world, [item]) {
std::for_each(Direction.begin(), Direction.end(), [&](std::pair<std::string, Location> direction) {
const room = find(world.rooms, r => isEqual(first(r), world.player.location))
if (room_exists(location + direction.second)) {
result.push_back(direction.first);
}
});
return result;
}
private:
std::map<Location, Room> rooms;
};


if (!item) {
////////////
console.log('Take what?');
// Player //
return world;
////////////
}


if (item === 'all') {
class Player : public IItemContainer {
if (isEmpty(room[2])) {
public:
console.log('There is nothing to take here');
Player () : current_location(0, 0, 0) {
} else{
items.insert(SLEDGE);
world.player.inventory = uniq(world.player.inventory.concat(room[2] || []));
};
room[2] = [];
const Location goto_direction (const Location direction) {
console.log('All items taken');
current_location += direction;
return current_location;
}
}
return world;
Location get_current_location () const {
}
return current_location;
}
std::string inventory () {
if (items.size() == 0) {
return "You are not carrying anything";
} else {
std::vector<std::string> itemsv;
std::for_each(items.begin(), items.end(), [&](Item i) {
itemsv.push_back(Item_strings.find(i)->second);
});
return "You are carrying: " + descriptive_join(itemsv);
}
}
bool equip(Item item) {
if (has_item(item)) {
equipping = true;
equipped = item;
return true;
} else {
return false;
}
}
bool unequip(Item item) {
if (equipped == item) {
equipping = false;
return true;
} else {
return false;
}
}
bool is_equipping(Item item) {
return equipping == true && equipped == item;
}
private:
Location current_location;
bool equipping {false};
Item equipped;
};


if (!contains(room[2], item)) {
console.log("You can't see anything like that here");
return world;
}


world.player.inventory = uniq(world.player.inventory.concat([item]));
//////////
pull(room[2], item);
// Game //
console.log('Taken');
//////////


return world
class Game {
}
public:

void execute(const std::string command) {
function drop (world, [item]) {
std::vector<std::string> cmd;
const room = find(world.rooms, r => isEqual(first(r), world.player.location))

if (!item) {
console.log('Drop what?');
return world;
}


if (item === 'all') {
std::for_each(std::sregex_token_iterator {command.begin(), command.end(), std::regex {"\\s+"}, -1},
if (isEmpty(world.player.inventory)) {
std::sregex_token_iterator {},
console.log('You have nothing to drop');
[&](std::ssub_match sm) {
} else{
cmd.push_back(sm.str());
room[2] = uniq(room[2].concat(world.player.inventory || []));
});
world.player.inventory = [];
console.log('All items dropped');
std::string action {unalias(cmd.front())};
// HELP
if (action == "help") {
print_help();
// MOVE (Up, Down, North etc)
} else if (Direction.find(action) != Direction.end()) {
Location direction {Direction.find(action)->second};
Location location {direction + player.get_current_location()};
if (world.room_exists(location)) {
Room& room {world.room_at(location)};
if (action == "up" && !room.has_item(LADDER)) {
std::cout << "You can't go upwards without a ladder!" << std::endl;
} else {
Location current_location = player.goto_direction(direction);
std::cout << world.room_at(current_location).describe(world.adjacent_directions(current_location));
}
} else {
std::cout << "There's no exit in that direction!" << std::endl;
}
// LOOK and describe the room
} else if (action == "look") {
std::cout << world.room_at(player.get_current_location()).describe(world.adjacent_directions(player.get_current_location()));
// DIG towards a direction
} else if (action == "dig") {
if (cmd.size() != 2) {
std::cout << "Where do you want to dig?" << std::endl;
} else if (!player.is_equipping(SLEDGE)) {
std::cout << "With your bare hands?!" << std::endl;
} else {
if (Direction.find(cmd[1]) == Direction.end()) {
std::cout << "That is not a direction I recognize" << std::endl;
} else {
Location new_loc = player.get_current_location() + Direction.find(cmd[1])->second;
if (world.room_exists(new_loc)) {
std::cout << "There is already an exit, there!" << std::endl;
} else {
world.dig(player.get_current_location() + Direction.find(cmd[1])->second);
std::cout << "There is now a new exit " << cmd[1] << "ward" << std::endl;
}
}
}
// DROP an item or all in the inventory
} else if (action == "drop") {
if (cmd.size() == 1) {
std::cout << "Drop what?" << std::endl;
} else {
if (cmd[1] == "all" ) {
Room& room {world.room_at(player.get_current_location())};
room.add_all_items(player.remove_all_items());
std::cout << "All items dropped;" << std::endl;
} else if (String_items.find(cmd[1]) == String_items.end()) {
std::cout << "\"" << cmd[1] <<"\" is not something I recognize" << std::endl;
} else {
Item item {String_items.find(cmd[1])->second};
if (player.remove_item(item)) {
Room& room {world.room_at(player.get_current_location())};
room.add_item(item);
std::cout << "Item dropped" << std::endl;
} else {
std::cout << "You don't have any" << cmd[1] << std::endl;
}
}
}
// TAKE an item from the room or all of them
} else if (action == "take" ) {
if (cmd.size() == 1) {
std::cout << "Take what?" << std::endl;
} else {
if (cmd[1] == "all") {
Room& room {world.room_at(player.get_current_location())};
player.add_all_items(room.remove_all_items());
std::cout << "All items taken;" << std::endl;
} else if (String_items.find(cmd[1]) == String_items.end()) {
std::cout << "\"" << cmd[1] <<"\" is not something I recognize" << std::endl;
} else {
Item item {String_items.find(cmd[1])->second};
Room& room {world.room_at(player.get_current_location())};
if (room.remove_item(item)) {
player.add_item(item);
std::cout << "Item taken" << std::endl;
} else {
std::cout << "There is no such item in the room" << std::endl;
}
}
}
// INVENTORY listing
} else if (action == "inventory") {
std::cout << player.inventory() << std::endl;
// EQUIP an item currently in the inventory
} else if (action == "equip") {
if (cmd.size() == 1) {
std::cout << "What do you want to equip?" << std::endl;
} else {
if (String_items.find(cmd[1]) == String_items.end()) {
std::cout << "That is not an item I recognize" << std::endl;
} else {
if (player.equip(String_items.find(cmd[1])->second)) {
std::cout << "Item equipped!" << std::endl;
} else {
std::cout << "You don't have it" << std::endl;
}
}
}
// UNEQUIP an item currently equipped
} else if (action == "unequip") {
if (cmd.size() == 1) {
std::cout << "What do you want to unequip?" << std::endl;
} else {
if (String_items.find(cmd[1]) == String_items.end()) {
std::cout << "That is not an item I recognize" << std::endl;
} else {
if (player.equip(String_items.find(cmd[1])->second)) {
std::cout << "Item unequipped!" << std::endl;
} else {
std::cout << "You don't have it" << std::endl;
}
}
}
// ALIAS a command, or list the aliases
} else if (action == "alias") {
if (cmd.size() == 1) {
list_aliases();
} else if (cmd.size() == 3) {
add_alias(cmd[1], cmd[2]);
std::cout << "Alias added" << std::endl;
} else {
std::cout << "The correct use is: alias <ALIAS> <COMMAND>" << std::endl;
}
// WHAT?! A command that is not undertood by this simplistic parser
} else {
std::cout << "Hm?! What do you mean?" << std::endl;
}
}
}
return world;
void print_help() {
}
std::cout << "You need a sledge to dig rooms and ladders to go upwards." << std::endl
<< "Valid commands are: directions (north, south...), dig, take, drop, equip, inventory and look." << std::endl
<< "Additionally you can tag rooms with the 'name' command and alias commands with 'alias'." << std::endl
<< "Have fun!" << std::endl;
}
private:
World world;
Player player;
};


if (!contains(world.player.inventory, item)) {
console.log("You don't have that item");
return world;
}


room[2] = uniq(room[2].concat([item]));
////////////////
pull(world.player.inventory, item);
// Main cycle //
console.log('Dropped');
////////////////


return world
int main()
}
{

Game game;
function equip (world, [item]) {
std::string input_line;
if (!item) {
console.log('What do you want to equip?');
std::cout << "Welcome to the dungeon!" << std::endl
return world;
<< "Grab the sledge and make your way to room 1,1,5 for a non-existant prize!" << std::endl << std::endl;
}
game.print_help();
if (!contains(world.player.inventory, item)) {
console.log("You don't have such object");
while (input_line != "exit") {
return world;
std::getline(std::cin, input_line);
}

game.execute(input_line);
world.player.equipped = item;
console.log('Item equipped!');
return world;
}

function unequip (world, [item]) {
if (!item) {
console.log('What do you want to unequip?');
return world;
}
if (item !== world.player.equipped) {
console.log("You don't have it equipped");
return world;
}

world.player.equipped = null;
console.log('Item unequipped!');
return world;
}

function alias (world, [cmd, al]) {
let foundCommand;

if (!cmd && !al) {
console.log('Aliases:' + chain(world.commands)
.filter(c => first(c).length > 1)
.map(first)
.reduce((acc, c) => `${acc}\n${first(c)} => ${rest(c).join(', ')}`, '')
.value());
return world;
}

if (!(foundCommand = find(world.commands, c => contains(first(c), cmd)))) {
console.log('There is no such command');
return world
}

if (!al) {
let aliases = reject(first(foundCommand), c => c === cmd);

if (isEmpty(aliases)) {
console.log(`There are no aliases for ${cmd}`);
} else {
console.log(`Aliases for "${cmd}": ${aliases.join(', ')}`);
}
}

return world;
std::cout << "See you next time!" << std::endl;
}

return 0;
foundCommand[0] = uniq(first(foundCommand).concat([al]));
console.log('Alias assigned');
return world;
}
}

//////////////////////
// INPUT PROCESSING //
//////////////////////

function processInput(input, world) {
const splitInput = trim(input).split(/\s+/g),
[command, options] = [first(splitInput), rest(splitInput)],
commandFn = find(world.commands, c => contains(first(c), command.toLowerCase()));

if (commandFn) {
return commandFn[1](world, options);
} else {
console.log("I don't know what you mean.");
return world;
}
}

///////////////
// MAIN LOOP //
///////////////

(function runGame () {
let world = {
rooms: [
[[0, 0, 0], "The room where it all started...", ['ladder', 'sledge']],
[[1, 1 , 5], "You found it! Lots of gold!"]
],
commands: [
[['n', 'north'], partial(gotoRoom, 'n')],
[['s', 'south'], partial(gotoRoom, 's')],
[['w', 'west'], partial(gotoRoom, 'w')],
[['e', 'east'], partial(gotoRoom, 'e')],
[['d', 'down'], partial(gotoRoom, 'd')],
[['u', 'up'], partial(gotoRoom, 'u')],

[['help'], help],
[['dig'], dig],
[['l', 'look'], look],
[['i', 'inventory'], inventory],
[['take'], take],
[['drop'], drop],
[['equip'], equip],
[['unequip'], unequip],
[['alias'], alias]
],
player: {
location: [0, 0, 0],
inventory: ['sledge'],
equipped: null
}
};

welcome();

process.stdin.resume();
process.stdin.setEncoding('utf8');

process.stdin.on('data', input => world = processInput(input, world))
})();
</lang>
</lang>

Revision as of 10:05, 6 May 2015

RCRPG/C++11 is part of RCRPG. You may find other members of RCRPG at Category:RCRPG.

Javascript (node) version of RCRPG. The code can also be checked out and contributed to on github .

Code

<lang javascript> const _ = require('lodash'),

     readline = require('readline');


/////////////////// // LODASH IMPORT // ///////////////////

// import all lodash functions to the main namespace, but isNaN not to cause conflicts _.each(_.keys(_), k => global[k === 'isNaN' ? '_isNaN' : k] = _[k]);

/////////////// // CONSTANTS // ///////////////

const DIRECTIONS = {

 n: [ 0, -1,  0],
 s: [ 0,  1,  0],
 e: [-1,  0,  0],
 w: [ 1,  0,  0],
 u: [ 0,  0, -1],
 d: [ 0,  0,  1]

};

///////////// // HELPERS // /////////////

function findDirection (world, dir, longName=false) {

 const maybeDirection = find(world.commands, c => contains(first(c), dir)),
       dirLetter      = first(first(maybeDirection));
 if (maybeDirection && has(DIRECTIONS, dirLetter)) {
   return longName ? first(maybeDirection)[1] : dirLetter;
 }

}

function makeRoom(location) {

 return [location, null, compact([
   random(0, 2) === 0 ? 'sledge': null,
   random(0, 2) === 0 ? 'ladder': null,
   random(0, 2) === 0 ? 'gold':   null
 ])];

}

function listToDescriptiveString (lst) {

 if (!lst) return;
 if (lst.length === 1) return first(lst);
 return dropRight(lst).join(', ') + ' and ' + last(lst);

}

function itemsToDescriptiveItems (items) {

return map(items, i => {
   switch (i) {
     case 'sledge': return 'a sledge';
     case 'ladder': return 'a ladder';
     case 'gold':   return 'some gold';
   }
 });

}

////////////// // MESSAGES // //////////////

function help () {

 return `You need a sledge to dig rooms and ladders to go upwards.

Valid commands are: directions (north, south...), dig, take, drop, equip, inventory and look. Additionally you can tag rooms with the 'name' command and alias commands with 'alias'. Have fun! `; }

function welcome () {

 console.log(`Welcome to the dungeon!

Grab the sledge and make your way to room 1,1,5 for a non-existant prize! `);

 console.log(help());

}

///////////// // ACTIONS // /////////////

function gotoRoom (direction, world) {

 const wantedRoom = zipWith(world.player.location, DIRECTIONS[direction], add),
       room       = find(world.rooms, r => isEqual(first(r), world.player.location));
 if (direction === 'u' && !contains(room[2], 'ladder')) {
   console.log("You can't go upwards without a ladder!");
 } else {
   if (find(world.rooms, r => isEqual(first(r), wantedRoom))) {
     world.player.location = wantedRoom;
     look(world);
   } else {
     console.log("There's no exit in that direction!");
   }
 }
 return world

}

function dig (world, [direction]) {

 let dir,
     dirLongName,
     wantedRoom;
 if (!direction) {
   console.log('Where do you want to dig?');
   return world
 }
 if (!(dir = findDirection(world, direction))) {
     console.log('That is not a direction I recognize');
     return world;
 }
 if (!contains(world.player.inventory, 'sledge')) {
   console.log('With your bare hands?');
   return world;
 }
 dirLongName = findDirection(world, direction, true);
 wantedRoom  = zipWith(world.player.location, DIRECTIONS[dir], add);
 if (find(world.rooms, r => isEqual(first(r), wantedRoom))) {
   console.log('There is already an exit, there!');
   return world;
 }
 world.rooms.push(makeRoom(wantedRoom));
 console.log(`There is now an exit ${dirLongName}ward`);
 return world;

}

function look (world) {

 const room = find(world.rooms, r => isEqual(first(r), world.player.location)),
       itemsOnFloor    = itemsToDescriptiveItems(room[2]),
       itemsOnFloorStr = isEmpty(itemsOnFloor)
                         ? 
                         : (itemsOnFloor.length === 1
                           ? itemsOnFloor[0]
                           : listToDescriptiveString(itemsOnFloor)),
       exits = chain(DIRECTIONS)
               .map((d, kd) => [kd, zipWith(d, first(room), add)])
               .filter(r => find(world.rooms, rr => isEqual(first(rr), r[1])))
               .map(d => find(world.commands, c => {
                 return contains(first(c), first(d))
               })[0][1])
               .value(),
       exitsStr = isEmpty(exits)
                  ? 
                  : (exits.length === 1
                    ? `There is one exit: ${exits[0]}.`
                    : `You can see the following exits: ${listToDescriptiveString(exits)}.`);
 console.log(
   (room[1] ? room[1] : `Room at ${world.player.location[0]}, ${world.player.location[1]}, ${world.player.location[2]}`) + '\n' +
   (!itemsOnFloorStr ?  : `On the floor you can see ${itemsOnFloorStr}.\n`) +
   exitsStr
 );
 return world;

}

function inventory (world) {

 if (world.player.inventory.length === 0) {
   console.log('You are not carrying anything');
 } else {
   console.log(`You are carrying: ${listToDescriptiveString(itemsToDescriptiveItems(world.player.inventory))}`);
 }
 return world;

}

function take (world, [item]) {

 const room = find(world.rooms, r => isEqual(first(r), world.player.location))
 if (!item) {
   console.log('Take what?');
   return world;
 }
 if (item === 'all') {
   if (isEmpty(room[2])) {
     console.log('There is nothing to take here');
   } else{
     world.player.inventory = uniq(world.player.inventory.concat(room[2] || []));
     room[2] = [];
     console.log('All items taken');
   }
   return world;
 }
 if (!contains(room[2], item)) {
   console.log("You can't see anything like that here");
   return world;
 }
 world.player.inventory = uniq(world.player.inventory.concat([item]));
 pull(room[2], item);
 console.log('Taken');
 return world

}

function drop (world, [item]) {

 const room = find(world.rooms, r => isEqual(first(r), world.player.location))
 if (!item) {
   console.log('Drop what?');
   return world;
 }
 if (item === 'all') {
   if (isEmpty(world.player.inventory)) {
     console.log('You have nothing to drop');
   } else{
     room[2] = uniq(room[2].concat(world.player.inventory || []));
     world.player.inventory = [];
     console.log('All items dropped');
   }
   return world;
 }
 if (!contains(world.player.inventory, item)) {
   console.log("You don't have that item");
   return world;
 }
 room[2] = uniq(room[2].concat([item]));
 pull(world.player.inventory, item);
 console.log('Dropped');
 return world

}

function equip (world, [item]) {

 if (!item) {
   console.log('What do you want to equip?');
   return world;
 }
 if (!contains(world.player.inventory, item)) {
   console.log("You don't have such object");
   return world;
 }
 world.player.equipped = item;
 console.log('Item equipped!');
 return world;

}

function unequip (world, [item]) {

 if (!item) {
   console.log('What do you want to unequip?');
   return world;
 }
 if (item !== world.player.equipped) {
   console.log("You don't have it equipped");
   return world;
 }
 world.player.equipped = null;
 console.log('Item unequipped!');
 return world;

}

function alias (world, [cmd, al]) {

 let foundCommand;
 if (!cmd && !al) {
   console.log('Aliases:' + chain(world.commands)
               .filter(c => first(c).length > 1)
               .map(first)
               .reduce((acc, c) => `${acc}\n${first(c)} => ${rest(c).join(', ')}`, )
               .value());
   return world;
 }
 if (!(foundCommand = find(world.commands, c => contains(first(c), cmd)))) {
   console.log('There is no such command');
   return world
 }
 if (!al) {
   let aliases = reject(first(foundCommand), c => c === cmd);
   if (isEmpty(aliases)) {
     console.log(`There are no aliases for ${cmd}`);
   } else {
     console.log(`Aliases for "${cmd}":  ${aliases.join(', ')}`);
   }
   return world;
 }
 foundCommand[0] = uniq(first(foundCommand).concat([al]));
 console.log('Alias assigned');
 return world;

}

////////////////////// // INPUT PROCESSING // //////////////////////

function processInput(input, world) {

 const splitInput         = trim(input).split(/\s+/g),
       [command, options] = [first(splitInput), rest(splitInput)],
       commandFn          = find(world.commands, c => contains(first(c), command.toLowerCase()));
 if (commandFn) {
   return commandFn[1](world, options);
 } else {
   console.log("I don't know what you mean.");
   return world;
 }

}

/////////////// // MAIN LOOP // ///////////////

(function runGame () {

 let world = {
   rooms: [
     [[0, 0, 0],  "The room where it all started...", ['ladder', 'sledge']],
     [[1, 1 , 5], "You found it! Lots of gold!"]
   ],
   commands: [
     [['n', 'north'], partial(gotoRoom, 'n')],
     [['s', 'south'], partial(gotoRoom, 's')],
     [['w', 'west'],  partial(gotoRoom, 'w')],
     [['e', 'east'],  partial(gotoRoom, 'e')],
     [['d', 'down'],  partial(gotoRoom, 'd')],
     [['u', 'up'],    partial(gotoRoom, 'u')],
     [['help'],           help],
     [['dig'],            dig],
     [['l', 'look'],      look],
     [['i', 'inventory'], inventory],
     [['take'],           take],
     [['drop'],           drop],
     [['equip'],          equip],
     [['unequip'],        unequip],
     [['alias'],          alias]
   ],
   player: {
     location:  [0, 0, 0],
     inventory: ['sledge'],
     equipped:  null
   }
 };
 welcome();
 process.stdin.resume();
 process.stdin.setEncoding('utf8');
 process.stdin.on('data', input => world = processInput(input, world))

})(); </lang>