Заключената лаборатория 2: загадки и изход
Проблем
Всички врати са отворени
В първия урок лабораторията е заключена само на думи: играчът обикаля където си иска. Ключът и бележката си стоят, но не служат за нищо. Играта няма край.
Ескейп играта има нужда от:
- заключена врата, която се отваря с предмет;
- кодова ключалка, чийто код се сглобява от улики в различни стаи;
- край: играчът е навън, и брой ходове, с които да се похвали;
- код за продължение: играта е дълга и играчът иска да спре и да продължи после.
Кодът за продължение е кратък текст. Играта го печата, а играчът го пише обратно. Ако текстът е развален, играта трябва да го каже, а не да се срине.
Идеята
Заключена врата
Стаята получава ключалка. Тайната е името на предмета, който я отваря, или кодът:
private String secret = null;private boolean codeLock = false;private boolean locked = false;
public void lockWithKey(String keyName) { secret = keyName; codeLock = false; locked = true;}
public void lockWithCode(String code) { secret = code; codeLock = true; locked = true;}
public boolean isLocked() { return locked;}
public boolean isCodeLock() { return codeLock;}
public boolean opensWith(String attempt) { return locked && secret.equals(attempt);}
public void unlock() { locked = false;}private string secret = null;
public bool IsLocked { get; private set; } = false;public bool IsCodeLock { get; private set; } = false;
public void LockWithKey(string keyName){ secret = keyName; IsCodeLock = false; IsLocked = true;}
public void LockWithCode(string code){ secret = code; IsCodeLock = true; IsLocked = true;}
public bool OpensWith(string attempt){ return IsLocked && secret == attempt;}
public void Unlock(){ IsLocked = false;}def __init__(self, name, description): self._name = name self._description = description self._exits = {} self._items = [] self._secret = None self._code_lock = False self._locked = False
def lock_with_key(self, key_name): self._secret = key_name self._code_lock = False self._locked = True
def lock_with_code(self, code): self._secret = code self._code_lock = True self._locked = True
@propertydef is_locked(self): return self._locked
@propertydef is_code_lock(self): return self._code_lock
def opens_with(self, attempt): return self._locked and self._secret == attempt
def unlock(self): self._locked = False#secret = null;#codeLock = false;#locked = false;
lockWithKey(keyName) { this.#secret = keyName; this.#codeLock = false; this.#locked = true;}
lockWithCode(code) { this.#secret = code; this.#codeLock = true; this.#locked = true;}
get isLocked() { return this.#locked;}
get isCodeLock() { return this.#codeLock;}
opensWith(attempt) { return this.#locked && this.#secret === attempt;}
unlock() { this.#locked = false;}„иди“ вече спира пред заключена врата:
class GoCommand implements Command { @Override public String execute(Game game, String argument) { Room next = game.getCurrent().getExit(argument); if (next == null) { return "Натам няма изход."; } if (next.isLocked()) { return "Вратата към " + next.getName() + " е заключена."; } 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 "Натам няма изход."; } if (next.IsLocked) { return "Вратата към " + next.Name + " е заключена."; } 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 "Натам няма изход." if next_room.is_locked: return f"Вратата към {next_room.name} е заключена." game.current = next_room return next_room.describe()class GoCommand { execute(game, argument) { const next = game.current.getExit(argument); if (next === undefined) { return "Натам няма изход."; } if (next.isLocked) { return `Вратата към ${next.name} е заключена.`; } game.current = next; return next.describe(); }}В света кабинетът е заключен с ключа от склада, а новото фоайе с код:
Room foyer = new Room("фоайе", "Стъклената врата към улицата е отворена.");corridor.addExit("север", foyer);foyer.addExit("юг", corridor);office.lockWithKey("ключ");foyer.lockWithCode("4719");Room foyer = new Room("фоайе", "Стъклената врата към улицата е отворена.");corridor.AddExit("север", foyer);foyer.AddExit("юг", corridor);office.LockWithKey("ключ");foyer.LockWithCode("4719");foyer = Room("фоайе", "Стъклената врата към улицата е отворена.")corridor.add_exit("север", foyer)foyer.add_exit("юг", corridor)office.lock_with_key("ключ")foyer.lock_with_code("4719")const foyer = new Room("фоайе", "Стъклената врата към улицата е отворена.");corridor.addExit("север", foyer);foyer.addExit("юг", corridor);office.lockWithKey("ключ");foyer.lockWithCode("4719");Идеята
Ключът отключва
„използвай ключ“ проверява раницата, после търси заключена съседна врата, която се отваря с този предмет:
class UseCommand implements Command { @Override public String execute(Game game, String argument) { if (!game.has(argument)) { return "Нямаш " + argument + "."; } for (Room room : game.getCurrent().getNeighbours()) { if (!room.isCodeLock() && room.opensWith(argument)) { game.unlock(room); return "Отключи вратата към " + room.getName() + "."; } } return "Тук " + argument + " не помага."; }}class UseCommand : ICommand{ public string Execute(Game game, string argument) { if (!game.Has(argument)) { return "Нямаш " + argument + "."; } foreach (Room room in game.Current.GetNeighbours()) { if (!room.IsCodeLock && room.OpensWith(argument)) { game.Unlock(room); return "Отключи вратата към " + room.Name + "."; } } return "Тук " + argument + " не помага."; }}class UseCommand(Command): def execute(self, game, argument): if not game.has(argument): return f"Нямаш {argument}." for room in game.current.get_neighbours(): if not room.is_code_lock and room.opens_with(argument): game.unlock(room) return f"Отключи вратата към {room.name}." return f"Тук {argument} не помага."class UseCommand { execute(game, argument) { if (!game.has(argument)) { return `Нямаш ${argument}.`; } for (const room of game.current.getNeighbours()) { if (!room.isCodeLock && room.opensWith(argument)) { game.unlock(room); return `Отключи вратата към ${room.name}.`; } } return `Тук ${argument} не помага.`; }}Отключването минава през играта, а не направо през стаята. Така играта помни кои врати са отворени. Това ще трябва за кода за продължение.
Пробвай
Кой ключ, коя врата?
Играчът пише тези команди от началото на играта. Какво отговаря играта на трите „използвай ключ“, подред?
иди северизползвай ключиди изтоквземи ключизползвай ключиди западизползвай ключПървия път ключът още е в склада. Втория път е в раницата, но от склада кабинетът не се вижда: съсед е само коридорът. Третия път играчът е в коридора и вратата на запад е заключена точно с ключа.
Идеята
Кодова ключалка от улики
Фоайето е заключено с четири цифри. Играта не ги казва направо:
- бележката в кабинета: „Кодът на фоайето: 47 и денят на инвентаризацията.“;
- календарът в склада: „Инвентаризация – 19 октомври“.
Кодът е 4719. Команда „въведи“ го проверява срещу кодовата ключалка на съседна врата:
class CodeCommand implements Command { @Override public String execute(Game game, String argument) { for (Room room : game.getCurrent().getNeighbours()) { if (room.isCodeLock() && room.isLocked()) { if (room.opensWith(argument)) { game.unlock(room); return "Ключалката щракна. Вратата към " + room.getName() + " е отворена."; } return "Грешен код. Ключалката светва в червено."; } } return "Тук няма кодова ключалка."; }}class CodeCommand : ICommand{ public string Execute(Game game, string argument) { foreach (Room room in game.Current.GetNeighbours()) { if (room.IsCodeLock && room.IsLocked) { if (room.OpensWith(argument)) { game.Unlock(room); return "Ключалката щракна. Вратата към " + room.Name + " е отворена."; } return "Грешен код. Ключалката светва в червено."; } } return "Тук няма кодова ключалка."; }}class CodeCommand(Command): def execute(self, game, argument): for room in game.current.get_neighbours(): if room.is_code_lock and room.is_locked: if room.opens_with(argument): game.unlock(room) return f"Ключалката щракна. Вратата към {room.name} е отворена." return "Грешен код. Ключалката светва в червено." return "Тук няма кодова ключалка."class CodeCommand { execute(game, argument) { for (const room of game.current.getNeighbours()) { if (room.isCodeLock && room.isLocked) { if (room.opensWith(argument)) { game.unlock(room); return `Ключалката щракна. Вратата към ${room.name} е отворена.`; } return "Грешен код. Ключалката светва в червено."; } } return "Тук няма кодова ключалка."; }}Загадката е в данните, а не в кода: друг код и други улики правят друга игра със същите класове.
Виж го
Навън
Играта е спечелена, когато играчът стигне фоайето:
public boolean isWon() { return current.getName().equals("фоайе");}public bool IsWon(){ return Current.Name == "фоайе";}def is_won(self): return self._current.name == "фоайе"isWon() { return this.#current.name === "фоайе";}handle брои всяка разбрана команда като ход. Цикълът в програмата спира при победа:
Game game = new Game();System.out.println(game.getCurrent().describe());Scanner in = new Scanner(System.in);while (!game.isWon() && in.hasNextLine()) { String line = in.nextLine(); System.out.println("> " + line); System.out.println(game.handle(line));}if (game.isWon()) { System.out.println("Излезе на свобода за " + game.getMoves() + " хода.");}[лаборатория] Масите са пълни с колби. Вратата зад теб се е заключила сама.Тук има: фенерчеИзходи: север> иди север[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди западВратата към кабинет е заключена.> иди изток[склад] Рафтове с кашони до тавана. На стената има календар: „Инвентаризация – 19 октомври“.Тук има: ключИзходи: запад> вземи ключВзе ключ. Малък месингов ключ с етикет „кабинет“.> иди запад[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> използвай ключОтключи вратата към кабинет.> иди запад[кабинет] Бюро, стол и шкаф с папки.Тук има: бележкаИзходи: изток> вземи бележкаВзе бележка. „Кодът на фоайето: 47 и денят на инвентаризацията.“> иди изток[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди северВратата към фоайе е заключена.> въведи 1234Грешен код. Ключалката светва в червено.> въведи 4719Ключалката щракна. Вратата към фоайе е отворена.> иди север[фоайе] Стъклената врата към улицата е отворена.Изходи: югИзлезе на свобода за 13 хода.Game game = new Game();Console.WriteLine(game.Current.Describe());string line = Console.ReadLine();while (!game.IsWon() && line != null){ Console.WriteLine("> " + line); Console.WriteLine(game.Handle(line)); line = Console.ReadLine();}if (game.IsWon()){ Console.WriteLine("Излезе на свобода за " + game.Moves + " хода.");}[лаборатория] Масите са пълни с колби. Вратата зад теб се е заключила сама.Тук има: фенерчеИзходи: север> иди север[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди западВратата към кабинет е заключена.> иди изток[склад] Рафтове с кашони до тавана. На стената има календар: „Инвентаризация – 19 октомври“.Тук има: ключИзходи: запад> вземи ключВзе ключ. Малък месингов ключ с етикет „кабинет“.> иди запад[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> използвай ключОтключи вратата към кабинет.> иди запад[кабинет] Бюро, стол и шкаф с папки.Тук има: бележкаИзходи: изток> вземи бележкаВзе бележка. „Кодът на фоайето: 47 и денят на инвентаризацията.“> иди изток[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди северВратата към фоайе е заключена.> въведи 1234Грешен код. Ключалката светва в червено.> въведи 4719Ключалката щракна. Вратата към фоайе е отворена.> иди север[фоайе] Стъклената врата към улицата е отворена.Изходи: югИзлезе на свобода за 13 хода.game = Game()print(game.current.describe())for line in sys.stdin: line = line.rstrip("\n") print(f"> {line}") print(game.handle(line)) if game.is_won(): breakif game.is_won(): print(f"Излезе на свобода за {game.moves} хода.")[лаборатория] Масите са пълни с колби. Вратата зад теб се е заключила сама.Тук има: фенерчеИзходи: север> иди север[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди западВратата към кабинет е заключена.> иди изток[склад] Рафтове с кашони до тавана. На стената има календар: „Инвентаризация – 19 октомври“.Тук има: ключИзходи: запад> вземи ключВзе ключ. Малък месингов ключ с етикет „кабинет“.> иди запад[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> използвай ключОтключи вратата към кабинет.> иди запад[кабинет] Бюро, стол и шкаф с папки.Тук има: бележкаИзходи: изток> вземи бележкаВзе бележка. „Кодът на фоайето: 47 и денят на инвентаризацията.“> иди изток[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди северВратата към фоайе е заключена.> въведи 1234Грешен код. Ключалката светва в червено.> въведи 4719Ключалката щракна. Вратата към фоайе е отворена.> иди север[фоайе] Стъклената врата към улицата е отворена.Изходи: югИзлезе на свобода за 13 хода.const game = new Game();console.log(game.current.describe());let line = await ask("");while (!game.isWon() && line !== undefined) { console.log(`> ${line}`); console.log(game.handle(line)); line = await ask("");}rl.close();if (game.isWon()) { console.log(`Излезе на свобода за ${game.moves} хода.`);}[лаборатория] Масите са пълни с колби. Вратата зад теб се е заключила сама.Тук има: фенерчеИзходи: север> иди север[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди западВратата към кабинет е заключена.> иди изток[склад] Рафтове с кашони до тавана. На стената има календар: „Инвентаризация – 19 октомври“.Тук има: ключИзходи: запад> вземи ключВзе ключ. Малък месингов ключ с етикет „кабинет“.> иди запад[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> използвай ключОтключи вратата към кабинет.> иди запад[кабинет] Бюро, стол и шкаф с папки.Тук има: бележкаИзходи: изток> вземи бележкаВзе бележка. „Кодът на фоайето: 47 и денят на инвентаризацията.“> иди изток[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди северВратата към фоайе е заключена.> въведи 1234Грешен код. Ключалката светва в червено.> въведи 4719Ключалката щракна. Вратата към фоайе е отворена.> иди север[фоайе] Стъклената врата към улицата е отворена.Изходи: югИзлезе на свобода за 13 хода.С този вход:
иди севериди западиди изтоквземи ключиди западизползвай ключиди западвземи бележкаиди изтокиди севервъведи 1234въведи 4719иди северconst game = new Game();console.log(game.current.describe());let line = await ask("");while (!game.isWon() && line !== undefined) { console.log(`> ${line}`); console.log(game.handle(line)); line = await ask("");}rl.close();if (game.isWon()) { console.log(`Излезе на свобода за ${game.moves} хода.`);}Стъпка по стъпка, с думи
- Започваш в „лаборатория“. Вратата към „кабинет“ се отключва с ключ, а към „фоайе“ с код от четири цифри.
- Ход 1: „иди север“. Влизаш в „коридор“.
- Ход 2: „иди запад“. Вратата към „кабинет“ е заключена: „иди“ спира, преди да те премести.
- Ход 3: „иди изток“. Влизаш в „склад“. На стената: „Инвентаризация – 19 октомври“.
- Ход 4: „вземи ключ“. Взимаш ключ. С него се отключва „кабинет“.
- Ход 5: „иди запад“. Влизаш в „коридор“.
- Ход 6: „използвай ключ“. Ключ е в раницата и отваря заключената съседна врата към „кабинет“.
- Ход 7: „иди запад“. Влизаш в „кабинет“.
- Ход 8: „вземи бележка“. Взимаш бележка: „Кодът на фоайето: 47 и денят на инвентаризацията.“
- Ход 9: „иди изток“. Влизаш в „коридор“.
- Ход 10: „иди север“. Вратата към „фоайе“ е заключена: „иди“ спира, преди да те премести.
- Ход 11: „въведи 1234“. 1234 не е кодът. Ключалката светва в червено.
- Ход 12: „въведи 4719“. 47 от бележката и 19 от календара: 4719. Ключалката на „фоайе“ щракна.
- Ход 13: „иди север“. Влизаш в „фоайе“.
- Стигаш до „фоайе“: isWon() е true и цикълът спира. Излизаш за 13 хода.
Идеята
Код за продължение
Цялото състояние на играта е четири неща: къде е играчът, какво носи, кои врати е отворил и колко хода е изиграл. Те се събират в кратък текст, разделен с ;:
public String saveCode() { List<String> names = new ArrayList<>(); for (Item item : inventory) { names.add(item.getName()); } String items = names.isEmpty() ? "-" : String.join(",", names); String doors = unlocked.isEmpty() ? "-" : String.join(",", unlocked); return current.getName() + ";" + items + ";" + doors + ";" + moves;}public string SaveCode(){ List<string> names = new List<string>(); foreach (Item item in Inventory) { names.Add(item.Name); } string items = names.Count == 0 ? "-" : string.Join(",", names); string doors = unlocked.Count == 0 ? "-" : string.Join(",", unlocked); return Current.Name + ";" + items + ";" + doors + ";" + Moves;}def save_code(self): names = [] for item in self._inventory: names.append(item.name) items = ",".join(names) if names else "-" doors = ",".join(self._unlocked) if self._unlocked else "-" return f"{self._current.name};{items};{doors};{self._moves}"saveCode() { const names = []; for (const item of this.#inventory) { names.push(item.name); } const items = names.length === 0 ? "-" : names.join(","); const doors = this.#unlocked.length === 0 ? "-" : this.#unlocked.join(","); return `${this.#current.name};${items};${doors};${this.#moves}`;}Например кабинет;ключ,бележка;кабинет;9. Списъците са със запетаи, а празният списък е -, за да не остава празно място между два знака ;.
Играта печата кода, а играчът го пише обратно със „зареди“. Не е нужен файл: кодът може да се препише на лист или да се прати на приятел.
По-точно, за любопитните
Същият текст може да се запише и във файл, с наученото в модул „Текст и файлове“:
import { readFileSync, writeFileSync } from "node:fs";
writeFileSync("save.txt", game.saveCode());...later.load(readFileSync("save.txt", "utf8"));Идеята
Развален код
Играчът може да сбърка буква или да опита да излъже: да се появи в заключения кабинет. Тогава зареждането хвърля свое изключение:
class InvalidSaveCodeException extends Exception { public InvalidSaveCodeException(String message) { super(message); }}class InvalidSaveCodeException : Exception{ public InvalidSaveCodeException(string message) : base(message) { }}class InvalidSaveCodeError(Exception): passclass InvalidSaveCodeError extends Error { constructor(message) { super(message); this.name = "InvalidSaveCodeError"; }}load строи нов свят и проверява кода част по част върху него. Чак когато всичко е минало, новият свят става текущ:
public void load(String code) throws InvalidSaveCodeException { String[] parts = code.split(";"); if (parts.length != 4) { throw new InvalidSaveCodeException("трябват 4 части, разделени с ;"); } Map<String, Room> world = buildWorld(); Room room = world.get(parts[0]); if (room == null) { throw new InvalidSaveCodeException("няма стая " + parts[0]); } List<Item> items = new ArrayList<>(); if (!parts[1].equals("-")) { for (String name : parts[1].split(",")) { Item found = takeFrom(world, name); if (found == null) { throw new InvalidSaveCodeException("няма предмет " + name); } items.add(found); } } List<String> doors = new ArrayList<>(); if (!parts[2].equals("-")) { for (String name : parts[2].split(",")) { Room door = world.get(name); if (door == null || !door.isLocked()) { throw new InvalidSaveCodeException("няма заключена врата към " + name); } door.unlock(); doors.add(name); } } if (room.isLocked()) { throw new InvalidSaveCodeException("стаята " + room.getName() + " е заключена"); } int savedMoves; try { savedMoves = Integer.parseInt(parts[3]); } catch (NumberFormatException e) { throw new InvalidSaveCodeException("ходовете не са число: " + parts[3]); } rooms = world; current = room; inventory = items; unlocked = doors; moves = savedMoves;}
private Item takeFrom(Map<String, Room> world, String name) { for (Room room : world.values()) { Item found = room.takeItem(name); if (found != null) { return found; } } return null;}public void Load(string code){ string[] parts = code.Split(';'); if (parts.Length != 4) { throw new InvalidSaveCodeException("трябват 4 части, разделени с ;"); } Dictionary<string, Room> world = BuildWorld(); if (!world.ContainsKey(parts[0])) { throw new InvalidSaveCodeException("няма стая " + parts[0]); } Room room = world[parts[0]]; List<Item> items = new List<Item>(); if (parts[1] != "-") { foreach (string name in parts[1].Split(',')) { Item found = TakeFrom(world, name); if (found == null) { throw new InvalidSaveCodeException("няма предмет " + name); } items.Add(found); } } List<string> doors = new List<string>(); if (parts[2] != "-") { foreach (string name in parts[2].Split(',')) { if (!world.ContainsKey(name) || !world[name].IsLocked) { throw new InvalidSaveCodeException("няма заключена врата към " + name); } world[name].Unlock(); doors.Add(name); } } if (room.IsLocked) { throw new InvalidSaveCodeException("стаята " + room.Name + " е заключена"); } int savedMoves; try { savedMoves = int.Parse(parts[3]); } catch (FormatException) { throw new InvalidSaveCodeException("ходовете не са число: " + parts[3]); } rooms = world; Current = room; Inventory = items; unlocked = doors; Moves = savedMoves;}
private Item TakeFrom(Dictionary<string, Room> world, string name){ foreach (Room room in world.Values) { Item found = room.TakeItem(name); if (found != null) { return found; } } return null;}def load(self, code): parts = code.split(";") if len(parts) != 4: raise InvalidSaveCodeError("трябват 4 части, разделени с ;") world = self._build_world() room = world.get(parts[0]) if room is None: raise InvalidSaveCodeError(f"няма стая {parts[0]}") items = [] if parts[1] != "-": for name in parts[1].split(","): found = self._take_from(world, name) if found is None: raise InvalidSaveCodeError(f"няма предмет {name}") items.append(found) doors = [] if parts[2] != "-": for name in parts[2].split(","): door = world.get(name) if door is None or not door.is_locked: raise InvalidSaveCodeError(f"няма заключена врата към {name}") door.unlock() doors.append(name) if room.is_locked: raise InvalidSaveCodeError(f"стаята {room.name} е заключена") try: saved_moves = int(parts[3]) except ValueError: raise InvalidSaveCodeError(f"ходовете не са число: {parts[3]}") self._rooms = world self._current = room self._inventory = items self._unlocked = doors self._moves = saved_moves
def _take_from(self, world, name): for room in world.values(): found = room.take_item(name) if found is not None: return found return Noneload(code) { const parts = code.split(";"); if (parts.length !== 4) { throw new InvalidSaveCodeError("трябват 4 части, разделени с ;"); } const world = this.#buildWorld(); const room = world.get(parts[0]); if (room === undefined) { throw new InvalidSaveCodeError(`няма стая ${parts[0]}`); } const items = []; if (parts[1] !== "-") { for (const name of parts[1].split(",")) { const found = this.#takeFrom(world, name); if (found === null) { throw new InvalidSaveCodeError(`няма предмет ${name}`); } items.push(found); } } const doors = []; if (parts[2] !== "-") { for (const name of parts[2].split(",")) { const door = world.get(name); if (door === undefined || !door.isLocked) { throw new InvalidSaveCodeError(`няма заключена врата към ${name}`); } door.unlock(); doors.push(name); } } if (room.isLocked) { throw new InvalidSaveCodeError(`стаята ${room.name} е заключена`); } if (!/^\d+$/.test(parts[3])) { throw new InvalidSaveCodeError(`ходовете не са число: ${parts[3]}`); } this.#rooms = world; this.#current = room; this.#inventory = items; this.#unlocked = doors; this.#moves = Number(parts[3]);}
#takeFrom(world, name) { for (const room of world.values()) { const found = room.takeItem(name); if (found !== null) { return found; } } return null;}Командата „зареди“ хваща изключението и казва на играча какво не е наред:
class LoadCommand implements Command { @Override public String execute(Game game, String argument) { try { game.load(argument); return "Продължаваш оттам, докъдето стигна.\n" + game.getCurrent().describe(); } catch (InvalidSaveCodeException e) { return "Кодът не става: " + e.getMessage() + "."; } }}class LoadCommand : ICommand{ public string Execute(Game game, string argument) { try { game.Load(argument); return "Продължаваш оттам, докъдето стигна.\n" + game.Current.Describe(); } catch (InvalidSaveCodeException e) { return "Кодът не става: " + e.Message + "."; } }}class LoadCommand(Command): def execute(self, game, argument): try: game.load(argument) return "Продължаваш оттам, докъдето стигна.\n" + game.current.describe() except InvalidSaveCodeError as e: return f"Кодът не става: {e}."class LoadCommand { execute(game, argument) { try { game.load(argument); return `Продължаваш оттам, докъдето стигна.\n${game.current.describe()}`; } catch (e) { if (!(e instanceof InvalidSaveCodeError)) { throw e; } return `Кодът не става: ${e.message}.`; } }}По-точно, за любопитните
-
Numberне хвърля: развалено число еNaN, а празният текст е0. Затоваloadпроверява ходовете с регулярния израз/^\d+$/(само цифри) и сам хвърля своето изключение. Така „зареди“ хваща само едно изключение и съобщението е на езика на играта.
console.log(Number("четири"));console.log(Number(""));console.log(Number("7"));console.log(/^\d+$/.test("четири"), /^\d+$/.test(""), /^\d+$/.test("7"));NaN07false false true- Кодът не е защита от измама: който разбере формата, може да си напише „фоайе“. Проверката, че стаята не е заключена, пази само от явното. Истинските игри добавят контролна сума: число, сметнато от целия текст, което не съвпада, ако някой го е пипал.
Пробвай
Намери грешката
Играчът пише зареди кабинет;чук;-;3. Играта казва „Кодът не става: няма предмет чук.“, но след „огледай“ той е в кабинета, който е заключен. На кой ред е грешката?
Избери реда с грешката.
Играчът е преместен, преди кодът да е проверен докрай. После проверката на предметите хвърля, но преместването вече е станало. Редът трябва да е най-долу, при другите промени, след всички проверки.
Виж го
Запази и продължи
Играчът взима ключа и запазва. После опитва три развалени кода и накрая един, който минава:
иди севериди изтоквземи ключзапазизареди склад;чук;-;4зареди коридор;ключ;кабинет;четиризареди кабинет;ключ;-;7зареди кабинет;ключ;кабинет;7иди изток[лаборатория] Масите са пълни с колби. Вратата зад теб се е заключила сама.Тук има: фенерчеИзходи: север> иди север[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди изток[склад] Рафтове с кашони до тавана. На стената има календар: „Инвентаризация – 19 октомври“.Тук има: ключИзходи: запад> вземи ключВзе ключ. Малък месингов ключ с етикет „кабинет“.> запазиКод за продължение: склад;ключ;-;4> зареди склад;чук;-;4Кодът не става: няма предмет чук.> зареди коридор;ключ;кабинет;четириКодът не става: ходовете не са число: четири.> зареди кабинет;ключ;-;7Кодът не става: стаята кабинет е заключена.> зареди кабинет;ключ;кабинет;7Продължаваш оттам, докъдето стигна.[кабинет] Бюро, стол и шкаф с папки.Тук има: бележкаИзходи: изток> иди изток[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, западПоследният код е написан на ръка, но е валиден: кабинетът е отключен, а ключът е в раницата. Играта не знае дали кодът е отпечатан от нея. Тя знае само, че е възможно състояние.
Упражнение
Още загадки
Реши задачите в редактора си, с класовете от урока.
1. Тъмното мазе. На юг от лабораторията има мазе („Ниско мазе с тръби по тавана.“), а в него резервен предпазител. Влиза се само с фенерче в раницата. Подсказка: команда, която наследява GoCommand, проверява за мазето и вика родителя със super.execute(...). Регистрирай я с game.addCommand на мястото на „иди“.
иди югвземи фенерчеиди югвземи предпазител[лаборатория] Масите са пълни с колби. Вратата зад теб се е заключила сама.Тук има: фенерчеИзходи: север, юг> иди югТвърде тъмно е. Без светлина няма да намериш нищо.> вземи фенерчеВзе фенерче. Работи, батерията е почти пълна.> иди юг[мазе] Ниско мазе с тръби по тавана.Тук има: предпазителИзходи: север> вземи предпазителВзе предпазител. Резервен предпазител за таблото.2. Таймер. Охраната идва след 12 хода. Ако играчът не е навън дотогава, играта свършва.
иди севериди изтоквземи ключиди западиди западизползвай ключиди западвземи бележкаиди изтокиди изтокиди западвъведи 4719иди север[лаборатория] Масите са пълни с колби. Вратата зад теб се е заключила сама.Тук има: фенерчеИзходи: север> иди север[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди изток[склад] Рафтове с кашони до тавана. На стената има календар: „Инвентаризация – 19 октомври“.Тук има: ключИзходи: запад> вземи ключВзе ключ. Малък месингов ключ с етикет „кабинет“.> иди запад[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди западВратата към кабинет е заключена.> използвай ключОтключи вратата към кабинет.> иди запад[кабинет] Бюро, стол и шкаф с папки.Тук има: бележкаИзходи: изток> вземи бележкаВзе бележка. „Кодът на фоайето: 47 и денят на инвентаризацията.“> иди изток[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди изток[склад] Рафтове с кашони до тавана. На стената има календар: „Инвентаризация – 19 октомври“.Изходи: запад> иди запад[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> въведи 4719Ключалката щракна. Вратата към фоайе е отворена.Охраната дойде. Хванаха те след 12 хода.3. Подсказка срещу ход. Команда „подсказка“ казва следващата стъпка според състоянието: ключът, кабинетът, бележката, календарът. Струва ход, както всяка команда.
подсказкаиди севериди изтоквземи ключподсказкаиди западиди западизползвай ключподсказка[лаборатория] Масите са пълни с колби. Вратата зад теб се е заключила сама.Тук има: фенерчеИзходи: север> подсказкаПодсказка: огледай склада.> иди север[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди изток[склад] Рафтове с кашони до тавана. На стената има календар: „Инвентаризация – 19 октомври“.Тук има: ключИзходи: запад> вземи ключВзе ключ. Малък месингов ключ с етикет „кабинет“.> подсказкаПодсказка: ключът е за кабинета.> иди запад[коридор] Дълъг коридор. Лампата на тавана мига.Изходи: север, юг, изток, запад> иди западВратата към кабинет е заключена.> използвай ключОтключи вратата към кабинет.> подсказкаПодсказка: в кабинета има нещо за четене.Още задачи
- Предпазителят. Кодовата ключалка не работи, докато предпазителят от мазето не е „използван“ в коридора.
- Ходовете в кода. Таймерът да важи и след „зареди“: кодът вече носи ходовете.
- Контролна сума. Добави пета част: сборът от дължините на първите четири. Код с грешна сума е развален.
Тест