{"id":"paradigms-procedural-commands","slug":"procedural-commands","title":"A command program, procedurally","text":"Programs that take commands are where the two styles differ the most. Module 25 wrote them both ways without naming the styles: first with everything in `main`, then with the work in a class. This step and the next one look at that change as a change of paradigm.\n\nHere's a box office that sells tickets for one concert, in a hall of 200 seats, written procedurally. Everything is in `main`: the program's state is two local variables, and its rules are `if`s inside the branches:\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        int seats = 200;\n        int sold = 0;\n        while (true) {\n            System.out.print(\"> \");\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Box office closed\");\n                break;\n            }\n            if (command.equals(\"sell\")) {\n                int count = Integer.valueOf(scanner.nextLine());\n                if (sold + count > seats) {\n                    System.out.println(\"Sorry, tickets left: \" + (seats - sold));\n                } else {\n                    sold = sold + count;\n                    System.out.println(\"Sold! Tickets left: \" + (seats - sold));\n                }\n            } else if (command.equals(\"refund\")) {\n                int count = Integer.valueOf(scanner.nextLine());\n                if (count > sold) {\n                    System.out.println(\"Sorry, tickets sold: \" + sold);\n                } else {\n                    sold = sold - count;\n                    System.out.println(\"Refunded! Tickets left: \" + (seats - sold));\n                }\n            } else if (command.equals(\"status\")) {\n                System.out.println(\"Sold: \" + sold + \", left: \" + (seats - sold));\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n    }\n}\n```\n\n```input\nsell\n150\nsell\n80\nsell\n50\nrefund\n20\nrefund\n300\nstatus\nquit\n```\n\n```output\n> Sold! Tickets left: 50\n> Sorry, tickets left: 50\n> Sold! Tickets left: 0\n> Refunded! Tickets left: 20\n> Sorry, tickets sold: 180\n> Sold: 180, left: 20\n> Box office closed\n```\n\n- `seats` and `sold` are the program's state. They're created before the loop, so they last from one command to the next (module 25).\n- `sell` checks the limit before it changes anything. When the tickets asked for don't fit, it sells none of them and says how many are left. `refund` checks the other end: it can't take back more tickets than were sold, so `sold` never goes below 0.\n- The number of tickets left isn't kept anywhere. It's worked out from the two variables, as `seats - sold`, in four places.\n\n### How it grows\n\nThis is easy to follow: one loop, one branch per command, read from top to bottom. The trouble comes as the program grows:\n\n- The rules are mixed in with the reading and the printing. To find out what a refund may do, you read the `refund` branch past its `nextLine` and between its `println`s.\n- Every branch that changes `sold` needs its own copy of a check. A new command that sells tickets in another way needs one too, and if a copy is missing or wrong, the concert is overbooked. Nothing warns you.\n- A second concert, such as an afternoon show with its own seats, means two more variables and a copy of every branch that uses them.\n- The only way to try the rules is to run the whole command loop on a list of commands, whether they're typed or fed to the program as input. The rules can't be tried on their own.\n\nStatic methods would help a little, as in step 1: `sold = sell(sold, count, seats);` could keep the check in one place. But a method returns only one value, so that call can't also say whether the sale went through. `main` needs to know that to print the right message, and it would have to find out by comparing `sold` before and after the call. The next step moves the state and the rules into a class instead.\n","task":"A bike-share station has 12 slots, and in the morning 5 of them hold a bike. The program is procedural: its state is two local variables, `slots` and `bikes`, and the commands `status` and `close` are ready. Add the two commands that change the state, as `else if` branches before the last `else`. Each one checks its own limit before it changes anything:\n\n- `rent`: the next line is a number of bikes. If the station has that many, take them and print `Enjoy the ride! Bikes here: ` and the bikes left. If it doesn't, change nothing and print `Sorry, bikes here: ` and the bikes.\n- `return`: the next line is a number of bikes. If there are that many free slots, put the bikes back and print `Thanks! Bikes here: ` and the bikes. If not, change nothing and print `Sorry, free slots: ` and the free slots.\n\nFor the input shown, the program prints:\n\n```\n> Enjoy the ride! Bikes here: 2\n> Sorry, bikes here: 2\n> Thanks! Bikes here: 11\n> Sorry, free slots: 1\n> Bikes: 11, free slots: 1\n> Station closed\n```\n","kind":"code","seed":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int slots = 12;\n        int bikes = 5;\n        while (true) {\n            System.out.print(\"> \");\n            String command = scanner.nextLine();\n            if (command.equals(\"close\")) {\n                System.out.println(\"Station closed\");\n                break;\n            }\n            // Add the branches for rent and return before the last else.\n            if (command.equals(\"status\")) {\n                System.out.println(\"Bikes: \" + bikes + \", free slots: \" + (slots - bikes));\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n    }\n}\n","solution":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int slots = 12;\n        int bikes = 5;\n        while (true) {\n            System.out.print(\"> \");\n            String command = scanner.nextLine();\n            if (command.equals(\"close\")) {\n                System.out.println(\"Station closed\");\n                break;\n            }\n            // Add the branches for rent and return before the last else.\n            if (command.equals(\"status\")) {\n                System.out.println(\"Bikes: \" + bikes + \", free slots: \" + (slots - bikes));\n            } else if (command.equals(\"rent\")) {\n                int count = Integer.valueOf(scanner.nextLine());\n                if (count > bikes) {\n                    System.out.println(\"Sorry, bikes here: \" + bikes);\n                } else {\n                    bikes = bikes - count;\n                    System.out.println(\"Enjoy the ride! Bikes here: \" + bikes);\n                }\n            } else if (command.equals(\"return\")) {\n                int count = Integer.valueOf(scanner.nextLine());\n                if (bikes + count > slots) {\n                    System.out.println(\"Sorry, free slots: \" + (slots - bikes));\n                } else {\n                    bikes = bikes + count;\n                    System.out.println(\"Thanks! Bikes here: \" + bikes);\n                }\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n    }\n}\n","hints":["Each new branch is shaped like the box office's `sell`: `} else if (command.equals(\"rent\")) {`, then the number from the next line, with `Integer.valueOf(scanner.nextLine())`.","Check the limit first, and change `bikes` only when the command is allowed. `rent` is refused when `count > bikes`, and `return` when `bikes + count > slots`. The free slots are `slots - bikes`."],"tests":[{"name":"Test 1","stdin":"rent\n3\nrent\n4\nreturn\n9\nreturn\n3\nstatus\nclose\n","expect":"> Enjoy the ride! Bikes here: 2\n> Sorry, bikes here: 2\n> Thanks! Bikes here: 11\n> Sorry, free slots: 1\n> Bikes: 11, free slots: 1\n> Station closed","hidden":false},{"name":"Test 2","stdin":"status\nrent\n5\nrent\n1\nreturn\n12\nreturn\n1\nrepair\nstatus\nclose\n","expect":"> Bikes: 5, free slots: 7\n> Enjoy the ride! Bikes here: 0\n> Sorry, bikes here: 0\n> Thanks! Bikes here: 12\n> Sorry, free slots: 0\n> Unknown command: repair\n> Bikes: 12, free slots: 0\n> Station closed","hidden":true},{"name":"Test 3","stdin":"return\n7\nreturn\n1\nrent\n12\nrent\n13\nstatus\nclose\n","expect":"> Thanks! Bikes here: 12\n> Sorry, free slots: 0\n> Enjoy the ride! Bikes here: 0\n> Sorry, bikes here: 0\n> Bikes: 0, free slots: 12\n> Station closed","hidden":true}],"require":[],"forbid":[],"more":[{"task":"The box office added the command `one`, which sells a single ticket, for people who buy at the door. But once the concert is sold out, `one` goes on selling, and `status` shows more tickets sold than there are seats. Fix the `one` branch so that it follows the same rule as `sell`: when no ticket is left, it sells nothing and prints `Sorry, sold out`. For the input shown, the program prints:\n\n```\n> Sold! Tickets left: 2\n> Sold! Tickets left: 1\n> Sold! Tickets left: 0\n> Sorry, sold out\n> Sold: 200, left: 0\n> Box office closed\n```\n","kind":"code","seed":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int seats = 200;\n        int sold = 0;\n        while (true) {\n            System.out.print(\"> \");\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Box office closed\");\n                break;\n            }\n            if (command.equals(\"sell\")) {\n                int count = Integer.valueOf(scanner.nextLine());\n                if (sold + count > seats) {\n                    System.out.println(\"Sorry, tickets left: \" + (seats - sold));\n                } else {\n                    sold = sold + count;\n                    System.out.println(\"Sold! Tickets left: \" + (seats - sold));\n                }\n            } else if (command.equals(\"one\")) {\n                sold++;\n                System.out.println(\"Sold! Tickets left: \" + (seats - sold));\n            } else if (command.equals(\"refund\")) {\n                int count = Integer.valueOf(scanner.nextLine());\n                if (count > sold) {\n                    System.out.println(\"Sorry, tickets sold: \" + sold);\n                } else {\n                    sold = sold - count;\n                    System.out.println(\"Refunded! Tickets left: \" + (seats - sold));\n                }\n            } else if (command.equals(\"status\")) {\n                System.out.println(\"Sold: \" + sold + \", left: \" + (seats - sold));\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n    }\n}\n","solution":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int seats = 200;\n        int sold = 0;\n        while (true) {\n            System.out.print(\"> \");\n            String command = scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Box office closed\");\n                break;\n            }\n            if (command.equals(\"sell\")) {\n                int count = Integer.valueOf(scanner.nextLine());\n                if (sold + count > seats) {\n                    System.out.println(\"Sorry, tickets left: \" + (seats - sold));\n                } else {\n                    sold = sold + count;\n                    System.out.println(\"Sold! Tickets left: \" + (seats - sold));\n                }\n            } else if (command.equals(\"one\")) {\n                if (sold >= seats) {\n                    System.out.println(\"Sorry, sold out\");\n                } else {\n                    sold++;\n                    System.out.println(\"Sold! Tickets left: \" + (seats - sold));\n                }\n            } else if (command.equals(\"refund\")) {\n                int count = Integer.valueOf(scanner.nextLine());\n                if (count > sold) {\n                    System.out.println(\"Sorry, tickets sold: \" + sold);\n                } else {\n                    sold = sold - count;\n                    System.out.println(\"Refunded! Tickets left: \" + (seats - sold));\n                }\n            } else if (command.equals(\"status\")) {\n                System.out.println(\"Sold: \" + sold + \", left: \" + (seats - sold));\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n    }\n}\n","hints":["The `sell` branch checks before it changes `sold`. The `one` branch changes `sold` without any check. That's the copy of the rule that went missing.","Wrap the two lines of `one` in an `if`: when `sold >= seats`, print `Sorry, sold out`, and otherwise add one to `sold` and print what it printed before."],"tests":[{"name":"Test 1","stdin":"sell\n198\none\none\none\nstatus\nquit\n","expect":"> Sold! Tickets left: 2\n> Sold! Tickets left: 1\n> Sold! Tickets left: 0\n> Sorry, sold out\n> Sold: 200, left: 0\n> Box office closed","hidden":false},{"name":"Test 2","stdin":"one\nstatus\nsell\n200\none\nrefund\n1\none\none\nstatus\nquit\n","expect":"> Sold! Tickets left: 199\n> Sold: 1, left: 199\n> Sorry, tickets left: 199\n> Sold! Tickets left: 198\n> Refunded! Tickets left: 199\n> Sold! Tickets left: 198\n> Sold! Tickets left: 197\n> Sold: 3, left: 197\n> Box office closed","hidden":true},{"name":"Test 3","stdin":"sell\n200\none\none\nrefund\n5\none\nstatus\nquit\n","expect":"> Sold! Tickets left: 0\n> Sorry, sold out\n> Sorry, sold out\n> Refunded! Tickets left: 5\n> Sold! Tickets left: 4\n> Sold: 196, left: 4\n> Box office closed","hidden":true}],"require":[],"forbid":[]},{"task":"What does this program print? A room of a museum lets in at most 50 visitors at a time. The commands come from a String, so the program reads no input: `in` and `out` are each followed by a line with the size of a group.\n","kind":"predict","seed":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(\"in\\n30\\nout\\n40\\nin\\n25\\nout\\n10\\nend\\n\");\n        int capacity = 50;\n        int inside = 0;\n        while (true) {\n            String command = scanner.nextLine();\n            if (command.equals(\"end\")) {\n                break;\n            }\n            int count = Integer.valueOf(scanner.nextLine());\n            if (command.equals(\"in\") && inside + count <= capacity) {\n                inside = inside + count;\n            } else if (command.equals(\"out\") && count <= inside) {\n                inside = inside - count;\n            } else {\n                System.out.println(\"refused: \" + command + \" \" + count);\n            }\n        }\n        System.out.println(inside);\n    }\n}\n","solution":"import java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(\"in\\n30\\nout\\n40\\nin\\n25\\nout\\n10\\nend\\n\");\n        int capacity = 50;\n        int inside = 0;\n        while (true) {\n            String command = scanner.nextLine();\n            if (command.equals(\"end\")) {\n                break;\n            }\n            int count = Integer.valueOf(scanner.nextLine());\n            if (command.equals(\"in\") && inside + count <= capacity) {\n                inside = inside + count;\n            } else if (command.equals(\"out\") && count <= inside) {\n                inside = inside - count;\n            } else {\n                System.out.println(\"refused: \" + command + \" \" + count);\n            }\n        }\n        System.out.println(inside);\n    }\n}\n","hints":["Keep track of `inside` command by command. After `in 30` it's 30, and `out 40` asks for more visitors than are inside.","A refused command changes nothing and prints a line. `in 25` would make 55, more than the 50 allowed. Then `out 10` leaves 20."],"tests":[{"name":"Output","stdin":"","expect":"refused: out 40\nrefused: in 25\n20","hidden":false}],"require":[],"forbid":[],"lines":["refused: out 40","refused: in 25","20"]}]}