El laboratorio cerrado 1: salas y comandos
En resumen: Un juego de escape de texto: las salas y los objetos son objetos del programa, las salidas están en un Map, el inventario en un array, cada comando es una clase y un comando desconocido es una excepción.
Problema
Encerrado en el laboratorio
Te quedaste hasta tarde en el laboratorio. El sistema de seguridad cerró las puertas automáticamente. Tienes que salir antes de que lleguen los guardias por la mañana. No hay peleas, solo salas, objetos y acertijos.
El juego es de texto. El jugador escribe comandos y el juego responde:
> ir norte[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> tomar llaveAquí no hay llave.La primera idea es un if enorme: si el comando es «ir», haz esto; si es «tomar», haz aquello. Crece con cada comando nuevo, y un error en una rama rompe las demás. En esta lección los comandos se convierten en clases separadas, y las salas y los objetos, en objetos dentro de colecciones.
La idea
Salas y objetos
Un objeto del juego tiene un nombre y una descripción:
class Item { private String name; private String description;
public Item(String name, String description) { this.name = name; this.description = description; }
public String getName() { return name; }
public String getDescription() { return description; }}class Item{ public string Name { get; } public string Description { get; }
public Item(string name, string description) { Name = name; Description = description; }}class Item: def __init__(self, name, description): self._name = name self._description = description
@property def name(self): return self._name
@property def description(self): return self._descriptionclass Item { #name; #description;
constructor(name, description) { this.#name = name; this.#description = description; }
get name() { return this.#name; }
get description() { return this.#description; }}Una sala tiene un nombre, una descripción, salidas y objetos. Las salidas son un Map: la dirección es la clave y la sala siguiente es el valor. Los objetos son una lista, porque cuántos hay cambia:
private static final String[] DIRECTIONS = { "norte", "sur", "este", "oeste" };
private String name;private String description;private Map<String, Room> exits = new HashMap<>();private List<Item> items = new ArrayList<>();private static readonly string[] Directions = { "norte", "sur", "este", "oeste" };
public string Name { get; }public string Description { get; }private Dictionary<string, Room> exits = new Dictionary<string, Room>();private List<Item> items = new List<Item>();DIRECTIONS = ("norte", "sur", "este", "oeste")
def __init__(self, name, description): self._name = name self._description = description self._exits = {} self._items = []static DIRECTIONS = ["norte", "sur", "este", "oeste"];
#name;#description;#exits = new Map();#items = [];public void addExit(String direction, Room room) { exits.put(direction, room);}
public Room getExit(String direction) { return exits.get(direction);}public void AddExit(string direction, Room room){ exits[direction] = room;}
public Room GetExit(string direction){ if (exits.ContainsKey(direction)) { return exits[direction]; } return null;}def add_exit(self, direction, room): self._exits[direction] = room
def get_exit(self, direction): return self._exits.get(direction)addExit(direction, room) { this.#exits.set(direction, room);}
getExit(direction) { return this.#exits.get(direction);}get en un Map devuelve undefined si la clave no está. Así que «no hay salida por ahí» es simplemente undefined. DIRECTIONS es static: compartida por todas las salas, y se lee como Room.DIRECTIONS.
Míralo
Qué hay en la sala
Tomar un objeto lo quita de la sala y lo devuelve. Si no está, devuelve null:
public void addItem(Item item) { items.add(item);}
public Item takeItem(String name) { for (int i = 0; i < items.size(); i++) { if (items.get(i).getName().equals(name)) { return items.remove(i); } } return null;}public void AddItem(Item item){ items.Add(item);}
public Item TakeItem(string name){ for (int i = 0; i < items.Count; i++) { if (items[i].Name == name) { Item item = items[i]; items.RemoveAt(i); return item; } } return null;}def add_item(self, item): self._items.append(item)
def take_item(self, name): for item in self._items: if item.name == name: self._items.remove(item) return item return NoneaddItem(item) { this.#items.push(item);}
takeItem(name) { for (let i = 0; i < this.#items.length; i++) { if (this.#items[i].name === name) { return this.#items.splice(i, 1)[0]; } } return null;}La descripción reúne los nombres de los objetos y las salidas. Las direcciones van en un orden fijo (norte, sur, este, oeste), para que el texto no dependa del orden del diccionario:
public String describe() { String text = "[" + name + "] " + description; if (!items.isEmpty()) { List<String> names = new ArrayList<>(); for (Item item : items) { names.add(item.getName()); } text += "\nVes: " + String.join(", ", names); } List<String> ways = new ArrayList<>(); for (String direction : DIRECTIONS) { if (exits.containsKey(direction)) { ways.add(direction); } } return text + "\nSalidas: " + String.join(", ", ways);}public string Describe(){ string text = "[" + Name + "] " + Description; if (items.Count > 0) { List<string> names = new List<string>(); foreach (Item item in items) { names.Add(item.Name); } text += "\nVes: " + string.Join(", ", names); } List<string> ways = new List<string>(); foreach (string direction in Directions) { if (exits.ContainsKey(direction)) { ways.Add(direction); } } return text + "\nSalidas: " + string.Join(", ", ways);}def describe(self): text = f"[{self._name}] {self._description}" if self._items: names = [] for item in self._items: names.append(item.name) text += "\nVes: " + ", ".join(names) ways = [] for direction in Room.DIRECTIONS: if direction in self._exits: ways.append(direction) return text + "\nSalidas: " + ", ".join(ways)describe() { let text = `[${this.#name}] ${this.#description}`; if (this.#items.length > 0) { const names = []; for (const item of this.#items) { names.push(item.name); } text += `\nVes: ${names.join(", ")}`; } const ways = []; for (const direction of Room.DIRECTIONS) { if (this.#exits.has(direction)) { ways.push(direction); } } return `${text}\nSalidas: ${ways.join(", ")}`;}Pruébalo
¿Salida o undefined?
¿Qué imprimirá el programa?
const hall = new Room("pasillo", "Un pasillo largo.");const store = new Room("almacén", "Estantes con cajas.");hall.addExit("este", store);store.addExit("oeste", hall);store.addItem(new Item("llave", "Una llave de latón."));console.log(hall.getExit("este").name);console.log(hall.getExit("oeste") === undefined);console.log(store.takeItem("llave") !== null);console.log(store.takeItem("llave") !== null);Al este del pasillo está el almacén. Al oeste del pasillo no hay salida, así que es undefined. La primera vez que tomas la llave, encuentra el objeto y lo quita de la lista. La segunda vez no encuentra nada y devuelve null.
La idea
Cada comando es una clase
Todos los comandos siguen un mismo contrato: reciben el juego y la palabra que viene después del comando, y devuelven el texto que ve el jugador:
interface Command { String execute(Game game, String argument);}interface ICommand{ string Execute(Game game, string argument);}class Command(ABC): @abstractmethod def execute(self, game, argument): pass// JavaScript no tiene interface. El contrato de «orden» es un acuerdo:// cada orden es una clase con un método execute(game, argument)// que devuelve el texto que ve el jugador.JavaScript no tiene interfaces. El contrato es un acuerdo: cada clase de comando tiene un método execute(game, argument). El juego llama a este método y no pregunta de qué clase es el objeto.
Tomar e ir son dos clases pequeñas. Cada una sabe solo lo suyo:
class TakeCommand implements Command { @Override public String execute(Game game, String argument) { if (argument.isEmpty()) { return "¿Qué tomo?"; } Item item = game.getCurrent().takeItem(argument); if (item == null) { return "Aquí no hay " + argument + "."; } game.getInventory().add(item); return "Tomas: " + item.getName() + ". " + item.getDescription(); }}class TakeCommand : ICommand{ public string Execute(Game game, string argument) { if (argument == "") { return "¿Qué tomo?"; } Item item = game.Current.TakeItem(argument); if (item == null) { return "Aquí no hay " + argument + "."; } game.Inventory.Add(item); return "Tomas: " + item.Name + ". " + item.Description; }}class TakeCommand(Command): def execute(self, game, argument): if argument == "": return "¿Qué tomo?" item = game.current.take_item(argument) if item is None: return f"Aquí no hay {argument}." game.inventory.append(item) return f"Tomas: {item.name}. {item.description}"class TakeCommand { execute(game, argument) { if (argument === "") { return "¿Qué tomo?"; } const item = game.current.takeItem(argument); if (item === null) { return `Aquí no hay ${argument}.`; } game.inventory.push(item); return `Tomas: ${item.name}. ${item.description}`; }}class GoCommand implements Command { @Override public String execute(Game game, String argument) { Room next = game.getCurrent().getExit(argument); if (next == null) { return "Por ahí no hay salida."; } game.setCurrent(next); return next.describe(); }}class GoCommand : ICommand{ public string Execute(Game game, string argument) { Room next = game.Current.GetExit(argument); if (next == null) { return "Por ahí no hay salida."; } game.Current = next; return next.Describe(); }}class GoCommand(Command): def execute(self, game, argument): next_room = game.current.get_exit(argument) if next_room is None: return "Por ahí no hay salida." game.current = next_room return next_room.describe()class GoCommand { execute(game, argument) { const next = game.current.getExit(argument); if (next === undefined) { return "Por ahí no hay salida."; } game.current = next; return next.describe(); }}Los comandos no imprimen, devuelven texto. Así el juego decide dónde mostrarlo, y puedes probar un comando sin mirar la pantalla.
La idea
Un diccionario de comandos
El juego guarda los comandos en un Map: la palabra es la clave y el objeto comando es el valor. Añadir un comando es una sola línea:
private Map<String, Room> rooms;private Map<String, Command> commands = new HashMap<>();private List<Item> inventory = new ArrayList<>();private Room current;
public Game() { rooms = buildWorld(); current = rooms.get("laboratorio"); commands.put("mirar", new LookCommand()); commands.put("tomar", new TakeCommand()); commands.put("ir", new GoCommand());}private Dictionary<string, Room> rooms;private Dictionary<string, ICommand> commands = new Dictionary<string, ICommand>();
public Room Current { get; set; }public List<Item> Inventory { get; } = new List<Item>();
public Game(){ rooms = BuildWorld(); Current = rooms["laboratorio"]; commands["mirar"] = new LookCommand(); commands["tomar"] = new TakeCommand(); commands["ir"] = new GoCommand();}def __init__(self): self._rooms = self._build_world() self._current = self._rooms["laboratorio"] self._inventory = [] self._commands = { "mirar": LookCommand(), "tomar": TakeCommand(), "ir": GoCommand(), }#rooms;#commands = new Map();#inventory = [];#current;
constructor() { this.#rooms = this.#buildWorld(); this.#current = this.#rooms.get("laboratorio"); this.#commands.set("mirar", new LookCommand()); this.#commands.set("tomar", new TakeCommand()); this.#commands.set("ir", new GoCommand());}handle quita los espacios de los extremos, pasa la línea a minúsculas y la divide en palabras. La primera palabra es el comando y la segunda es el argumento. Si la palabra no está en el diccionario, #find lanza su propia excepción, y handle la captura y dice «No entiendo»:
public String handle(String line) { String[] words = line.trim().toLowerCase().split(" "); String argument = words.length > 1 ? words[1] : ""; try { Command command = find(words[0]); return command.execute(this, argument); } catch (UnknownCommandException e) { return "No entiendo «" + e.getVerb() + "»."; }}
private Command find(String verb) throws UnknownCommandException { Command command = commands.get(verb); if (command == null) { throw new UnknownCommandException(verb); } return command;}public string Handle(string line){ string[] words = line.Trim().ToLower().Split(' '); string argument = words.Length > 1 ? words[1] : ""; try { ICommand command = Find(words[0]); return command.Execute(this, argument); } catch (UnknownCommandException e) { return "No entiendo «" + e.Verb + "»."; }}
private ICommand Find(string verb){ if (!commands.ContainsKey(verb)) { throw new UnknownCommandException(verb); } return commands[verb];}def handle(self, line): words = line.strip().lower().split(" ") argument = words[1] if len(words) > 1 else "" try: command = self._find(words[0]) return command.execute(self, argument) except UnknownCommandError as e: return f"No entiendo «{e.verb}»."
def _find(self, verb): command = self._commands.get(verb) if command is None: raise UnknownCommandError(verb) return commandhandle(line) { const words = line.trim().toLowerCase().split(" "); const argument = words.length > 1 ? words[1] : ""; try { const command = this.#find(words[0]); return command.execute(this, argument); } catch (e) { if (!(e instanceof UnknownCommandError)) { throw e; } return `No entiendo «${e.verb}».`; }}
#find(verb) { const command = this.#commands.get(verb); if (command === undefined) { throw new UnknownCommandError(verb); } return command;}class UnknownCommandException extends Exception { private String verb;
public UnknownCommandException(String verb) { super("orden desconocida: " + verb); this.verb = verb; }
public String getVerb() { return verb; }}class UnknownCommandException : Exception{ public string Verb { get; }
public UnknownCommandException(string verb) : base("orden desconocida: " + verb) { Verb = verb; }}class UnknownCommandError(Exception): def __init__(self, verb): super().__init__(f"orden desconocida: {verb}") self.verb = verbclass UnknownCommandError extends Error { constructor(verb) { super(`orden desconocida: ${verb}`); this.name = "UnknownCommandError"; this.verb = verb; }}catch en JavaScript captura cualquier error, no solo UnknownCommandError. Por eso handle comprueba con instanceof y vuelve a lanzar todo lo demás: un error del propio programa no debe parecer un «No entiendo».
Más precisamente, para curiosos
- El juego sabe solo que el comando tiene
execute. El objeto del diccionario decide quéexecutese ejecuta. Es el polimorfismo del módulo de herencia, salvo que los objetos están en un diccionario y no en un array. - Otra forma es un
switchsobre la palabra. Para cuatro comandos está bien. Para veinte, con reglas distintas cada uno, las clases separadas se leen y se cambian con más facilidad. split(/\s+/), con una expresión regular, convierte varios espacios seguidos en un solo separador. Aquí un espacio es suficiente.
console.log("go norte".split(" "));console.log("go norte".split(/\s+/));[ 'ir', '', 'norte' ][ 'ir', 'norte' ]Pruébalo
Encuentra el error
El jugador escribe «Mirar» con mayúscula, y el juego responde No entiendo «Mirar». ¿En qué línea está el error?
Elige la línea con el error.
Las claves del diccionario están en minúsculas, y «Mirar» no es «mirar». La línea debería ser line.trim().toLowerCase().split(" "). Entonces se entiende cada comando, escriba como escriba el jugador.
Míralo
Un primer recorrido
El programa muestra la primera sala y después lee comando tras comando hasta que se acaba la entrada. Antes de cada comando imprime >, para que se vea qué escribió el jugador:
Game game = new Game();System.out.println(game.getCurrent().describe());Scanner in = new Scanner(System.in);while (in.hasNextLine()) { String line = in.nextLine(); System.out.println("> " + line); System.out.println(game.handle(line));}[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Ves: linternaSalidas: norte> Tomar linternaTomas: linterna. Funciona y la batería está casi llena.> mirar[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Salidas: norte> tomar linternaAquí no hay linterna.> bailarNo entiendo «bailar».> ir norte[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ir este[almacén] Estantes con cajas hasta el techo. Un calendario en la pared dice: «Inventario el 19 de octubre».Ves: llaveSalidas: oeste> tomar llaveTomas: llave. Una llavecita de latón. Su etiqueta dice «oficina».> ir oeste[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ir arribaPor ahí no hay salida.> ir oeste[oficina] Un escritorio, una silla y un archivero lleno de carpetas.Ves: notaSalidas: este> tomar notaTomas: nota. Código del vestíbulo: 47 y el día del inventario.Game game = new Game();Console.WriteLine(game.Current.Describe());string line = Console.ReadLine();while (line != null){ Console.WriteLine("> " + line); Console.WriteLine(game.Handle(line)); line = Console.ReadLine();}[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Ves: linternaSalidas: norte> Tomar linternaTomas: linterna. Funciona y la batería está casi llena.> mirar[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Salidas: norte> tomar linternaAquí no hay linterna.> bailarNo entiendo «bailar».> ir norte[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ir este[almacén] Estantes con cajas hasta el techo. Un calendario en la pared dice: «Inventario el 19 de octubre».Ves: llaveSalidas: oeste> tomar llaveTomas: llave. Una llavecita de latón. Su etiqueta dice «oficina».> ir oeste[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ir arribaPor ahí no hay salida.> ir oeste[oficina] Un escritorio, una silla y un archivero lleno de carpetas.Ves: notaSalidas: este> tomar notaTomas: nota. Código del vestíbulo: 47 y el día del inventario.game = Game()print(game.current.describe())for line in sys.stdin: line = line.rstrip("\n") print(f"> {line}") print(game.handle(line))[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Ves: linternaSalidas: norte> Tomar linternaTomas: linterna. Funciona y la batería está casi llena.> mirar[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Salidas: norte> tomar linternaAquí no hay linterna.> bailarNo entiendo «bailar».> ir norte[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ir este[almacén] Estantes con cajas hasta el techo. Un calendario en la pared dice: «Inventario el 19 de octubre».Ves: llaveSalidas: oeste> tomar llaveTomas: llave. Una llavecita de latón. Su etiqueta dice «oficina».> ir oeste[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ir arribaPor ahí no hay salida.> ir oeste[oficina] Un escritorio, una silla y un archivero lleno de carpetas.Ves: notaSalidas: este> tomar notaTomas: nota. Código del vestíbulo: 47 y el día del inventario.const game = new Game();console.log(game.current.describe());let line = await ask("");while (line !== undefined) { console.log(`> ${line}`); console.log(game.handle(line)); line = await ask("");}rl.close();[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Ves: linternaSalidas: norte> Tomar linternaTomas: linterna. Funciona y la batería está casi llena.> mirar[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Salidas: norte> tomar linternaAquí no hay linterna.> bailarNo entiendo «bailar».> ir norte[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ir este[almacén] Estantes con cajas hasta el techo. Un calendario en la pared dice: «Inventario el 19 de octubre».Ves: llaveSalidas: oeste> tomar llaveTomas: llave. Una llavecita de latón. Su etiqueta dice «oficina».> ir oeste[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ir arribaPor ahí no hay salida.> ir oeste[oficina] Un escritorio, una silla y un archivero lleno de carpetas.Ves: notaSalidas: este> tomar notaTomas: nota. Código del vestíbulo: 47 y el día del inventario.ask es la función auxiliar de entrada del módulo «Fundamentos del lenguaje». Cuando la entrada se acaba, devuelve undefined, y el bucle se detiene.
Con esta entrada:
Tomar linternamirartomar linternabailarir norteir estetomar llaveir oesteir arribair oestetomar notaLa nota dice que el código del vestíbulo es 47 y el día del inventario. El calendario del almacén dice 19. Pero todavía no hay vestíbulo, y la oficina tampoco está cerrada con llave. Los acertijos llegan en la siguiente lección.
const game = new Game();console.log(game.current.describe());let line = await ask("");while (line !== undefined) { console.log(`> ${line}`); console.log(game.handle(line)); line = await ask("");}rl.close();Paso a paso, en palabras
- El juego empieza en la sala «laboratorio». Los comandos están en un diccionario: la palabra es la clave y el objeto comando es el valor.
- Escribes «Tomar linterna». La línea pasa a minúsculas. «tomar» es una clave del diccionario, y el comando es TakeCommand. El objeto «linterna» pasa de la sala a la mochila.
- Escribes «mirar». «mirar» es una clave del diccionario, y el comando es LookCommand. Describe la sala otra vez, ahora sin el objeto que tomaste.
- Escribes «tomar linterna». «tomar» es una clave del diccionario, y el comando es TakeCommand. Ya no hay «linterna» en la sala, así que la respuesta es «Aquí no hay».
- Escribes «bailar». «bailar» no está en el diccionario: #find lanza UnknownCommandError, y handle dice «No entiendo».
- Escribes «ir norte». «ir» es una clave del diccionario, y el comando es GoCommand. getExit("norte") en la sala «laboratorio» da la sala «pasillo».
- Escribes «ir este». «ir» es una clave del diccionario, y el comando es GoCommand. getExit("este") en la sala «pasillo» da la sala «almacén».
- Escribes «tomar llave». «tomar» es una clave del diccionario, y el comando es TakeCommand. El objeto «llave» pasa de la sala a la mochila.
- Escribes «ir oeste». «ir» es una clave del diccionario, y el comando es GoCommand. getExit("oeste") en la sala «almacén» da la sala «pasillo».
- Escribes «ir arriba». «ir» es una clave del diccionario, y el comando es GoCommand. Las salidas de la sala «pasillo» no tienen la clave «arriba»: por ahí no hay salida.
- Escribes «ir oeste». «ir» es una clave del diccionario, y el comando es GoCommand. getExit("oeste") en la sala «pasillo» da la sala «oficina».
- Escribes «tomar nota». «tomar» es una clave del diccionario, y el comando es TakeCommand. El objeto «nota» pasa de la sala a la mochila.
- La entrada se acabó, y el bucle se detiene. La mochila: linterna, llave, nota.
Ejercicio
Más comandos y salas
Resuelve las tareas en tu editor, con las clases de la lección. Añade lo nuevo desde fuera, con game.addCommand y game.getRoom, sin tocar Game.
1. Inventario. Un comando «inventario» dice qué hay en la mochila o que está vacía.
inventariotomar linternair norteir estetomar llaveinventario[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Ves: linternaSalidas: norte> inventarioTu mochila está vacía.> tomar linternaTomas: linterna. Funciona y la batería está casi llena.> ir norte[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ir este[almacén] Estantes con cajas hasta el techo. Un calendario en la pared dice: «Inventario el 19 de octubre».Ves: llaveSalidas: oeste> tomar llaveTomas: llave. Una llavecita de latón. Su etiqueta dice «oficina».> inventarioEn la mochila: linterna, llave2. Una sala nueva. Una sala de servidores al norte del almacén, con un cable de red dentro. Su descripción es «Racks con luces intermitentes. Los ventiladores zumban.»
ir norteir esteir nortetomar cableir sur[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Ves: linternaSalidas: norte> ir norte[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ir este[almacén] Estantes con cajas hasta el techo. Un calendario en la pared dice: «Inventario el 19 de octubre».Ves: llaveSalidas: norte, oeste> ir norte[sala de servidores] Racks con luces intermitentes. Los ventiladores zumban.Ves: cableSalidas: sur> tomar cableTomas: cable. Un cable de red largo.> ir sur[almacén] Estantes con cajas hasta el techo. Un calendario en la pared dice: «Inventario el 19 de octubre».Ves: llaveSalidas: norte, oeste3. Sinónimos. Haz que «agarrar» signifique «tomar», «caminar» signifique «ir» y «ver» signifique «mirar». Pista: el mismo comando puede ser el valor de varias claves.
agarrar linternacaminar nortever[laboratorio] Las mesas están llenas de matraces. La puerta a tus espaldas se bloqueó sola.Ves: linternaSalidas: norte> agarrar linternaTomas: linterna. Funciona y la batería está casi llena.> caminar norte[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oeste> ver[pasillo] Un pasillo largo. La luz del techo parpadea.Salidas: sur, este, oesteMás tareas
- Soltar. Un comando «soltar llave» devuelve el objeto de la mochila a la sala.
- Ayuda. Un comando «ayuda» enumera todas las palabras del diccionario de comandos.
- Direcciones cortas. Haz que «n», «s», «e», «o» signifiquen norte, sur, este, oeste.
Test
Ponte a prueba
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Excepciones · Lanzar excepciones y crear las tuyas · Arrays: push, splice y sort · Map y Set
- Ejercicios sobre el tema: Hagamos un juego