{"id":"larger-programs-planning","slug":"planning","title":"From a description to a plan","text":"The MOOC's section 7.3 is a set of larger programming exercises. Larger doesn't mean new tools: every program in this module uses things you've already learned. What changes is the size. A program of a hundred lines can't be written from top to bottom and then run for the first time. It needs a plan, and it's built one small piece at a time, with a run after every piece. This module does that for three kinds of programs that come up again and again, and then looks back at the whole of Java Programming I.\n\n### From a description to its parts\n\nPrograms usually start as a description in plain words. Here's one:\n\n> A board-game club meets once a week. At the end of the evening, someone types in the games that were played, one per line, like `Azul,35`: the game's name and how many minutes it took. An empty line ends the list. The program then prints how many games were played, how many minutes they took in all, and the longest game.\n\nRead it and look for three kinds of things:\n\n| Look for | In this description | It becomes |\n| --- | --- | --- |\n| A thing with values of its own | a game, with a name and minutes | a class for the data: `Game` |\n| Questions about all of them together | how many, how long in all, the longest | a class for the logic: `GameNight`, with a list of games and a method for each question |\n| What's typed and what's printed | lines like `Azul,35`, the empty line, the results | the part that reads input and prints: here, `main` |\n\nThis is the split of module 25. The logic class doesn't read or print anything: its methods take values as parameters and give answers back as return values, and only the input part talks with the user. Module 25 put that part in a `UserInterface` class. A program that reads once and then prints once can keep it in `main`.\n\n### One piece at a time\n\nBuild the parts in the order they depend on each other: the data first, then the logic, and the reading and printing last. Run the program after each piece.\n\n1. `Game`: a constructor, getters and a toString. Try it with `System.out.println(new Game(\"Azul\", 35));`.\n2. `GameNight`: the list, `add` and `count` first. Try them with a few calls in `main`, then add `totalMinutes` and `longest`, one at a time.\n3. The input part: a loop that reads lines until an empty one, splits each line and adds a Game.\n\nWhile `GameNight` is being written, `main` is a **test drive**: a few calls whose answers you can check by hand.\n\n```java\nGameNight night = new GameNight();\nnight.add(new Game(\"Azul\", 35));\nnight.add(new Game(\"Carcassonne\", 50));\nSystem.out.println(night.count() + \" \" + night.totalMinutes());\nSystem.out.println(night.longest());\n```\n\nA method you haven't written yet can get a **placeholder**: a body that only returns something of the right type, such as `return 0;` or `return null;`. Then the class compiles, and the methods that are ready can be tried. (A method that returns a value can't be left empty: javac says `missing return statement`.) Come back to every placeholder, though: its answer is wrong.\n\nWhen each piece works on its own, `main` gets its real job. Here's the finished program:\n\n```java\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        GameNight night = new GameNight();\n        while (true) {\n            String line = scanner.nextLine();\n            if (line.isEmpty()) {\n                break;\n            }\n            String[] parts = line.split(\",\");\n            night.add(new Game(parts[0], Integer.valueOf(parts[1])));\n        }\n        System.out.println(\"Games played: \" + night.count());\n        System.out.println(\"Minutes in all: \" + night.totalMinutes());\n        System.out.println(\"Longest: \" + night.longest());\n    }\n}\n// ==== Game.java ====\npublic class Game {\n    private String name;\n    private int minutes;\n\n    public Game(String name, int minutes) {\n        this.name = name;\n        this.minutes = minutes;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public int getMinutes() {\n        return this.minutes;\n    }\n\n    public String toString() {\n        return this.name + \" (\" + this.minutes + \" min)\";\n    }\n}\n// ==== GameNight.java ====\nimport java.util.ArrayList;\n\npublic class GameNight {\n    private ArrayList<Game> games;\n\n    public GameNight() {\n        this.games = new ArrayList<>();\n    }\n\n    public void add(Game game) {\n        this.games.add(game);\n    }\n\n    public int count() {\n        return this.games.size();\n    }\n\n    public int totalMinutes() {\n        int total = 0;\n        for (Game game : this.games) {\n            total += game.getMinutes();\n        }\n        return total;\n    }\n\n    public Game longest() {\n        Game longest = null;\n        for (Game game : this.games) {\n            if (longest == null || game.getMinutes() > longest.getMinutes()) {\n                longest = game;\n            }\n        }\n        return longest;\n    }\n}\n```\n\n```input\nAzul,35\nCarcassonne,50\nHanabi,25\nCodenames,20\n\n```\n\n```output\nGames played: 4\nMinutes in all: 130\nLongest: Carcassonne (50 min)\n```\n\n- Each class is small, and each has one job. `Game` knows one game, `GameNight` answers questions about all of them, and `main` reads and prints.\n- `longest` starts from `null`, which means \"no game yet\", so the first game always becomes the longest so far. With no games at all, it returns `null`.\n- If the program misbehaves now, the classes are already known to work from their test drives, so the mistake is most likely in the part written last.\n","task":"Friends plan a movie marathon: films watched one after another. The plan is the same as for the game night: `Film` for the data (ready), `Marathon` for the logic, and `main` for the input (the next challenge). `Marathon.java` has placeholders that compile but give wrong answers. Write its four methods:\n\n- `add(Film film)` adds the film to the list.\n- `count()` returns how many films there are.\n- `totalMinutes()` returns the films' minutes added up.\n- `shortest()` returns the shortest film, or `null` when there are no films. If two films are equally short, it returns the one added first.\n\nMarathon doesn't print anything. `Main.java` is a test drive, and it prints:\n\n```\n0 0 null\n3 315\nPaper Moons (95 min)\n```\n","kind":"code","seed":"// ==== Marathon.java ====\nimport java.util.ArrayList;\n\npublic class Marathon {\n    private ArrayList<Film> films;\n\n    public Marathon() {\n        this.films = new ArrayList<>();\n    }\n\n    public void add(Film film) {\n        // Replace the placeholders with the real code.\n    }\n\n    public int count() {\n        return 0;\n    }\n\n    public int totalMinutes() {\n        return 0;\n    }\n\n    public Film shortest() {\n        return null;\n    }\n}\n\n// ==== Film.java ====\npublic class Film {\n    private String title;\n    private int minutes;\n\n    public Film(String title, int minutes) {\n        this.title = title;\n        this.minutes = minutes;\n    }\n\n    public String getTitle() {\n        return this.title;\n    }\n\n    public int getMinutes() {\n        return this.minutes;\n    }\n\n    public String toString() {\n        return this.title + \" (\" + this.minutes + \" min)\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Marathon marathon = new Marathon();\n        System.out.println(marathon.count() + \" \" + marathon.totalMinutes() + \" \" + marathon.shortest());\n        marathon.add(new Film(\"Night Train\", 118));\n        marathon.add(new Film(\"Paper Moons\", 95));\n        marathon.add(new Film(\"Summer Rain\", 102));\n        System.out.println(marathon.count() + \" \" + marathon.totalMinutes());\n        System.out.println(marathon.shortest());\n    }\n}\n","solution":"// ==== Marathon.java ====\nimport java.util.ArrayList;\n\npublic class Marathon {\n    private ArrayList<Film> films;\n\n    public Marathon() {\n        this.films = new ArrayList<>();\n    }\n\n    public void add(Film film) {\n        this.films.add(film);\n    }\n\n    public int count() {\n        return this.films.size();\n    }\n\n    public int totalMinutes() {\n        int total = 0;\n        for (Film film : this.films) {\n            total += film.getMinutes();\n        }\n        return total;\n    }\n\n    public Film shortest() {\n        Film shortest = null;\n        for (Film film : this.films) {\n            if (shortest == null || film.getMinutes() < shortest.getMinutes()) {\n                shortest = film;\n            }\n        }\n        return shortest;\n    }\n}\n\n// ==== Film.java ====\npublic class Film {\n    private String title;\n    private int minutes;\n\n    public Film(String title, int minutes) {\n        this.title = title;\n        this.minutes = minutes;\n    }\n\n    public String getTitle() {\n        return this.title;\n    }\n\n    public int getMinutes() {\n        return this.minutes;\n    }\n\n    public String toString() {\n        return this.title + \" (\" + this.minutes + \" min)\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Marathon marathon = new Marathon();\n        System.out.println(marathon.count() + \" \" + marathon.totalMinutes() + \" \" + marathon.shortest());\n        marathon.add(new Film(\"Night Train\", 118));\n        marathon.add(new Film(\"Paper Moons\", 95));\n        marathon.add(new Film(\"Summer Rain\", 102));\n        System.out.println(marathon.count() + \" \" + marathon.totalMinutes());\n        System.out.println(marathon.shortest());\n    }\n}\n","hints":["`add` puts the film in `this.films`, and `count` is the list's `size()`. `totalMinutes` adds up `film.getMinutes()` in a for-each loop.","For `shortest`, start with `Film shortest = null;` and go through the list. Replace it when it's still `null`, or when the film has fewer minutes (`<`, so that the first of two equal films stays). Return it after the loop."],"tests":[{"name":"The test drive","stdin":"","expect":"0 0 null\n3 315\nPaper Moons (95 min)","hidden":false},{"name":"Test 2","stdin":"","call":"Marathon m = new Marathon();\nm.add(new Film(\"Owl Song\", 90));\nm.add(new Film(\"Blue Hour\", 90));\nm.add(new Film(\"The Long Road\", 200));\nSystem.out.println(m.count() + \" \" + m.totalMinutes() + \" \" + m.shortest());\n","expect":"3 380 Owl Song (90 min)","hidden":true},{"name":"Test 3","stdin":"","call":"Marathon m = new Marathon();\nm.add(new Film(\"Solo\", 61));\nSystem.out.println(m.shortest() + \" \" + m.count());\nm.add(new Film(\"Short Cut\", 12));\nSystem.out.println(m.shortest().getTitle() + \" \" + m.totalMinutes());\n","expect":"Solo (61 min) 1\nShort Cut 73","hidden":true}],"require":[],"forbid":[{"pattern":"\\bclass\\s+Marathon\\b[\\s\\S]*\\bSystem\\s*\\.\\s*out\\b[\\s\\S]*\\bclass\\s+Film\\b","message":"Marathon doesn't print. Its methods return their answers, and `main` prints them."}],"more":[{"task":"Now the input part of the movie marathon. `Film.java` and `Marathon.java` are ready. In `Main.java`, read films until an empty line. Each line has a title and a length in minutes, separated by a comma, like `Night Train,118`. Add a Film to the marathon for each line. Then, using the marathon's methods (`count()`, `totalMinutes()` and `shortest()`), print:\n\n- `Films: ` and the number of films,\n- `Watching time: ` and the total as hours and minutes, like `5 h 15 min`: always both numbers, such as `3 h 0 min`,\n- `Shortest: ` and the shortest film.\n\nIf no films were typed, print only `No films`. For the input shown, the program prints:\n\n```\nFilms: 3\nWatching time: 5 h 15 min\nShortest: Paper Moons (95 min)\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        Marathon marathon = new Marathon();\n        // Read the films here, and then print the results.\n    }\n}\n\n// ==== Film.java ====\npublic class Film {\n    private String title;\n    private int minutes;\n\n    public Film(String title, int minutes) {\n        this.title = title;\n        this.minutes = minutes;\n    }\n\n    public String getTitle() {\n        return this.title;\n    }\n\n    public int getMinutes() {\n        return this.minutes;\n    }\n\n    public String toString() {\n        return this.title + \" (\" + this.minutes + \" min)\";\n    }\n}\n\n// ==== Marathon.java ====\nimport java.util.ArrayList;\n\npublic class Marathon {\n    private ArrayList<Film> films;\n\n    public Marathon() {\n        this.films = new ArrayList<>();\n    }\n\n    public void add(Film film) {\n        this.films.add(film);\n    }\n\n    public int count() {\n        return this.films.size();\n    }\n\n    public int totalMinutes() {\n        int total = 0;\n        for (Film film : this.films) {\n            total += film.getMinutes();\n        }\n        return total;\n    }\n\n    public Film shortest() {\n        Film shortest = null;\n        for (Film film : this.films) {\n            if (shortest == null || film.getMinutes() < shortest.getMinutes()) {\n                shortest = film;\n            }\n        }\n        return shortest;\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        Marathon marathon = new Marathon();\n        // Read the films here, and then print the results.\n        while (true) {\n            String line = scanner.nextLine();\n            if (line.isEmpty()) {\n                break;\n            }\n            String[] parts = line.split(\",\");\n            marathon.add(new Film(parts[0], Integer.valueOf(parts[1])));\n        }\n        if (marathon.count() == 0) {\n            System.out.println(\"No films\");\n        } else {\n            int total = marathon.totalMinutes();\n            System.out.println(\"Films: \" + marathon.count());\n            System.out.println(\"Watching time: \" + total / 60 + \" h \" + total % 60 + \" min\");\n            System.out.println(\"Shortest: \" + marathon.shortest());\n        }\n    }\n}\n\n// ==== Film.java ====\npublic class Film {\n    private String title;\n    private int minutes;\n\n    public Film(String title, int minutes) {\n        this.title = title;\n        this.minutes = minutes;\n    }\n\n    public String getTitle() {\n        return this.title;\n    }\n\n    public int getMinutes() {\n        return this.minutes;\n    }\n\n    public String toString() {\n        return this.title + \" (\" + this.minutes + \" min)\";\n    }\n}\n\n// ==== Marathon.java ====\nimport java.util.ArrayList;\n\npublic class Marathon {\n    private ArrayList<Film> films;\n\n    public Marathon() {\n        this.films = new ArrayList<>();\n    }\n\n    public void add(Film film) {\n        this.films.add(film);\n    }\n\n    public int count() {\n        return this.films.size();\n    }\n\n    public int totalMinutes() {\n        int total = 0;\n        for (Film film : this.films) {\n            total += film.getMinutes();\n        }\n        return total;\n    }\n\n    public Film shortest() {\n        Film shortest = null;\n        for (Film film : this.films) {\n            if (shortest == null || film.getMinutes() < shortest.getMinutes()) {\n                shortest = film;\n            }\n        }\n        return shortest;\n    }\n}\n","hints":["The loop of the game night's `main`: read a line, `break` when it `isEmpty()`, split it at the comma, and add `new Film(parts[0], Integer.valueOf(parts[1]))` to the marathon.","After the loop, check `marathon.count() == 0` first. The hours are `total / 60`, and the minutes left over are `total % 60`."],"tests":[{"name":"Test 1","stdin":"Night Train,118\nPaper Moons,95\nSummer Rain,102\n\n","expect":"Films: 3\nWatching time: 5 h 15 min\nShortest: Paper Moons (95 min)","hidden":false},{"name":"Test 2","stdin":"\n","expect":"No films","hidden":true},{"name":"Test 3","stdin":"The Long Way Home,180\n\n","expect":"Films: 1\nWatching time: 3 h 0 min\nShortest: The Long Way Home (180 min)","hidden":true},{"name":"Test 4","stdin":"Owl Song,45\nBlue Hour,20\nTiny Lights,20\n\n","expect":"Films: 3\nWatching time: 1 h 25 min\nShortest: Blue Hour (20 min)","hidden":true}],"require":[{"pattern":"\\.totalMinutes\\s*\\(\\s*\\)","message":"Use the Marathon's methods for the answers: `marathon.totalMinutes()` gives the total."},{"pattern":"\\.shortest\\s*\\(\\s*\\)","message":"Use the Marathon's methods for the answers: `marathon.shortest()` gives the shortest film."}],"forbid":[]},{"task":"A bus timetable is being built one piece at a time. Times are written as numbers, so 615 is 6:15. `nextAfter` is written, and `count` is still a placeholder. What does this test drive print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        Timetable bus = new Timetable();\n        bus.add(615);\n        bus.add(640);\n        bus.add(710);\n        System.out.println(bus.nextAfter(600));\n        System.out.println(bus.nextAfter(640));\n        System.out.println(bus.nextAfter(800));\n        System.out.println(bus.count());\n    }\n}\n// ==== Timetable.java ====\nimport java.util.ArrayList;\n\npublic class Timetable {\n    private ArrayList<Integer> departures;\n\n    public Timetable() {\n        this.departures = new ArrayList<>();\n    }\n\n    public void add(int time) {\n        this.departures.add(time);\n    }\n\n    public int nextAfter(int time) {\n        for (int departure : this.departures) {\n            if (departure > time) {\n                return departure;\n            }\n        }\n        return -1;\n    }\n\n    public int count() {\n        return 0;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        Timetable bus = new Timetable();\n        bus.add(615);\n        bus.add(640);\n        bus.add(710);\n        System.out.println(bus.nextAfter(600));\n        System.out.println(bus.nextAfter(640));\n        System.out.println(bus.nextAfter(800));\n        System.out.println(bus.count());\n    }\n}\n// ==== Timetable.java ====\nimport java.util.ArrayList;\n\npublic class Timetable {\n    private ArrayList<Integer> departures;\n\n    public Timetable() {\n        this.departures = new ArrayList<>();\n    }\n\n    public void add(int time) {\n        this.departures.add(time);\n    }\n\n    public int nextAfter(int time) {\n        for (int departure : this.departures) {\n            if (departure > time) {\n                return departure;\n            }\n        }\n        return -1;\n    }\n\n    public int count() {\n        return 0;\n    }\n}\n","hints":["`nextAfter` returns the first departure that's later than the time. Is 640 later than 640?","When no departure is later, the loop ends and the method returns -1. `count` returns 0 whatever the list holds: it's a placeholder."],"tests":[{"name":"Output","stdin":"","expect":"615\n710\n-1\n0","hidden":false}],"require":[],"forbid":[],"lines":["615","710","-1","0"]}]}