{"id":"text-adventure","title":"A text adventure","summary":"A small game you play by typing. Walk between the rooms of an old lighthouse, pick up things, unlock a door and light the lamp, with the whole world read from a file and the game saved to one.","after":"objects-with-lists","intro":"In this project you build a **text adventure**: a game that describes where you are, reads what you want to do (`go north`, `take key`), and answers in text. The world isn't written into the program. It comes from a file, `world.txt`, so the same program can play any world that file describes.\n\nWhen it's finished, this is a whole world: an old lighthouse with five rooms, two things to find, a locked door and a goal.\n\n```text\nroom;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\nroom;Boathouse;An old rowboat lies upside down. It smells of tar.\nroom;Tower;Stone stairs climb into the dark.\nroom;Lamp Room;A great glass lens waits for a flame.\nexit;Shore;north;Cottage\nexit;Shore;east;Boathouse\nexit;Cottage;south;Shore\nexit;Cottage;east;Tower\nexit;Boathouse;west;Shore\nexit;Tower;west;Cottage\nexit;Tower;up;Lamp Room\nexit;Lamp Room;down;Tower\nitem;oil;Cottage\nitem;key;Boathouse\nlock;Cottage;east;key\ngoal;Lamp Room;oil;You fill the lamp with oil and light it. Its beam sweeps across the sea.\n```\n\nA player who types these commands, one per line:\n\n```text\ngo north\ntake oil\ngo east\ngo south\ngo east\ntake key\ngo west\ngo north\nuse key\ngo east\ngo up\n```\n\nsees this:\n\n```text\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou see: oil\nYou take the oil.\nThe way east is locked.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Boathouse]\nAn old rowboat lies upside down. It smells of tar.\nExits: west\nYou see: key\nYou take the key.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou unlock the way east with the key.\n[Tower]\nStone stairs climb into the dark.\nExits: west, up\n[Lamp Room]\nA great glass lens waits for a flame.\nExits: down\nYou fill the lamp with oil and light it. Its beam sweeps across the sea.\nYou won in 7 moves.\n```\n\n### What you need\n\nModules 16 to 24, especially: string methods such as `split`, `startsWith` and `substring` (module 16), classes with constructors, getters and `toString` (modules 17 and 18), lists of objects (module 19), reading a file with a Scanner, `try` and `catch` (module 20), objects as method parameters and as instance variables, like the player's `Room` and `addExit(Exit exit)` (module 23), and a list as an instance variable created in the constructor, with methods that add to it, go through it, empty it and remove from it (module 24). The last milestone also writes a file, with a class called `PrintWriter`; its task shows you how.\n\n### How you build it\n\nThe program has a class for each kind of thing in the game: `Room`, `Exit` and `Player`, with `Main` reading the world and the commands. Each line of `world.txt` starts with its kind (`room`, `exit`, `item`, `lock`, `goal`), and each milestone teaches the program one more kind, together with the commands that go with it:\n\n1. **Rooms and commands:** rooms from the file, and a loop that reads commands.\n2. **Exits and moves:** `go north` takes you to the next room.\n3. **Things to carry:** `take`, `drop` and `inventory`.\n4. **A locked door:** an exit that opens with the right item, and `use`.\n5. **A goal:** the game ends when you reach it, and counts your moves.\n6. **Saving and loading:** `save` writes the game to a file, and `load` reads it back.\n\nLines of a kind the program doesn't know yet are skipped, so a world file written for a later milestone still works with an earlier program.\n","milestones":[{"id":"rooms","title":"Rooms and commands","task":"The starting code reads `world.txt` line by line and splits each line at `;`. A line such as `room;Shore;Cold waves break on gray pebbles.` is a room: its name, then its description. `Player.java` is ready: a player is always in one room.\n\n1. Write the class `Room`, with a constructor `Room(String name, String description)`, a getter `getName()`, and a `toString()` that returns the name in square brackets, a line break (`\\n`), and the description:\n\n   ```text\n   [Shore]\n   Cold waves break on gray pebbles.\n   ```\n\n2. In `main`, create a `Room` from each room line and add it to `rooms`.\n3. Create a `Player` in the first room of the list and print that room.\n4. Then read commands, one per line, until the player types `quit`:\n   - `look` prints the room again.\n   - `quit` prints `Goodbye.` and ends the game.\n   - Anything else prints `I don't understand \"dance\".`, with the command in the quotes.\n\n     Inside quotes, `\\\"` is a quote character: `\"say \\\"hi\\\"\"` prints `say \"hi\"`.\n\nWith this `world.txt`\n\n```text\nroom;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\n```\n\nand the commands `look`, `dance` and `quit`, the program prints:\n\n```\n[Shore]\nCold waves break on gray pebbles.\n[Shore]\nCold waves break on gray pebbles.\nI don't understand \"dance\".\nGoodbye.\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    // Create a Room from parts[1] and parts[2] and add it to rooms.\n\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Scanner scanner = new Scanner(System.in);\n        // Start the player in the first room, print the room, and read commands.\n\n    }\n}\n\n// ==== Room.java ====\npublic class Room {\n\n}\n\n// ==== Player.java ====\npublic class Player {\n    private Room room;\n\n    public Player(Room room) {\n        this.room = room;\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n        }\n    }\n}\n\n// ==== Room.java ====\npublic class Room {\n    private String name;\n    private String description;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public String toString() {\n        return \"[\" + this.name + \"]\\n\" + this.description;\n    }\n}\n\n// ==== Player.java ====\npublic class Player {\n    private Room room;\n\n    public Player(Room room) {\n        this.room = room;\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n    }\n}\n","hints":["`rooms.add(new Room(parts[1], parts[2]));` goes inside the `if` of the room lines. The game starts in `rooms.get(0)`: `Player player = new Player(rooms.get(0));`.","Read the commands in a `while (scanner.hasNextLine())` loop. Compare each one with `equals`, and leave the loop with `break` after printing `Goodbye.`","The line with the quotes: `\"I don't understand \\\"\" + command + \"\\\".\"`"],"tests":[{"name":"Look around","stdin":"look\ndance\nquit\n","files":{"world.txt":"room;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\n"},"expect":"[Shore]\nCold waves break on gray pebbles.\n[Shore]\nCold waves break on gray pebbles.\nI don't understand \"dance\".\nGoodbye.","hidden":false},{"name":"Another world","stdin":"sing\nlook\nLOOK\nquit\n","files":{"world.txt":"room;Greenhouse;Warm air and the smell of wet soil.\nroom;Shed;Rakes and spades hang on the wall.\nroom;Pond;Frogs sit on the lily pads.\n"},"expect":"[Greenhouse]\nWarm air and the smell of wet soil.\nI don't understand \"sing\".\n[Greenhouse]\nWarm air and the smell of wet soil.\nI don't understand \"LOOK\".\nGoodbye.","hidden":true},{"name":"Quit at once","stdin":"quit\n","files":{"world.txt":"room;Attic;Dust and old trunks.\n"},"expect":"[Attic]\nDust and old trunks.\nGoodbye.","hidden":true},{"name":"A room as text","stdin":"","call":"Room room = new Room(\"Cellar\", \"Shelves of jam jars line the walls.\");\nSystem.out.println(room);\nSystem.out.println(room.getName());\n","expect":"[Cellar]\nShelves of jam jars line the walls.\nCellar","hidden":true}],"require":[{"pattern":"String\\s+toString\\s*\\(","message":"Give `Room` a `toString()` method that returns the name and the description."},{"pattern":"new\\s+Room\\s*\\(","message":"Create a `Room` for each room line with `new Room(...)`."}],"forbid":[]},{"id":"exits","title":"Exits and moves","task":"Now the rooms get exits. A line such as `exit;Shore;north;Cottage` means: from the Shore, the way north leads to the Cottage. A world file lists its room lines first, so every room an exit line names already exists when the line is read. A new file, `Exit.java`, has been added to your program.\n\n1. Write the class `Exit`, with a constructor `Exit(String direction, String target)` and the getters `getDirection()` and `getTarget()`. The target is the name of the room the exit leads to.\n2. Give `Room` a list of its exits, an `ArrayList<Exit>` created in the constructor, and two methods:\n   - `addExit(Exit exit)` adds an exit to the list.\n   - `getTarget(String direction)` returns the name of the room the exit in that direction leads to, or `\"\"` when the room has no exit that way.\n3. When a room has exits, its `toString()` ends with one more line that lists their directions in the order they were added: `Exits: north, east`. A room without exits prints as before.\n4. In `Main`, write a method `findRoom(ArrayList<Room> rooms, String name)` that returns the room of that name, and use it to add an `Exit` to the right room for each exit line.\n5. A new command, `go` and a direction such as `go north`, moves the player through that exit and prints the new room. When there's no exit that way, it prints `You can't go west from here.` (with the direction the player typed). Only a command that starts with `go ` (with the space) is a move; a bare `go` is a command the game doesn't understand.\n\nWith this `world.txt`\n\n```text\nroom;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\nroom;Boathouse;An old rowboat lies upside down. It smells of tar.\nexit;Shore;north;Cottage\nexit;Shore;east;Boathouse\nexit;Cottage;south;Shore\nexit;Boathouse;west;Shore\n```\n\nand the commands `go north`, `go west`, `go south` and `quit`, the program prints:\n\n```\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south\nYou can't go west from here.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\nGoodbye.\n```\n","files":{"Exit.java":"public class Exit {\n\n}\n"},"kind":"code","seed":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n        }\n    }\n}\n\n// ==== Room.java ====\npublic class Room {\n    private String name;\n    private String description;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public String toString() {\n        return \"[\" + this.name + \"]\\n\" + this.description;\n    }\n}\n\n// ==== Player.java ====\npublic class Player {\n    private Room room;\n\n    public Player(Room room) {\n        this.room = room;\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n    }\n}\n\n// ==== Exit.java ====\npublic class Exit {\n\n}\n","solution":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                } else if (parts[0].equals(\"exit\")) {\n                    findRoom(rooms, parts[1]).addExit(new Exit(parts[2], parts[3]));\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else if (command.startsWith(\"go \")) {\n                move(player, rooms, command.substring(3));\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n        }\n    }\n\n    public static Room findRoom(ArrayList<Room> rooms, String name) {\n        for (Room room : rooms) {\n            if (room.getName().equals(name)) {\n                return room;\n            }\n        }\n        // Every room name in world.txt belongs to a room line, so this isn't reached.\n        return rooms.get(0);\n    }\n\n    public static void move(Player player, ArrayList<Room> rooms, String direction) {\n        String target = player.getRoom().getTarget(direction);\n        if (target.isEmpty()) {\n            System.out.println(\"You can't go \" + direction + \" from here.\");\n        } else {\n            player.moveTo(findRoom(rooms, target));\n            System.out.println(player.getRoom());\n        }\n    }\n}\n\n// ==== Room.java ====\nimport java.util.ArrayList;\n\npublic class Room {\n    private String name;\n    private String description;\n    private ArrayList<Exit> exits;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n        this.exits = new ArrayList<>();\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public void addExit(Exit exit) {\n        this.exits.add(exit);\n    }\n\n    public String getTarget(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.getTarget();\n            }\n        }\n        return \"\";\n    }\n\n    public String toString() {\n        String text = \"[\" + this.name + \"]\\n\" + this.description;\n        if (!this.exits.isEmpty()) {\n            text += \"\\nExits: \";\n            for (int i = 0; i < this.exits.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.exits.get(i).getDirection();\n            }\n        }\n        return text;\n    }\n}\n\n// ==== Player.java ====\npublic class Player {\n    private Room room;\n\n    public Player(Room room) {\n        this.room = room;\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n    }\n}\n\n// ==== Exit.java ====\npublic class Exit {\n    private String direction;\n    private String target;\n\n    public Exit(String direction, String target) {\n        this.direction = direction;\n        this.target = target;\n    }\n\n    public String getDirection() {\n        return this.direction;\n    }\n\n    public String getTarget() {\n        return this.target;\n    }\n}\n","hints":["`command.startsWith(\"go \")` is true for every go command, and `command.substring(3)` is the direction after it. A method `move(Player player, ArrayList<Room> rooms, String direction)` keeps the loop in `main` short.","In `getTarget`, loop over the exits and return `exit.getTarget()` when `exit.getDirection().equals(direction)`. After the loop, `return \"\";`. The caller checks the result with `isEmpty()`.","To join the directions with commas, put `\", \"` before every direction except the first: loop with an index and check `i > 0`."],"tests":[{"name":"Walk north and back","stdin":"go north\ngo west\ngo south\nquit\n","files":{"world.txt":"room;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\nroom;Boathouse;An old rowboat lies upside down. It smells of tar.\nexit;Shore;north;Cottage\nexit;Shore;east;Boathouse\nexit;Cottage;south;Shore\nexit;Boathouse;west;Shore\n"},"expect":"[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south\nYou can't go west from here.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\nGoodbye.","hidden":false},{"name":"Up and down the tower","stdin":"go up\ngo up\nlook\ngo down\ngo west\ngo east\ngo\nquit\n","files":{"world.txt":"room;Hall;A long hall with a red carpet.\nroom;Lamp Room;A great glass lens waits for a flame.\nroom;Library;Books from the floor to the ceiling.\nroom;Garden;Roses climb over an old wall.\nexit;Hall;up;Lamp Room\nexit;Hall;west;Library\nexit;Hall;south;Garden\nexit;Lamp Room;down;Hall\nexit;Library;east;Hall\n"},"expect":"[Hall]\nA long hall with a red carpet.\nExits: up, west, south\n[Lamp Room]\nA great glass lens waits for a flame.\nExits: down\nYou can't go up from here.\n[Lamp Room]\nA great glass lens waits for a flame.\nExits: down\n[Hall]\nA long hall with a red carpet.\nExits: up, west, south\n[Library]\nBooks from the floor to the ceiling.\nExits: east\n[Hall]\nA long hall with a red carpet.\nExits: up, west, south\nI don't understand \"go\".\nGoodbye.","hidden":true},{"name":"A room without exits","stdin":"go north\nquit\n","files":{"world.txt":"room;Cell;Four stone walls and a narrow window.\n"},"expect":"[Cell]\nFour stone walls and a narrow window.\nYou can't go north from here.\nGoodbye.","hidden":true},{"name":"Exits of a room","stdin":"","call":"Room hall = new Room(\"Hall\", \"Tall windows.\");\nhall.addExit(new Exit(\"west\", \"Library\"));\nhall.addExit(new Exit(\"down\", \"Cellar\"));\nSystem.out.println(hall);\nSystem.out.println(hall.getTarget(\"down\"));\nSystem.out.println(\"[\" + hall.getTarget(\"east\") + \"]\");\n","expect":"[Hall]\nTall windows.\nExits: west, down\nCellar\n[]","hidden":true}],"require":[{"pattern":"ArrayList<Exit>","message":"Keep a room's exits in an `ArrayList<Exit>`."},{"pattern":"findRoom\\s*\\(","min":2,"message":"Write a method `findRoom` and use it to find rooms by name."}],"forbid":[]},{"id":"items","title":"Things to carry","task":"Time for things to pick up. A line such as `item;oil;Cottage` puts the item `oil` in the room called Cottage. An item is just its name, a `String`.\n\n1. Give `Room` a list of the items in it, an `ArrayList<String>`, with the methods `addItem(String item)`, `hasItem(String item)` (true when the item is in the room) and `removeItem(String item)`.\n2. When a room has items, its `toString()` ends with one more line, after the exits: `You see: oil, rope`, in the order they were added. A room without items prints as before.\n3. Give `Player` a list of the items they carry, with the methods `take(String item)` (adds it), `has(String item)`, `drop(String item)` (removes it) and `inventory()`, which returns `You carry: shell, oil` in the order they were taken, or `You carry nothing.` when the list is empty.\n4. In `Main`, put each item line's item into its room, and add three commands:\n   - `take oil` moves the oil from the room to the player and prints `You take the oil.` When the room has no oil, it prints `There is no oil here.`\n   - `drop oil` moves it from the player to the end of the room's items and prints `You drop the oil.` When the player doesn't carry it, it prints `You aren't carrying the oil.`\n   - `inventory` prints what `inventory()` returns.\n\nWith this `world.txt`\n\n```text\nroom;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\nroom;Boathouse;An old rowboat lies upside down. It smells of tar.\nexit;Shore;north;Cottage\nexit;Shore;east;Boathouse\nexit;Cottage;south;Shore\nexit;Boathouse;west;Shore\nitem;shell;Shore\nitem;oil;Cottage\nitem;rope;Cottage\n```\n\nand the commands `inventory`, `take shell`, `take shell`, `go north`, `take oil`, `inventory`, `drop shell`, `look` and `quit`, the program prints:\n\n```\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\nYou see: shell\nYou carry nothing.\nYou take the shell.\nThere is no shell here.\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south\nYou see: oil, rope\nYou take the oil.\nYou carry: shell, oil\nYou drop the shell.\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south\nYou see: rope, shell\nGoodbye.\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                } else if (parts[0].equals(\"exit\")) {\n                    findRoom(rooms, parts[1]).addExit(new Exit(parts[2], parts[3]));\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else if (command.startsWith(\"go \")) {\n                move(player, rooms, command.substring(3));\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n        }\n    }\n\n    public static Room findRoom(ArrayList<Room> rooms, String name) {\n        for (Room room : rooms) {\n            if (room.getName().equals(name)) {\n                return room;\n            }\n        }\n        // Every room name in world.txt belongs to a room line, so this isn't reached.\n        return rooms.get(0);\n    }\n\n    public static void move(Player player, ArrayList<Room> rooms, String direction) {\n        String target = player.getRoom().getTarget(direction);\n        if (target.isEmpty()) {\n            System.out.println(\"You can't go \" + direction + \" from here.\");\n        } else {\n            player.moveTo(findRoom(rooms, target));\n            System.out.println(player.getRoom());\n        }\n    }\n}\n\n// ==== Room.java ====\nimport java.util.ArrayList;\n\npublic class Room {\n    private String name;\n    private String description;\n    private ArrayList<Exit> exits;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n        this.exits = new ArrayList<>();\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public void addExit(Exit exit) {\n        this.exits.add(exit);\n    }\n\n    public String getTarget(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.getTarget();\n            }\n        }\n        return \"\";\n    }\n\n    public String toString() {\n        String text = \"[\" + this.name + \"]\\n\" + this.description;\n        if (!this.exits.isEmpty()) {\n            text += \"\\nExits: \";\n            for (int i = 0; i < this.exits.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.exits.get(i).getDirection();\n            }\n        }\n        return text;\n    }\n}\n\n// ==== Player.java ====\npublic class Player {\n    private Room room;\n\n    public Player(Room room) {\n        this.room = room;\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n    }\n}\n\n// ==== Exit.java ====\npublic class Exit {\n    private String direction;\n    private String target;\n\n    public Exit(String direction, String target) {\n        this.direction = direction;\n        this.target = target;\n    }\n\n    public String getDirection() {\n        return this.direction;\n    }\n\n    public String getTarget() {\n        return this.target;\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                } else if (parts[0].equals(\"exit\")) {\n                    findRoom(rooms, parts[1]).addExit(new Exit(parts[2], parts[3]));\n                } else if (parts[0].equals(\"item\")) {\n                    findRoom(rooms, parts[2]).addItem(parts[1]);\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else if (command.startsWith(\"go \")) {\n                move(player, rooms, command.substring(3));\n            } else if (command.startsWith(\"take \")) {\n                take(player, command.substring(5));\n            } else if (command.startsWith(\"drop \")) {\n                drop(player, command.substring(5));\n            } else if (command.equals(\"inventory\")) {\n                System.out.println(player.inventory());\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n        }\n    }\n\n    public static Room findRoom(ArrayList<Room> rooms, String name) {\n        for (Room room : rooms) {\n            if (room.getName().equals(name)) {\n                return room;\n            }\n        }\n        // Every room name in world.txt belongs to a room line, so this isn't reached.\n        return rooms.get(0);\n    }\n\n    public static void move(Player player, ArrayList<Room> rooms, String direction) {\n        String target = player.getRoom().getTarget(direction);\n        if (target.isEmpty()) {\n            System.out.println(\"You can't go \" + direction + \" from here.\");\n        } else {\n            player.moveTo(findRoom(rooms, target));\n            System.out.println(player.getRoom());\n        }\n    }\n\n    public static void take(Player player, String item) {\n        Room room = player.getRoom();\n        if (room.hasItem(item)) {\n            room.removeItem(item);\n            player.take(item);\n            System.out.println(\"You take the \" + item + \".\");\n        } else {\n            System.out.println(\"There is no \" + item + \" here.\");\n        }\n    }\n\n    public static void drop(Player player, String item) {\n        if (player.has(item)) {\n            player.drop(item);\n            player.getRoom().addItem(item);\n            System.out.println(\"You drop the \" + item + \".\");\n        } else {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n        }\n    }\n}\n\n// ==== Room.java ====\nimport java.util.ArrayList;\n\npublic class Room {\n    private String name;\n    private String description;\n    private ArrayList<Exit> exits;\n    private ArrayList<String> items;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n        this.exits = new ArrayList<>();\n        this.items = new ArrayList<>();\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public void addExit(Exit exit) {\n        this.exits.add(exit);\n    }\n\n    public String getTarget(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.getTarget();\n            }\n        }\n        return \"\";\n    }\n\n    public void addItem(String item) {\n        this.items.add(item);\n    }\n\n    public boolean hasItem(String item) {\n        return this.items.contains(item);\n    }\n\n    public void removeItem(String item) {\n        this.items.remove(item);\n    }\n\n    public String toString() {\n        String text = \"[\" + this.name + \"]\\n\" + this.description;\n        if (!this.exits.isEmpty()) {\n            text += \"\\nExits: \";\n            for (int i = 0; i < this.exits.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.exits.get(i).getDirection();\n            }\n        }\n        if (!this.items.isEmpty()) {\n            text += \"\\nYou see: \";\n            for (int i = 0; i < this.items.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.items.get(i);\n            }\n        }\n        return text;\n    }\n}\n\n// ==== Player.java ====\nimport java.util.ArrayList;\n\npublic class Player {\n    private Room room;\n    private ArrayList<String> items;\n\n    public Player(Room room) {\n        this.room = room;\n        this.items = new ArrayList<>();\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n    }\n\n    public void take(String item) {\n        this.items.add(item);\n    }\n\n    public boolean has(String item) {\n        return this.items.contains(item);\n    }\n\n    public void drop(String item) {\n        this.items.remove(item);\n    }\n\n    public String inventory() {\n        if (this.items.isEmpty()) {\n            return \"You carry nothing.\";\n        }\n        String text = \"You carry: \";\n        for (int i = 0; i < this.items.size(); i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.items.get(i);\n        }\n        return text;\n    }\n}\n\n// ==== Exit.java ====\npublic class Exit {\n    private String direction;\n    private String target;\n\n    public Exit(String direction, String target) {\n        this.direction = direction;\n        this.target = target;\n    }\n\n    public String getDirection() {\n        return this.direction;\n    }\n\n    public String getTarget() {\n        return this.target;\n    }\n}\n","hints":["The list methods of module 14 do the work: `contains` for `hasItem` and `has`, `remove` with the item's name for `removeItem` and `drop`.","`take(Player player, String item)` and `drop(Player player, String item)` in `Main` keep the loop short. The room is `player.getRoom()`: check with `hasItem` or `has` first, then move the item.","`inventory()` joins the names with commas just as the exits are joined in `toString()`."],"tests":[{"name":"Take and drop","stdin":"inventory\ntake shell\ntake shell\ngo north\ntake oil\ninventory\ndrop shell\nlook\nquit\n","files":{"world.txt":"room;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\nroom;Boathouse;An old rowboat lies upside down. It smells of tar.\nexit;Shore;north;Cottage\nexit;Shore;east;Boathouse\nexit;Cottage;south;Shore\nexit;Boathouse;west;Shore\nitem;shell;Shore\nitem;oil;Cottage\nitem;rope;Cottage\n"},"expect":"[Shore]\nCold waves break on gray pebbles.\nExits: north, east\nYou see: shell\nYou carry nothing.\nYou take the shell.\nThere is no shell here.\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south\nYou see: oil, rope\nYou take the oil.\nYou carry: shell, oil\nYou drop the shell.\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south\nYou see: rope, shell\nGoodbye.","hidden":false},{"name":"Carry things to another room","stdin":"drop spoon\ntake spoon\ntake bread\ngo east\ntake flour\ntake apple\nlook\ngo west\ndrop apple\ndrop spoon\ninventory\nlook\nquit\n","files":{"world.txt":"room;Kitchen;Copper pans hang over the stove.\nroom;Pantry;Sacks of flour and a smell of apples.\nexit;Kitchen;east;Pantry\nexit;Pantry;west;Kitchen\nitem;apple;Pantry\nitem;flour;Pantry\nitem;spoon;Kitchen\n"},"expect":"[Kitchen]\nCopper pans hang over the stove.\nExits: east\nYou see: spoon\nYou aren't carrying the spoon.\nYou take the spoon.\nThere is no bread here.\n[Pantry]\nSacks of flour and a smell of apples.\nExits: west\nYou see: apple, flour\nYou take the flour.\nYou take the apple.\n[Pantry]\nSacks of flour and a smell of apples.\nExits: west\n[Kitchen]\nCopper pans hang over the stove.\nExits: east\nYou drop the apple.\nYou drop the spoon.\nYou carry: flour\n[Kitchen]\nCopper pans hang over the stove.\nExits: east\nYou see: apple, spoon\nGoodbye.","hidden":true},{"name":"A player's items","stdin":"","call":"Player player = new Player(new Room(\"Hall\", \"Quiet.\"));\nSystem.out.println(player.inventory());\nplayer.take(\"map\");\nplayer.take(\"coin\");\nSystem.out.println(player.inventory());\nSystem.out.println(player.has(\"map\"));\nplayer.drop(\"map\");\nSystem.out.println(player.has(\"map\"));\nSystem.out.println(player.inventory());\n","expect":"You carry nothing.\nYou carry: map, coin\ntrue\nfalse\nYou carry: coin","hidden":true}],"require":[{"pattern":"ArrayList<String>","min":2,"message":"Keep the items of a room and of the player in lists, `ArrayList<String>`."}],"forbid":[]},{"id":"locks","title":"A locked door","task":"Some ways are locked. A line such as `lock;Cottage;east;key` locks the exit east from the Cottage, and the item `key` opens it. A lock line comes after the exit line it locks.\n\n1. Give `Exit` a third instance variable, `key`: the item that opens it, or `\"\"` when the exit isn't locked. The constructor stays as it is and sets `key` to `\"\"`. Add the methods `getKey()`, `setKey(String key)` and `isLocked()`, which is true when the key isn't `\"\"`.\n2. Give `Room` three methods:\n   - `lock(String direction, String key)` sets the key of the exit in that direction.\n   - `isLocked(String direction)` is true when the exit in that direction is locked.\n   - `unlock(String item)` looks for a locked exit whose key is that item. It opens the first one it finds (its key becomes `\"\"`) and returns its direction, or returns `\"\"` when the item opens nothing here.\n3. In `Main`, lock the exit of each lock line. `go` through a locked exit prints `The way east is locked.` and the player stays.\n4. A new command, `use key`:\n   - When the player doesn't carry the key, it prints `You aren't carrying the key.`\n   - When the key opens a locked exit of the room, it prints `You unlock the way east with the key.` The player keeps the key.\n   - Otherwise it prints `Nothing happens.`\n\nWith the lighthouse world of the project's introduction, without its goal line, and the commands `go north`, `go east`, `use key`, `go south`, `go east`, `take key`, `use key`, `go west`, `go north`, `use key`, `go east` and `quit`, the program prints:\n\n```\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou see: oil\nThe way east is locked.\nYou aren't carrying the key.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Boathouse]\nAn old rowboat lies upside down. It smells of tar.\nExits: west\nYou see: key\nYou take the key.\nNothing happens.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou see: oil\nYou unlock the way east with the key.\n[Tower]\nStone stairs climb into the dark.\nExits: west, up\nGoodbye.\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                } else if (parts[0].equals(\"exit\")) {\n                    findRoom(rooms, parts[1]).addExit(new Exit(parts[2], parts[3]));\n                } else if (parts[0].equals(\"item\")) {\n                    findRoom(rooms, parts[2]).addItem(parts[1]);\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else if (command.startsWith(\"go \")) {\n                move(player, rooms, command.substring(3));\n            } else if (command.startsWith(\"take \")) {\n                take(player, command.substring(5));\n            } else if (command.startsWith(\"drop \")) {\n                drop(player, command.substring(5));\n            } else if (command.equals(\"inventory\")) {\n                System.out.println(player.inventory());\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n        }\n    }\n\n    public static Room findRoom(ArrayList<Room> rooms, String name) {\n        for (Room room : rooms) {\n            if (room.getName().equals(name)) {\n                return room;\n            }\n        }\n        // Every room name in world.txt belongs to a room line, so this isn't reached.\n        return rooms.get(0);\n    }\n\n    public static void move(Player player, ArrayList<Room> rooms, String direction) {\n        String target = player.getRoom().getTarget(direction);\n        if (target.isEmpty()) {\n            System.out.println(\"You can't go \" + direction + \" from here.\");\n        } else {\n            player.moveTo(findRoom(rooms, target));\n            System.out.println(player.getRoom());\n        }\n    }\n\n    public static void take(Player player, String item) {\n        Room room = player.getRoom();\n        if (room.hasItem(item)) {\n            room.removeItem(item);\n            player.take(item);\n            System.out.println(\"You take the \" + item + \".\");\n        } else {\n            System.out.println(\"There is no \" + item + \" here.\");\n        }\n    }\n\n    public static void drop(Player player, String item) {\n        if (player.has(item)) {\n            player.drop(item);\n            player.getRoom().addItem(item);\n            System.out.println(\"You drop the \" + item + \".\");\n        } else {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n        }\n    }\n}\n\n// ==== Room.java ====\nimport java.util.ArrayList;\n\npublic class Room {\n    private String name;\n    private String description;\n    private ArrayList<Exit> exits;\n    private ArrayList<String> items;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n        this.exits = new ArrayList<>();\n        this.items = new ArrayList<>();\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public void addExit(Exit exit) {\n        this.exits.add(exit);\n    }\n\n    public String getTarget(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.getTarget();\n            }\n        }\n        return \"\";\n    }\n\n    public void addItem(String item) {\n        this.items.add(item);\n    }\n\n    public boolean hasItem(String item) {\n        return this.items.contains(item);\n    }\n\n    public void removeItem(String item) {\n        this.items.remove(item);\n    }\n\n    public String toString() {\n        String text = \"[\" + this.name + \"]\\n\" + this.description;\n        if (!this.exits.isEmpty()) {\n            text += \"\\nExits: \";\n            for (int i = 0; i < this.exits.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.exits.get(i).getDirection();\n            }\n        }\n        if (!this.items.isEmpty()) {\n            text += \"\\nYou see: \";\n            for (int i = 0; i < this.items.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.items.get(i);\n            }\n        }\n        return text;\n    }\n}\n\n// ==== Player.java ====\nimport java.util.ArrayList;\n\npublic class Player {\n    private Room room;\n    private ArrayList<String> items;\n\n    public Player(Room room) {\n        this.room = room;\n        this.items = new ArrayList<>();\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n    }\n\n    public void take(String item) {\n        this.items.add(item);\n    }\n\n    public boolean has(String item) {\n        return this.items.contains(item);\n    }\n\n    public void drop(String item) {\n        this.items.remove(item);\n    }\n\n    public String inventory() {\n        if (this.items.isEmpty()) {\n            return \"You carry nothing.\";\n        }\n        String text = \"You carry: \";\n        for (int i = 0; i < this.items.size(); i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.items.get(i);\n        }\n        return text;\n    }\n}\n\n// ==== Exit.java ====\npublic class Exit {\n    private String direction;\n    private String target;\n\n    public Exit(String direction, String target) {\n        this.direction = direction;\n        this.target = target;\n    }\n\n    public String getDirection() {\n        return this.direction;\n    }\n\n    public String getTarget() {\n        return this.target;\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                } else if (parts[0].equals(\"exit\")) {\n                    findRoom(rooms, parts[1]).addExit(new Exit(parts[2], parts[3]));\n                } else if (parts[0].equals(\"item\")) {\n                    findRoom(rooms, parts[2]).addItem(parts[1]);\n                } else if (parts[0].equals(\"lock\")) {\n                    findRoom(rooms, parts[1]).lock(parts[2], parts[3]);\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else if (command.startsWith(\"go \")) {\n                move(player, rooms, command.substring(3));\n            } else if (command.startsWith(\"take \")) {\n                take(player, command.substring(5));\n            } else if (command.startsWith(\"drop \")) {\n                drop(player, command.substring(5));\n            } else if (command.equals(\"inventory\")) {\n                System.out.println(player.inventory());\n            } else if (command.startsWith(\"use \")) {\n                use(player, command.substring(4));\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n        }\n    }\n\n    public static Room findRoom(ArrayList<Room> rooms, String name) {\n        for (Room room : rooms) {\n            if (room.getName().equals(name)) {\n                return room;\n            }\n        }\n        // Every room name in world.txt belongs to a room line, so this isn't reached.\n        return rooms.get(0);\n    }\n\n    public static void move(Player player, ArrayList<Room> rooms, String direction) {\n        String target = player.getRoom().getTarget(direction);\n        if (target.isEmpty()) {\n            System.out.println(\"You can't go \" + direction + \" from here.\");\n        } else if (player.getRoom().isLocked(direction)) {\n            System.out.println(\"The way \" + direction + \" is locked.\");\n        } else {\n            player.moveTo(findRoom(rooms, target));\n            System.out.println(player.getRoom());\n        }\n    }\n\n    public static void take(Player player, String item) {\n        Room room = player.getRoom();\n        if (room.hasItem(item)) {\n            room.removeItem(item);\n            player.take(item);\n            System.out.println(\"You take the \" + item + \".\");\n        } else {\n            System.out.println(\"There is no \" + item + \" here.\");\n        }\n    }\n\n    public static void drop(Player player, String item) {\n        if (player.has(item)) {\n            player.drop(item);\n            player.getRoom().addItem(item);\n            System.out.println(\"You drop the \" + item + \".\");\n        } else {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n        }\n    }\n\n    public static void use(Player player, String item) {\n        if (!player.has(item)) {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n            return;\n        }\n        String direction = player.getRoom().unlock(item);\n        if (direction.isEmpty()) {\n            System.out.println(\"Nothing happens.\");\n        } else {\n            System.out.println(\"You unlock the way \" + direction + \" with the \" + item + \".\");\n        }\n    }\n}\n\n// ==== Room.java ====\nimport java.util.ArrayList;\n\npublic class Room {\n    private String name;\n    private String description;\n    private ArrayList<Exit> exits;\n    private ArrayList<String> items;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n        this.exits = new ArrayList<>();\n        this.items = new ArrayList<>();\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public void addExit(Exit exit) {\n        this.exits.add(exit);\n    }\n\n    public String getTarget(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.getTarget();\n            }\n        }\n        return \"\";\n    }\n\n    public void lock(String direction, String key) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                exit.setKey(key);\n            }\n        }\n    }\n\n    public boolean isLocked(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.isLocked();\n            }\n        }\n        return false;\n    }\n\n    public String unlock(String item) {\n        for (Exit exit : this.exits) {\n            if (exit.isLocked() && exit.getKey().equals(item)) {\n                exit.setKey(\"\");\n                return exit.getDirection();\n            }\n        }\n        return \"\";\n    }\n\n    public void addItem(String item) {\n        this.items.add(item);\n    }\n\n    public boolean hasItem(String item) {\n        return this.items.contains(item);\n    }\n\n    public void removeItem(String item) {\n        this.items.remove(item);\n    }\n\n    public String toString() {\n        String text = \"[\" + this.name + \"]\\n\" + this.description;\n        if (!this.exits.isEmpty()) {\n            text += \"\\nExits: \";\n            for (int i = 0; i < this.exits.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.exits.get(i).getDirection();\n            }\n        }\n        if (!this.items.isEmpty()) {\n            text += \"\\nYou see: \";\n            for (int i = 0; i < this.items.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.items.get(i);\n            }\n        }\n        return text;\n    }\n}\n\n// ==== Player.java ====\nimport java.util.ArrayList;\n\npublic class Player {\n    private Room room;\n    private ArrayList<String> items;\n\n    public Player(Room room) {\n        this.room = room;\n        this.items = new ArrayList<>();\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n    }\n\n    public void take(String item) {\n        this.items.add(item);\n    }\n\n    public boolean has(String item) {\n        return this.items.contains(item);\n    }\n\n    public void drop(String item) {\n        this.items.remove(item);\n    }\n\n    public String inventory() {\n        if (this.items.isEmpty()) {\n            return \"You carry nothing.\";\n        }\n        String text = \"You carry: \";\n        for (int i = 0; i < this.items.size(); i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.items.get(i);\n        }\n        return text;\n    }\n}\n\n// ==== Exit.java ====\npublic class Exit {\n    private String direction;\n    private String target;\n    private String key;\n\n    public Exit(String direction, String target) {\n        this.direction = direction;\n        this.target = target;\n        this.key = \"\";\n    }\n\n    public String getDirection() {\n        return this.direction;\n    }\n\n    public String getTarget() {\n        return this.target;\n    }\n\n    public String getKey() {\n        return this.key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n    public boolean isLocked() {\n        return !this.key.isEmpty();\n    }\n}\n","hints":["`isLocked()` in `Exit` is one line: `return !this.key.isEmpty();`","In `move`, check the exit in this order: no exit (`getTarget` returns `\"\"`), then locked (`isLocked(direction)`), and only then move the player.","In `unlock`, loop over the exits: when `exit.isLocked()` and `exit.getKey().equals(item)`, call `exit.setKey(\"\")` and return `exit.getDirection()`."],"tests":[{"name":"Find the key and open the door","stdin":"go north\ngo east\nuse key\ngo south\ngo east\ntake key\nuse key\ngo west\ngo north\nuse key\ngo east\nquit\n","files":{"world.txt":"room;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\nroom;Boathouse;An old rowboat lies upside down. It smells of tar.\nroom;Tower;Stone stairs climb into the dark.\nroom;Lamp Room;A great glass lens waits for a flame.\nexit;Shore;north;Cottage\nexit;Shore;east;Boathouse\nexit;Cottage;south;Shore\nexit;Cottage;east;Tower\nexit;Boathouse;west;Shore\nexit;Tower;west;Cottage\nexit;Tower;up;Lamp Room\nexit;Lamp Room;down;Tower\nitem;oil;Cottage\nitem;key;Boathouse\nlock;Cottage;east;key\n"},"expect":"[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou see: oil\nThe way east is locked.\nYou aren't carrying the key.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Boathouse]\nAn old rowboat lies upside down. It smells of tar.\nExits: west\nYou see: key\nYou take the key.\nNothing happens.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou see: oil\nYou unlock the way east with the key.\n[Tower]\nStone stairs climb into the dark.\nExits: west, up\nGoodbye.","hidden":false},{"name":"Two locks and the wrong item","stdin":"take ladder\ntake bolt\nuse ladder\ngo north\ngo up\ngo east\nuse bolt\nuse bolt\ngo east\ngo west\ngo up\nuse ladder\ngo up\ngo down\nuse rope\nquit\n","files":{"world.txt":"room;Yard;Chickens peck at the ground.\nroom;Barn;Hay is piled up to the roof.\nroom;Loft;A small window looks over the fields.\nroom;Stable;An empty stall and a saddle.\nexit;Yard;north;Barn\nexit;Barn;south;Yard\nexit;Barn;up;Loft\nexit;Barn;east;Stable\nexit;Loft;down;Barn\nexit;Stable;west;Barn\nitem;ladder;Yard\nitem;bolt;Yard\nlock;Barn;up;ladder\nlock;Barn;east;bolt\n"},"expect":"[Yard]\nChickens peck at the ground.\nExits: north\nYou see: ladder, bolt\nYou take the ladder.\nYou take the bolt.\nNothing happens.\n[Barn]\nHay is piled up to the roof.\nExits: south, up, east\nThe way up is locked.\nThe way east is locked.\nYou unlock the way east with the bolt.\nNothing happens.\n[Stable]\nAn empty stall and a saddle.\nExits: west\n[Barn]\nHay is piled up to the roof.\nExits: south, up, east\nThe way up is locked.\nYou unlock the way up with the ladder.\n[Loft]\nA small window looks over the fields.\nExits: down\n[Barn]\nHay is piled up to the roof.\nExits: south, up, east\nYou aren't carrying the rope.\nGoodbye.","hidden":true},{"name":"Unlocking a room's exit","stdin":"","call":"Room gate = new Room(\"Gate\", \"Iron bars.\");\ngate.addExit(new Exit(\"north\", \"Park\"));\ngate.addExit(new Exit(\"south\", \"Street\"));\ngate.lock(\"north\", \"card\");\nSystem.out.println(gate.isLocked(\"north\"));\nSystem.out.println(gate.isLocked(\"south\"));\nSystem.out.println(\"[\" + gate.unlock(\"coin\") + \"]\");\nSystem.out.println(gate.unlock(\"card\"));\nSystem.out.println(gate.isLocked(\"north\"));\nSystem.out.println(\"[\" + gate.unlock(\"card\") + \"]\");\n","expect":"true\nfalse\n[]\nnorth\nfalse\n[]","hidden":true}],"require":[{"pattern":"isLocked\\s*\\(","message":"Write `isLocked` and use it to keep the player out of a locked exit."}],"forbid":[]},{"id":"goal","title":"A goal","task":"A game needs a way to win. A line such as `goal;Lamp Room;oil;You fill the lamp with oil and light it. Its beam sweeps across the sea.` says: the player wins when they're in the Lamp Room and carry the oil. The last part is the text the game prints then. A world has at most one goal line, and a world without one can't be won.\n\n1. Give `Player` an instance variable `moves` that starts at 0, and a getter `getMoves()`. `moveTo` adds 1 to it, so it counts the moves the player has made. A `go` that fails (no exit, or a locked one) isn't a move.\n2. In `Main`, keep the goal's room, item and text in three `String` variables made before the `try` (empty strings at first), and set them from the goal line.\n3. After every command except `quit`, check whether the player is in the goal's room and carries its item. If so, print the goal's text and then `You won in 7 moves.` (with the player's count), and end the game.\n\nWith the whole lighthouse world of the project's introduction, goal line and all, and the commands of the sample run there, the program prints:\n\n```\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou see: oil\nYou take the oil.\nThe way east is locked.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Boathouse]\nAn old rowboat lies upside down. It smells of tar.\nExits: west\nYou see: key\nYou take the key.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou unlock the way east with the key.\n[Tower]\nStone stairs climb into the dark.\nExits: west, up\n[Lamp Room]\nA great glass lens waits for a flame.\nExits: down\nYou fill the lamp with oil and light it. Its beam sweeps across the sea.\nYou won in 7 moves.\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                } else if (parts[0].equals(\"exit\")) {\n                    findRoom(rooms, parts[1]).addExit(new Exit(parts[2], parts[3]));\n                } else if (parts[0].equals(\"item\")) {\n                    findRoom(rooms, parts[2]).addItem(parts[1]);\n                } else if (parts[0].equals(\"lock\")) {\n                    findRoom(rooms, parts[1]).lock(parts[2], parts[3]);\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else if (command.startsWith(\"go \")) {\n                move(player, rooms, command.substring(3));\n            } else if (command.startsWith(\"take \")) {\n                take(player, command.substring(5));\n            } else if (command.startsWith(\"drop \")) {\n                drop(player, command.substring(5));\n            } else if (command.equals(\"inventory\")) {\n                System.out.println(player.inventory());\n            } else if (command.startsWith(\"use \")) {\n                use(player, command.substring(4));\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n        }\n    }\n\n    public static Room findRoom(ArrayList<Room> rooms, String name) {\n        for (Room room : rooms) {\n            if (room.getName().equals(name)) {\n                return room;\n            }\n        }\n        // Every room name in world.txt belongs to a room line, so this isn't reached.\n        return rooms.get(0);\n    }\n\n    public static void move(Player player, ArrayList<Room> rooms, String direction) {\n        String target = player.getRoom().getTarget(direction);\n        if (target.isEmpty()) {\n            System.out.println(\"You can't go \" + direction + \" from here.\");\n        } else if (player.getRoom().isLocked(direction)) {\n            System.out.println(\"The way \" + direction + \" is locked.\");\n        } else {\n            player.moveTo(findRoom(rooms, target));\n            System.out.println(player.getRoom());\n        }\n    }\n\n    public static void take(Player player, String item) {\n        Room room = player.getRoom();\n        if (room.hasItem(item)) {\n            room.removeItem(item);\n            player.take(item);\n            System.out.println(\"You take the \" + item + \".\");\n        } else {\n            System.out.println(\"There is no \" + item + \" here.\");\n        }\n    }\n\n    public static void drop(Player player, String item) {\n        if (player.has(item)) {\n            player.drop(item);\n            player.getRoom().addItem(item);\n            System.out.println(\"You drop the \" + item + \".\");\n        } else {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n        }\n    }\n\n    public static void use(Player player, String item) {\n        if (!player.has(item)) {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n            return;\n        }\n        String direction = player.getRoom().unlock(item);\n        if (direction.isEmpty()) {\n            System.out.println(\"Nothing happens.\");\n        } else {\n            System.out.println(\"You unlock the way \" + direction + \" with the \" + item + \".\");\n        }\n    }\n}\n\n// ==== Room.java ====\nimport java.util.ArrayList;\n\npublic class Room {\n    private String name;\n    private String description;\n    private ArrayList<Exit> exits;\n    private ArrayList<String> items;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n        this.exits = new ArrayList<>();\n        this.items = new ArrayList<>();\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public void addExit(Exit exit) {\n        this.exits.add(exit);\n    }\n\n    public String getTarget(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.getTarget();\n            }\n        }\n        return \"\";\n    }\n\n    public void lock(String direction, String key) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                exit.setKey(key);\n            }\n        }\n    }\n\n    public boolean isLocked(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.isLocked();\n            }\n        }\n        return false;\n    }\n\n    public String unlock(String item) {\n        for (Exit exit : this.exits) {\n            if (exit.isLocked() && exit.getKey().equals(item)) {\n                exit.setKey(\"\");\n                return exit.getDirection();\n            }\n        }\n        return \"\";\n    }\n\n    public void addItem(String item) {\n        this.items.add(item);\n    }\n\n    public boolean hasItem(String item) {\n        return this.items.contains(item);\n    }\n\n    public void removeItem(String item) {\n        this.items.remove(item);\n    }\n\n    public String toString() {\n        String text = \"[\" + this.name + \"]\\n\" + this.description;\n        if (!this.exits.isEmpty()) {\n            text += \"\\nExits: \";\n            for (int i = 0; i < this.exits.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.exits.get(i).getDirection();\n            }\n        }\n        if (!this.items.isEmpty()) {\n            text += \"\\nYou see: \";\n            for (int i = 0; i < this.items.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.items.get(i);\n            }\n        }\n        return text;\n    }\n}\n\n// ==== Player.java ====\nimport java.util.ArrayList;\n\npublic class Player {\n    private Room room;\n    private ArrayList<String> items;\n\n    public Player(Room room) {\n        this.room = room;\n        this.items = new ArrayList<>();\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n    }\n\n    public void take(String item) {\n        this.items.add(item);\n    }\n\n    public boolean has(String item) {\n        return this.items.contains(item);\n    }\n\n    public void drop(String item) {\n        this.items.remove(item);\n    }\n\n    public String inventory() {\n        if (this.items.isEmpty()) {\n            return \"You carry nothing.\";\n        }\n        String text = \"You carry: \";\n        for (int i = 0; i < this.items.size(); i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.items.get(i);\n        }\n        return text;\n    }\n}\n\n// ==== Exit.java ====\npublic class Exit {\n    private String direction;\n    private String target;\n    private String key;\n\n    public Exit(String direction, String target) {\n        this.direction = direction;\n        this.target = target;\n        this.key = \"\";\n    }\n\n    public String getDirection() {\n        return this.direction;\n    }\n\n    public String getTarget() {\n        return this.target;\n    }\n\n    public String getKey() {\n        return this.key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n    public boolean isLocked() {\n        return !this.key.isEmpty();\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        String goalRoom = \"\";\n        String goalItem = \"\";\n        String goalText = \"\";\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                } else if (parts[0].equals(\"exit\")) {\n                    findRoom(rooms, parts[1]).addExit(new Exit(parts[2], parts[3]));\n                } else if (parts[0].equals(\"item\")) {\n                    findRoom(rooms, parts[2]).addItem(parts[1]);\n                } else if (parts[0].equals(\"lock\")) {\n                    findRoom(rooms, parts[1]).lock(parts[2], parts[3]);\n                } else if (parts[0].equals(\"goal\")) {\n                    goalRoom = parts[1];\n                    goalItem = parts[2];\n                    goalText = parts[3];\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else if (command.startsWith(\"go \")) {\n                move(player, rooms, command.substring(3));\n            } else if (command.startsWith(\"take \")) {\n                take(player, command.substring(5));\n            } else if (command.startsWith(\"drop \")) {\n                drop(player, command.substring(5));\n            } else if (command.equals(\"inventory\")) {\n                System.out.println(player.inventory());\n            } else if (command.startsWith(\"use \")) {\n                use(player, command.substring(4));\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n\n            if (player.getRoom().getName().equals(goalRoom) && player.has(goalItem)) {\n                System.out.println(goalText);\n                System.out.println(\"You won in \" + player.getMoves() + \" moves.\");\n                break;\n            }\n        }\n    }\n\n    public static Room findRoom(ArrayList<Room> rooms, String name) {\n        for (Room room : rooms) {\n            if (room.getName().equals(name)) {\n                return room;\n            }\n        }\n        // Every room name in world.txt belongs to a room line, so this isn't reached.\n        return rooms.get(0);\n    }\n\n    public static void move(Player player, ArrayList<Room> rooms, String direction) {\n        String target = player.getRoom().getTarget(direction);\n        if (target.isEmpty()) {\n            System.out.println(\"You can't go \" + direction + \" from here.\");\n        } else if (player.getRoom().isLocked(direction)) {\n            System.out.println(\"The way \" + direction + \" is locked.\");\n        } else {\n            player.moveTo(findRoom(rooms, target));\n            System.out.println(player.getRoom());\n        }\n    }\n\n    public static void take(Player player, String item) {\n        Room room = player.getRoom();\n        if (room.hasItem(item)) {\n            room.removeItem(item);\n            player.take(item);\n            System.out.println(\"You take the \" + item + \".\");\n        } else {\n            System.out.println(\"There is no \" + item + \" here.\");\n        }\n    }\n\n    public static void drop(Player player, String item) {\n        if (player.has(item)) {\n            player.drop(item);\n            player.getRoom().addItem(item);\n            System.out.println(\"You drop the \" + item + \".\");\n        } else {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n        }\n    }\n\n    public static void use(Player player, String item) {\n        if (!player.has(item)) {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n            return;\n        }\n        String direction = player.getRoom().unlock(item);\n        if (direction.isEmpty()) {\n            System.out.println(\"Nothing happens.\");\n        } else {\n            System.out.println(\"You unlock the way \" + direction + \" with the \" + item + \".\");\n        }\n    }\n}\n\n// ==== Room.java ====\nimport java.util.ArrayList;\n\npublic class Room {\n    private String name;\n    private String description;\n    private ArrayList<Exit> exits;\n    private ArrayList<String> items;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n        this.exits = new ArrayList<>();\n        this.items = new ArrayList<>();\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public void addExit(Exit exit) {\n        this.exits.add(exit);\n    }\n\n    public String getTarget(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.getTarget();\n            }\n        }\n        return \"\";\n    }\n\n    public void lock(String direction, String key) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                exit.setKey(key);\n            }\n        }\n    }\n\n    public boolean isLocked(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.isLocked();\n            }\n        }\n        return false;\n    }\n\n    public String unlock(String item) {\n        for (Exit exit : this.exits) {\n            if (exit.isLocked() && exit.getKey().equals(item)) {\n                exit.setKey(\"\");\n                return exit.getDirection();\n            }\n        }\n        return \"\";\n    }\n\n    public void addItem(String item) {\n        this.items.add(item);\n    }\n\n    public boolean hasItem(String item) {\n        return this.items.contains(item);\n    }\n\n    public void removeItem(String item) {\n        this.items.remove(item);\n    }\n\n    public String toString() {\n        String text = \"[\" + this.name + \"]\\n\" + this.description;\n        if (!this.exits.isEmpty()) {\n            text += \"\\nExits: \";\n            for (int i = 0; i < this.exits.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.exits.get(i).getDirection();\n            }\n        }\n        if (!this.items.isEmpty()) {\n            text += \"\\nYou see: \";\n            for (int i = 0; i < this.items.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.items.get(i);\n            }\n        }\n        return text;\n    }\n}\n\n// ==== Player.java ====\nimport java.util.ArrayList;\n\npublic class Player {\n    private Room room;\n    private ArrayList<String> items;\n    private int moves;\n\n    public Player(Room room) {\n        this.room = room;\n        this.items = new ArrayList<>();\n        this.moves = 0;\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n        this.moves++;\n    }\n\n    public int getMoves() {\n        return this.moves;\n    }\n\n    public void take(String item) {\n        this.items.add(item);\n    }\n\n    public boolean has(String item) {\n        return this.items.contains(item);\n    }\n\n    public void drop(String item) {\n        this.items.remove(item);\n    }\n\n    public String inventory() {\n        if (this.items.isEmpty()) {\n            return \"You carry nothing.\";\n        }\n        String text = \"You carry: \";\n        for (int i = 0; i < this.items.size(); i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.items.get(i);\n        }\n        return text;\n    }\n}\n\n// ==== Exit.java ====\npublic class Exit {\n    private String direction;\n    private String target;\n    private String key;\n\n    public Exit(String direction, String target) {\n        this.direction = direction;\n        this.target = target;\n        this.key = \"\";\n    }\n\n    public String getDirection() {\n        return this.direction;\n    }\n\n    public String getTarget() {\n        return this.target;\n    }\n\n    public String getKey() {\n        return this.key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n    public boolean isLocked() {\n        return !this.key.isEmpty();\n    }\n}\n","hints":["`this.moves++;` in `moveTo` counts every move, wherever it comes from. `move` in `Main` only calls `moveTo` when the player really goes.","Put the check after the whole `if`/`else if` chain, at the end of the loop's body: `if (player.getRoom().getName().equals(goalRoom) && player.has(goalItem))`. If your loop goes on to the next command with `continue`, the end of the body is skipped. Put the check at the top of the loop instead, inside the `while` and before `nextLine()`. `quit` has already left the loop with `break`, so it's never checked.","Without a goal line, `goalRoom` stays `\"\"`, which no room is called, so the game goes on until `quit`."],"tests":[{"name":"Light the lamp","stdin":"go north\ntake oil\ngo east\ngo south\ngo east\ntake key\ngo west\ngo north\nuse key\ngo east\ngo up\nquit\n","files":{"world.txt":"room;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\nroom;Boathouse;An old rowboat lies upside down. It smells of tar.\nroom;Tower;Stone stairs climb into the dark.\nroom;Lamp Room;A great glass lens waits for a flame.\nexit;Shore;north;Cottage\nexit;Shore;east;Boathouse\nexit;Cottage;south;Shore\nexit;Cottage;east;Tower\nexit;Boathouse;west;Shore\nexit;Tower;west;Cottage\nexit;Tower;up;Lamp Room\nexit;Lamp Room;down;Tower\nitem;oil;Cottage\nitem;key;Boathouse\nlock;Cottage;east;key\ngoal;Lamp Room;oil;You fill the lamp with oil and light it. Its beam sweeps across the sea.\n"},"expect":"[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou see: oil\nYou take the oil.\nThe way east is locked.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Boathouse]\nAn old rowboat lies upside down. It smells of tar.\nExits: west\nYou see: key\nYou take the key.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou unlock the way east with the key.\n[Tower]\nStone stairs climb into the dark.\nExits: west, up\n[Lamp Room]\nA great glass lens waits for a flame.\nExits: down\nYou fill the lamp with oil and light it. Its beam sweeps across the sea.\nYou won in 7 moves.","hidden":false},{"name":"Arrive first, then fetch the item","stdin":"go north\ngo west\ngo east\ntake bucket\ngo west\nlook\nquit\n","files":{"world.txt":"room;Gate;A rusty gate stands open.\nroom;Orchard;Rows of old pear trees.\nroom;Well;A stone well with a bucket on a rope.\nexit;Gate;north;Orchard\nexit;Orchard;south;Gate\nexit;Orchard;east;Well\nexit;Well;west;Orchard\nitem;bucket;Well\ngoal;Orchard;bucket;You water the trees, and the leaves lift.\n"},"expect":"[Gate]\nA rusty gate stands open.\nExits: north\n[Orchard]\nRows of old pear trees.\nExits: south, east\nYou can't go west from here.\n[Well]\nA stone well with a bucket on a rope.\nExits: west\nYou see: bucket\nYou take the bucket.\n[Orchard]\nRows of old pear trees.\nExits: south, east\nYou water the trees, and the leaves lift.\nYou won in 3 moves.","hidden":true},{"name":"Win by taking the item","stdin":"go east\ngo west\ngo east\ntake map\ninventory\nquit\n","files":{"world.txt":"room;Dock;Ropes and crates everywhere.\nroom;Ship;The deck rocks gently.\nexit;Dock;east;Ship\nexit;Ship;west;Dock\nitem;map;Ship\ngoal;Ship;map;With the map in your hands, you can set sail.\n"},"expect":"[Dock]\nRopes and crates everywhere.\nExits: east\n[Ship]\nThe deck rocks gently.\nExits: west\nYou see: map\n[Dock]\nRopes and crates everywhere.\nExits: east\n[Ship]\nThe deck rocks gently.\nExits: west\nYou see: map\nYou take the map.\nWith the map in your hands, you can set sail.\nYou won in 3 moves.","hidden":true},{"name":"No goal in this world","stdin":"go down\ntake stone\ngo up\nquit\n","files":{"world.txt":"room;Hill;Wind bends the grass.\nroom;Valley;A stream runs between the stones.\nexit;Hill;down;Valley\nexit;Valley;up;Hill\nitem;stone;Valley\n"},"expect":"[Hill]\nWind bends the grass.\nExits: down\n[Valley]\nA stream runs between the stones.\nExits: up\nYou see: stone\nYou take the stone.\n[Hill]\nWind bends the grass.\nExits: down\nGoodbye.","hidden":true},{"name":"Counting moves","stdin":"","call":"Room hall = new Room(\"Hall\", \"Quiet.\");\nPlayer player = new Player(hall);\nSystem.out.println(player.getMoves());\nplayer.moveTo(new Room(\"Yard\", \"Sunny.\"));\nplayer.moveTo(hall);\nSystem.out.println(player.getMoves());\n","expect":"0\n2","hidden":true}],"require":[{"pattern":"getMoves\\s*\\(","message":"Print the count of moves with `getMoves()`."}],"forbid":[]},{"id":"save-and-load","title":"Saving and loading","task":"Last, the player can stop and come back later. `save` writes the state of the game to a file, `save.txt`, and `load` reads it back. The file uses the same style of lines as `world.txt`:\n\n```text\nat;Boathouse\nmoves;3\ncarry;oil\ncarry;key\nlock;Cottage;east;key\n```\n\n- `at;Boathouse`: the room the player is in. Always the first line.\n- `moves;3`: the player's count of moves. Always the second line.\n- `carry;oil`: one line for each item the player carries, in their order.\n- Then, for each room in the order of the world file: an `item;...` line for each item in it, as in `world.txt` (`item;rope;Shore`), and a `lock;...` line for each exit of it that is still locked (as above).\n\n### Writing a file\n\nModule 43 covers writing files in depth; here's the part you need. The class `PrintWriter` (`import java.io.PrintWriter;`) writes text into a file, with the same `println` you know from `System.out`. Open it in a `try (...)` as you open a Scanner for a file: the `try` also closes it at the end of the block, and only then is all the text in the file. Creating it makes the file, or empties it when it's already there.\n\n```java\ntry (PrintWriter writer = new PrintWriter(\"save.txt\")) {\n    writer.println(\"at;\" + player.getRoom().getName());\n} catch (Exception e) {\n    System.out.println(\"Error: \" + e.getMessage());\n}\n```\n\n### What to do\n\n1. Give `Player` the methods `getItems()` (returns the list), `clearItems()` and `setMoves(int moves)`. Give `Room` the methods `getItems()`, `getExits()`, `clearItems()` and `openAll()` (every exit's key becomes `\"\"`).\n2. The command `save` writes `save.txt` as described above and prints `Game saved.`\n3. The command `load` reads `save.txt`. It first empties the player's and every room's items and opens every exit, then goes through the lines: `at` moves the player there, `moves` sets the count with `setMoves` (the file's `at` line comes first, and moving the player there with `moveTo` adds 1, which this line then overwrites), `carry` gives the player the item, and `item` and `lock` lines work as in `world.txt`. Then it prints `Game loaded.` and the room the player is in. When there's no `save.txt`, it prints `There is no saved game.` and the game goes on as it was.\n\nWith the whole lighthouse world and the commands `go north`, `take oil`, `go south`, `go east`, `take key`, `save` and `quit`, the program writes the file above and prints:\n\n```\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou see: oil\nYou take the oil.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Boathouse]\nAn old rowboat lies upside down. It smells of tar.\nExits: west\nYou see: key\nYou take the key.\nGame saved.\nGoodbye.\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        String goalRoom = \"\";\n        String goalItem = \"\";\n        String goalText = \"\";\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                } else if (parts[0].equals(\"exit\")) {\n                    findRoom(rooms, parts[1]).addExit(new Exit(parts[2], parts[3]));\n                } else if (parts[0].equals(\"item\")) {\n                    findRoom(rooms, parts[2]).addItem(parts[1]);\n                } else if (parts[0].equals(\"lock\")) {\n                    findRoom(rooms, parts[1]).lock(parts[2], parts[3]);\n                } else if (parts[0].equals(\"goal\")) {\n                    goalRoom = parts[1];\n                    goalItem = parts[2];\n                    goalText = parts[3];\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else if (command.startsWith(\"go \")) {\n                move(player, rooms, command.substring(3));\n            } else if (command.startsWith(\"take \")) {\n                take(player, command.substring(5));\n            } else if (command.startsWith(\"drop \")) {\n                drop(player, command.substring(5));\n            } else if (command.equals(\"inventory\")) {\n                System.out.println(player.inventory());\n            } else if (command.startsWith(\"use \")) {\n                use(player, command.substring(4));\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n\n            if (player.getRoom().getName().equals(goalRoom) && player.has(goalItem)) {\n                System.out.println(goalText);\n                System.out.println(\"You won in \" + player.getMoves() + \" moves.\");\n                break;\n            }\n        }\n    }\n\n    public static Room findRoom(ArrayList<Room> rooms, String name) {\n        for (Room room : rooms) {\n            if (room.getName().equals(name)) {\n                return room;\n            }\n        }\n        // Every room name in world.txt belongs to a room line, so this isn't reached.\n        return rooms.get(0);\n    }\n\n    public static void move(Player player, ArrayList<Room> rooms, String direction) {\n        String target = player.getRoom().getTarget(direction);\n        if (target.isEmpty()) {\n            System.out.println(\"You can't go \" + direction + \" from here.\");\n        } else if (player.getRoom().isLocked(direction)) {\n            System.out.println(\"The way \" + direction + \" is locked.\");\n        } else {\n            player.moveTo(findRoom(rooms, target));\n            System.out.println(player.getRoom());\n        }\n    }\n\n    public static void take(Player player, String item) {\n        Room room = player.getRoom();\n        if (room.hasItem(item)) {\n            room.removeItem(item);\n            player.take(item);\n            System.out.println(\"You take the \" + item + \".\");\n        } else {\n            System.out.println(\"There is no \" + item + \" here.\");\n        }\n    }\n\n    public static void drop(Player player, String item) {\n        if (player.has(item)) {\n            player.drop(item);\n            player.getRoom().addItem(item);\n            System.out.println(\"You drop the \" + item + \".\");\n        } else {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n        }\n    }\n\n    public static void use(Player player, String item) {\n        if (!player.has(item)) {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n            return;\n        }\n        String direction = player.getRoom().unlock(item);\n        if (direction.isEmpty()) {\n            System.out.println(\"Nothing happens.\");\n        } else {\n            System.out.println(\"You unlock the way \" + direction + \" with the \" + item + \".\");\n        }\n    }\n}\n\n// ==== Room.java ====\nimport java.util.ArrayList;\n\npublic class Room {\n    private String name;\n    private String description;\n    private ArrayList<Exit> exits;\n    private ArrayList<String> items;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n        this.exits = new ArrayList<>();\n        this.items = new ArrayList<>();\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public void addExit(Exit exit) {\n        this.exits.add(exit);\n    }\n\n    public String getTarget(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.getTarget();\n            }\n        }\n        return \"\";\n    }\n\n    public void lock(String direction, String key) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                exit.setKey(key);\n            }\n        }\n    }\n\n    public boolean isLocked(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.isLocked();\n            }\n        }\n        return false;\n    }\n\n    public String unlock(String item) {\n        for (Exit exit : this.exits) {\n            if (exit.isLocked() && exit.getKey().equals(item)) {\n                exit.setKey(\"\");\n                return exit.getDirection();\n            }\n        }\n        return \"\";\n    }\n\n    public void addItem(String item) {\n        this.items.add(item);\n    }\n\n    public boolean hasItem(String item) {\n        return this.items.contains(item);\n    }\n\n    public void removeItem(String item) {\n        this.items.remove(item);\n    }\n\n    public String toString() {\n        String text = \"[\" + this.name + \"]\\n\" + this.description;\n        if (!this.exits.isEmpty()) {\n            text += \"\\nExits: \";\n            for (int i = 0; i < this.exits.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.exits.get(i).getDirection();\n            }\n        }\n        if (!this.items.isEmpty()) {\n            text += \"\\nYou see: \";\n            for (int i = 0; i < this.items.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.items.get(i);\n            }\n        }\n        return text;\n    }\n}\n\n// ==== Player.java ====\nimport java.util.ArrayList;\n\npublic class Player {\n    private Room room;\n    private ArrayList<String> items;\n    private int moves;\n\n    public Player(Room room) {\n        this.room = room;\n        this.items = new ArrayList<>();\n        this.moves = 0;\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n        this.moves++;\n    }\n\n    public int getMoves() {\n        return this.moves;\n    }\n\n    public void take(String item) {\n        this.items.add(item);\n    }\n\n    public boolean has(String item) {\n        return this.items.contains(item);\n    }\n\n    public void drop(String item) {\n        this.items.remove(item);\n    }\n\n    public String inventory() {\n        if (this.items.isEmpty()) {\n            return \"You carry nothing.\";\n        }\n        String text = \"You carry: \";\n        for (int i = 0; i < this.items.size(); i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.items.get(i);\n        }\n        return text;\n    }\n}\n\n// ==== Exit.java ====\npublic class Exit {\n    private String direction;\n    private String target;\n    private String key;\n\n    public Exit(String direction, String target) {\n        this.direction = direction;\n        this.target = target;\n        this.key = \"\";\n    }\n\n    public String getDirection() {\n        return this.direction;\n    }\n\n    public String getTarget() {\n        return this.target;\n    }\n\n    public String getKey() {\n        return this.key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n    public boolean isLocked() {\n        return !this.key.isEmpty();\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.io.PrintWriter;\nimport java.nio.file.Paths;\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Room> rooms = new ArrayList<>();\n        String goalRoom = \"\";\n        String goalItem = \"\";\n        String goalText = \"\";\n        try (Scanner file = new Scanner(Paths.get(\"world.txt\"))) {\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"room\")) {\n                    rooms.add(new Room(parts[1], parts[2]));\n                } else if (parts[0].equals(\"exit\")) {\n                    findRoom(rooms, parts[1]).addExit(new Exit(parts[2], parts[3]));\n                } else if (parts[0].equals(\"item\")) {\n                    findRoom(rooms, parts[2]).addItem(parts[1]);\n                } else if (parts[0].equals(\"lock\")) {\n                    findRoom(rooms, parts[1]).lock(parts[2], parts[3]);\n                } else if (parts[0].equals(\"goal\")) {\n                    goalRoom = parts[1];\n                    goalItem = parts[2];\n                    goalText = parts[3];\n                }\n            }\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n\n        Player player = new Player(rooms.get(0));\n        System.out.println(player.getRoom());\n        Scanner scanner = new Scanner(System.in);\n        while (scanner.hasNextLine()) {\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Goodbye.\");\n                break;\n            } else if (command.equals(\"look\")) {\n                System.out.println(player.getRoom());\n            } else if (command.startsWith(\"go \")) {\n                move(player, rooms, command.substring(3));\n            } else if (command.startsWith(\"take \")) {\n                take(player, command.substring(5));\n            } else if (command.startsWith(\"drop \")) {\n                drop(player, command.substring(5));\n            } else if (command.equals(\"inventory\")) {\n                System.out.println(player.inventory());\n            } else if (command.startsWith(\"use \")) {\n                use(player, command.substring(4));\n            } else if (command.equals(\"save\")) {\n                save(player, rooms);\n            } else if (command.equals(\"load\")) {\n                load(player, rooms);\n            } else {\n                System.out.println(\"I don't understand \\\"\" + command + \"\\\".\");\n            }\n\n            if (player.getRoom().getName().equals(goalRoom) && player.has(goalItem)) {\n                System.out.println(goalText);\n                System.out.println(\"You won in \" + player.getMoves() + \" moves.\");\n                break;\n            }\n        }\n    }\n\n    public static Room findRoom(ArrayList<Room> rooms, String name) {\n        for (Room room : rooms) {\n            if (room.getName().equals(name)) {\n                return room;\n            }\n        }\n        // Every room name in world.txt belongs to a room line, so this isn't reached.\n        return rooms.get(0);\n    }\n\n    public static void move(Player player, ArrayList<Room> rooms, String direction) {\n        String target = player.getRoom().getTarget(direction);\n        if (target.isEmpty()) {\n            System.out.println(\"You can't go \" + direction + \" from here.\");\n        } else if (player.getRoom().isLocked(direction)) {\n            System.out.println(\"The way \" + direction + \" is locked.\");\n        } else {\n            player.moveTo(findRoom(rooms, target));\n            System.out.println(player.getRoom());\n        }\n    }\n\n    public static void take(Player player, String item) {\n        Room room = player.getRoom();\n        if (room.hasItem(item)) {\n            room.removeItem(item);\n            player.take(item);\n            System.out.println(\"You take the \" + item + \".\");\n        } else {\n            System.out.println(\"There is no \" + item + \" here.\");\n        }\n    }\n\n    public static void drop(Player player, String item) {\n        if (player.has(item)) {\n            player.drop(item);\n            player.getRoom().addItem(item);\n            System.out.println(\"You drop the \" + item + \".\");\n        } else {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n        }\n    }\n\n    public static void use(Player player, String item) {\n        if (!player.has(item)) {\n            System.out.println(\"You aren't carrying the \" + item + \".\");\n            return;\n        }\n        String direction = player.getRoom().unlock(item);\n        if (direction.isEmpty()) {\n            System.out.println(\"Nothing happens.\");\n        } else {\n            System.out.println(\"You unlock the way \" + direction + \" with the \" + item + \".\");\n        }\n    }\n\n    public static void save(Player player, ArrayList<Room> rooms) {\n        try (PrintWriter writer = new PrintWriter(\"save.txt\")) {\n            writer.println(\"at;\" + player.getRoom().getName());\n            writer.println(\"moves;\" + player.getMoves());\n            for (String item : player.getItems()) {\n                writer.println(\"carry;\" + item);\n            }\n            for (Room room : rooms) {\n                for (String item : room.getItems()) {\n                    writer.println(\"item;\" + item + \";\" + room.getName());\n                }\n                for (Exit exit : room.getExits()) {\n                    if (exit.isLocked()) {\n                        writer.println(\"lock;\" + room.getName() + \";\" + exit.getDirection() + \";\" + exit.getKey());\n                    }\n                }\n            }\n            System.out.println(\"Game saved.\");\n        } catch (Exception e) {\n            System.out.println(\"Error: \" + e.getMessage());\n        }\n    }\n\n    public static void load(Player player, ArrayList<Room> rooms) {\n        try (Scanner file = new Scanner(Paths.get(\"save.txt\"))) {\n            player.clearItems();\n            for (Room room : rooms) {\n                room.clearItems();\n                room.openAll();\n            }\n            while (file.hasNextLine()) {\n                String[] parts = file.nextLine().split(\";\");\n                if (parts[0].equals(\"at\")) {\n                    player.moveTo(findRoom(rooms, parts[1]));\n                } else if (parts[0].equals(\"moves\")) {\n                    player.setMoves(Integer.valueOf(parts[1]));\n                } else if (parts[0].equals(\"carry\")) {\n                    player.take(parts[1]);\n                } else if (parts[0].equals(\"item\")) {\n                    findRoom(rooms, parts[2]).addItem(parts[1]);\n                } else if (parts[0].equals(\"lock\")) {\n                    findRoom(rooms, parts[1]).lock(parts[2], parts[3]);\n                }\n            }\n            System.out.println(\"Game loaded.\");\n            System.out.println(player.getRoom());\n        } catch (Exception e) {\n            System.out.println(\"There is no saved game.\");\n        }\n    }\n}\n\n// ==== Room.java ====\nimport java.util.ArrayList;\n\npublic class Room {\n    private String name;\n    private String description;\n    private ArrayList<Exit> exits;\n    private ArrayList<String> items;\n\n    public Room(String name, String description) {\n        this.name = name;\n        this.description = description;\n        this.exits = new ArrayList<>();\n        this.items = new ArrayList<>();\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public void addExit(Exit exit) {\n        this.exits.add(exit);\n    }\n\n    public ArrayList<Exit> getExits() {\n        return this.exits;\n    }\n\n    public String getTarget(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.getTarget();\n            }\n        }\n        return \"\";\n    }\n\n    public void lock(String direction, String key) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                exit.setKey(key);\n            }\n        }\n    }\n\n    public boolean isLocked(String direction) {\n        for (Exit exit : this.exits) {\n            if (exit.getDirection().equals(direction)) {\n                return exit.isLocked();\n            }\n        }\n        return false;\n    }\n\n    public String unlock(String item) {\n        for (Exit exit : this.exits) {\n            if (exit.isLocked() && exit.getKey().equals(item)) {\n                exit.setKey(\"\");\n                return exit.getDirection();\n            }\n        }\n        return \"\";\n    }\n\n    public void openAll() {\n        for (Exit exit : this.exits) {\n            exit.setKey(\"\");\n        }\n    }\n\n    public void addItem(String item) {\n        this.items.add(item);\n    }\n\n    public boolean hasItem(String item) {\n        return this.items.contains(item);\n    }\n\n    public void removeItem(String item) {\n        this.items.remove(item);\n    }\n\n    public ArrayList<String> getItems() {\n        return this.items;\n    }\n\n    public void clearItems() {\n        this.items.clear();\n    }\n\n    public String toString() {\n        String text = \"[\" + this.name + \"]\\n\" + this.description;\n        if (!this.exits.isEmpty()) {\n            text += \"\\nExits: \";\n            for (int i = 0; i < this.exits.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.exits.get(i).getDirection();\n            }\n        }\n        if (!this.items.isEmpty()) {\n            text += \"\\nYou see: \";\n            for (int i = 0; i < this.items.size(); i++) {\n                if (i > 0) {\n                    text += \", \";\n                }\n                text += this.items.get(i);\n            }\n        }\n        return text;\n    }\n}\n\n// ==== Player.java ====\nimport java.util.ArrayList;\n\npublic class Player {\n    private Room room;\n    private ArrayList<String> items;\n    private int moves;\n\n    public Player(Room room) {\n        this.room = room;\n        this.items = new ArrayList<>();\n        this.moves = 0;\n    }\n\n    public Room getRoom() {\n        return this.room;\n    }\n\n    public void moveTo(Room room) {\n        this.room = room;\n        this.moves++;\n    }\n\n    public int getMoves() {\n        return this.moves;\n    }\n\n    public void setMoves(int moves) {\n        this.moves = moves;\n    }\n\n    public void take(String item) {\n        this.items.add(item);\n    }\n\n    public boolean has(String item) {\n        return this.items.contains(item);\n    }\n\n    public void drop(String item) {\n        this.items.remove(item);\n    }\n\n    public ArrayList<String> getItems() {\n        return this.items;\n    }\n\n    public void clearItems() {\n        this.items.clear();\n    }\n\n    public String inventory() {\n        if (this.items.isEmpty()) {\n            return \"You carry nothing.\";\n        }\n        String text = \"You carry: \";\n        for (int i = 0; i < this.items.size(); i++) {\n            if (i > 0) {\n                text += \", \";\n            }\n            text += this.items.get(i);\n        }\n        return text;\n    }\n}\n\n// ==== Exit.java ====\npublic class Exit {\n    private String direction;\n    private String target;\n    private String key;\n\n    public Exit(String direction, String target) {\n        this.direction = direction;\n        this.target = target;\n        this.key = \"\";\n    }\n\n    public String getDirection() {\n        return this.direction;\n    }\n\n    public String getTarget() {\n        return this.target;\n    }\n\n    public String getKey() {\n        return this.key;\n    }\n\n    public void setKey(String key) {\n        this.key = key;\n    }\n\n    public boolean isLocked() {\n        return !this.key.isEmpty();\n    }\n}\n","hints":["Write `save(Player player, ArrayList<Room> rooms)` and `load(Player player, ArrayList<Room> rooms)` in `Main`. Saving is three loops inside the `try`: the player's items, then the rooms, and inside each room its items and its exits (`if (exit.isLocked())`).","Loading reads `save.txt` the way `main` reads `world.txt`: split each line at `;` and look at `parts[0]`. Empty the items and open the exits right after the file is opened, inside the `try`, so nothing changes when there's no file.","`Integer.valueOf(parts[1])` turns the moves into a number. Print `There is no saved game.` in the `catch` block."],"tests":[{"name":"Save the game","stdin":"go north\ntake oil\ngo south\ngo east\ntake key\nsave\nquit\n","files":{"world.txt":"room;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\nroom;Boathouse;An old rowboat lies upside down. It smells of tar.\nroom;Tower;Stone stairs climb into the dark.\nroom;Lamp Room;A great glass lens waits for a flame.\nexit;Shore;north;Cottage\nexit;Shore;east;Boathouse\nexit;Cottage;south;Shore\nexit;Cottage;east;Tower\nexit;Boathouse;west;Shore\nexit;Tower;west;Cottage\nexit;Tower;up;Lamp Room\nexit;Lamp Room;down;Tower\nitem;oil;Cottage\nitem;key;Boathouse\nlock;Cottage;east;key\ngoal;Lamp Room;oil;You fill the lamp with oil and light it. Its beam sweeps across the sea.\n"},"writes":{"save.txt":"at;Boathouse\nmoves;3\ncarry;oil\ncarry;key\nlock;Cottage;east;key"},"expect":"[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\nYou see: oil\nYou take the oil.\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\n[Boathouse]\nAn old rowboat lies upside down. It smells of tar.\nExits: west\nYou see: key\nYou take the key.\nGame saved.\nGoodbye.","hidden":false},{"name":"Load a saved game","stdin":"load\ninventory\ngo west\ngo south\ntake oil\ngo north\ngo east\ngo up\nquit\n","files":{"world.txt":"room;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\nroom;Boathouse;An old rowboat lies upside down. It smells of tar.\nroom;Tower;Stone stairs climb into the dark.\nroom;Lamp Room;A great glass lens waits for a flame.\nexit;Shore;north;Cottage\nexit;Shore;east;Boathouse\nexit;Cottage;south;Shore\nexit;Cottage;east;Tower\nexit;Boathouse;west;Shore\nexit;Tower;west;Cottage\nexit;Tower;up;Lamp Room\nexit;Lamp Room;down;Tower\nitem;oil;Cottage\nitem;key;Boathouse\nlock;Cottage;east;key\ngoal;Lamp Room;oil;You fill the lamp with oil and light it. Its beam sweeps across the sea.\n","save.txt":"at;Tower\nmoves;4\ncarry;key\nitem;oil;Shore\n"},"expect":"[Shore]\nCold waves break on gray pebbles.\nExits: north, east\nGame loaded.\n[Tower]\nStone stairs climb into the dark.\nExits: west, up\nYou carry: key\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\n[Shore]\nCold waves break on gray pebbles.\nExits: north, east\nYou see: oil\nYou take the oil.\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south, east\n[Tower]\nStone stairs climb into the dark.\nExits: west, up\n[Lamp Room]\nA great glass lens waits for a flame.\nExits: down\nYou fill the lamp with oil and light it. Its beam sweeps across the sea.\nYou won in 9 moves.","hidden":false},{"name":"No saved game","stdin":"take shell\nload\ninventory\ngo north\nquit\n","files":{"world.txt":"room;Shore;Cold waves break on gray pebbles.\nroom;Cottage;A keeper's cottage. The stove is cold.\nexit;Shore;north;Cottage\nexit;Cottage;south;Shore\nitem;shell;Shore\n"},"expect":"[Shore]\nCold waves break on gray pebbles.\nExits: north\nYou see: shell\nYou take the shell.\nThere is no saved game.\nYou carry: shell\n[Cottage]\nA keeper's cottage. The stove is cold.\nExits: south\nGoodbye.","hidden":true},{"name":"Save, change and load again","stdin":"take ladder\ngo north\nsave\nuse ladder\ndrop ladder\ntake hat\ngo up\nload\ngo up\ninventory\ngo south\nlook\nquit\n","files":{"world.txt":"room;Yard;Chickens peck at the ground.\nroom;Barn;Hay is piled up to the roof.\nroom;Loft;A small window looks over the fields.\nexit;Yard;north;Barn\nexit;Barn;south;Yard\nexit;Barn;up;Loft\nexit;Loft;down;Barn\nitem;ladder;Yard\nitem;egg;Yard\nitem;hat;Barn\nlock;Barn;up;ladder\n"},"writes":{"save.txt":"at;Barn\nmoves;1\ncarry;ladder\nitem;egg;Yard\nitem;hat;Barn\nlock;Barn;up;ladder"},"expect":"[Yard]\nChickens peck at the ground.\nExits: north\nYou see: ladder, egg\nYou take the ladder.\n[Barn]\nHay is piled up to the roof.\nExits: south, up\nYou see: hat\nGame saved.\nYou unlock the way up with the ladder.\nYou drop the ladder.\nYou take the hat.\n[Loft]\nA small window looks over the fields.\nExits: down\nGame loaded.\n[Barn]\nHay is piled up to the roof.\nExits: south, up\nYou see: hat\nThe way up is locked.\nYou carry: ladder\n[Yard]\nChickens peck at the ground.\nExits: north\nYou see: egg\n[Yard]\nChickens peck at the ground.\nExits: north\nYou see: egg\nGoodbye.","hidden":true}],"require":[{"pattern":"new\\s+PrintWriter\\s*\\(","message":"Write `save.txt` with a `PrintWriter`."}],"forbid":[]}]}