{"id":"randomness-seeds","slug":"seeds","title":"Seeds: the same numbers every time","text":"Random numbers make a program hard to test. If the output changes on every run, what should a test expect? It turns out that a Random's numbers aren't as unpredictable as they look.\n\n### A seed\n\nGive `new Random` a whole number, and it hands out the same numbers on every run:\n\n```java\nimport java.util.Random;\n\npublic class Main {\n    public static void main(String[] args) {\n        Random random = new Random(42);\n        for (int i = 0; i < 5; i++) {\n            System.out.println(random.nextInt(10));\n        }\n    }\n}\n```\n\n```output\n0\n3\n8\n4\n0\n```\n\nRun it as often as you like: it prints these five numbers every time, on any computer. The 42 is the generator's **seed**.\n\nA Random doesn't roll anything. It works out its numbers one after the other with a fixed formula, starting from its seed. The numbers look random: they're spread evenly, and you can't easily guess the next one from the ones before it. But the same seed always leads to the same series. Numbers made this way are called **pseudorandom**.\n\n`new Random()` without a seed makes up a seed of its own, from the computer's clock and a counter, so it's different on every run. That's why the programs in the previous step printed new numbers each time.\n\n### Same seed, same numbers\n\nTwo generators with the same seed give the same numbers:\n\n```java\nimport java.util.Random;\n\npublic class Main {\n    public static void main(String[] args) {\n        Random first = new Random(2024);\n        Random second = new Random(2024);\n        for (int i = 0; i < 5; i++) {\n            int a = first.nextInt(6) + 1;\n            int b = second.nextInt(6) + 1;\n            System.out.println(a + \" \" + b);\n        }\n    }\n}\n```\n\n```output\n1 1\n3 3\n1 1\n5 5\n6 6\n```\n\nA seed fixes the series only for the same calls in the same order. Ask one of them for one extra number in between, and from there on the two series are out of step: one runs a number ahead of the other.\n\nJava's documentation spells out the exact formula that Random uses, so a seed gives the same numbers on every computer and every version of Java. That's what makes seeds useful:\n\n- **Tests.** A program with a seeded Random prints the same output on every run, so a test can compare it with the output written down from an earlier run. The checks in this module do exactly that.\n- **Games.** A game that builds its map from random numbers can let players share a seed: the same seed builds the same map. A daily puzzle can use the date as its seed, so that everyone gets the same puzzle that day.\n- **Simulations and bug hunts.** When a run with random numbers does something strange, running it again with the same seed repeats it exactly, so you can look at it step by step.\n\n### A class that takes its Random\n\nThe dice cup of a board game holds several dice and rolls them all at once. Where should its Random come from? If the class makes its own `new Random()`, nobody can test it: its rolls change on every run. So it takes a Random as a constructor parameter, and whoever creates the cup decides which:\n\n```java\nimport java.util.Random;\n\npublic class Main {\n    public static void main(String[] args) {\n        DiceCup cup = new DiceCup(3, new Random(7));\n        for (int i = 0; i < 4; i++) {\n            System.out.println(\"Rolled \" + cup.roll());\n        }\n    }\n}\n// ==== DiceCup.java ====\nimport java.util.Random;\n\npublic class DiceCup {\n    private int dice;\n    private Random random;\n\n    public DiceCup(int dice, Random random) {\n        this.dice = dice;\n        this.random = random;\n    }\n\n    public int roll() {\n        int total = 0;\n        for (int i = 0; i < this.dice; i++) {\n            total += this.random.nextInt(6) + 1;\n        }\n        return total;\n    }\n}\n```\n\n```output\nRolled 12\nRolled 15\nRolled 12\nRolled 3\n```\n\n- The game creates its cup with `new DiceCup(3, new Random())`, and gets new rolls every game.\n- A test creates it with a seed, as this `main` does, and gets the same rolls every time, so it can check them.\n- The class is the same in both cases. It doesn't care where its numbers come from, just as a class that reads with a Scanner doesn't care whether the Scanner reads the keyboard or a String. Module 26 tested methods by giving them `new Scanner(\"12\\n8\\n15\\n\")`; a seeded Random is the same idea.\n\nKeep the Random in an instance variable and use it for every roll. A method that creates a `new Random(7)` each time it's called starts the same series over on every call, and gets the same first number every time.\n","task":"A board game app has a practice mode with the same dice every time, so that players can compare their moves. Read a seed, a whole number, from the input, and create a Random with it. Then roll two dice three times. For each pair, roll the first die and then the second, each with `random.nextInt(6) + 1`, and print them and their total like `3 + 5 = 8`. With the input `42`, the program prints:\n\n```\n3 + 4 = 7\n1 + 3 = 4\n1 + 2 = 3\n```\n","kind":"code","seed":"import java.util.Random;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n\n    }\n}\n","solution":"import java.util.Random;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int seed = Integer.valueOf(scanner.nextLine());\n        Random random = new Random(seed);\n        for (int i = 0; i < 3; i++) {\n            int first = random.nextInt(6) + 1;\n            int second = random.nextInt(6) + 1;\n            System.out.println(first + \" + \" + second + \" = \" + (first + second));\n        }\n    }\n}\n","hints":["Read the seed with `Integer.valueOf(scanner.nextLine())`, and give it to the Random: `new Random(seed)`.","Loop three times. In the loop, roll `first` and then `second`, and print `first + \" + \" + second + \" = \" + (first + second)`."],"tests":[{"name":"Test 1","stdin":"42\n","expect":"3 + 4 = 7\n1 + 3 = 4\n1 + 2 = 3","hidden":false},{"name":"Test 2","stdin":"7\n","expect":"5 + 3 = 8\n4 + 5 = 9\n5 + 5 = 10","hidden":true},{"name":"Test 3","stdin":"123456\n","expect":"4 + 6 = 10\n2 + 2 = 4\n5 + 2 = 7","hidden":true},{"name":"Test 4","stdin":"-5\n","expect":"4 + 6 = 10\n4 + 1 = 5\n5 + 2 = 7","hidden":true}],"require":[{"pattern":"new\\s+Random\\s*\\(\\s*[^)\\s]","message":"Create the Random with the seed you read: `new Random(seed)`."},{"pattern":"\\.nextInt\\s*\\(\\s*6\\s*\\)","message":"Roll each die with `random.nextInt(6) + 1`, so that a seed gives the same dice as the check's."}],"forbid":[],"more":[{"task":"`rollDie` creates a new Random, with a seed, every time it's called. What does this program print? You can't know which numbers come up, but you can know how many different ones.\n","kind":"predict","seed":"import java.util.HashSet;\nimport java.util.Random;\n\npublic class Main {\n    public static void main(String[] args) {\n        HashSet<Integer> fresh = new HashSet<>();\n        for (int i = 0; i < 100; i++) {\n            fresh.add(rollDie());\n        }\n        Random random = new Random(2024);\n        HashSet<Integer> shared = new HashSet<>();\n        for (int i = 0; i < 100; i++) {\n            shared.add(random.nextInt(6) + 1);\n        }\n        System.out.println(fresh.size() + \" \" + shared.size());\n    }\n\n    public static int rollDie() {\n        Random random = new Random(2024);\n        return random.nextInt(6) + 1;\n    }\n}\n","solution":"import java.util.HashSet;\nimport java.util.Random;\n\npublic class Main {\n    public static void main(String[] args) {\n        HashSet<Integer> fresh = new HashSet<>();\n        for (int i = 0; i < 100; i++) {\n            fresh.add(rollDie());\n        }\n        Random random = new Random(2024);\n        HashSet<Integer> shared = new HashSet<>();\n        for (int i = 0; i < 100; i++) {\n            shared.add(random.nextInt(6) + 1);\n        }\n        System.out.println(fresh.size() + \" \" + shared.size());\n    }\n\n    public static int rollDie() {\n        Random random = new Random(2024);\n        return random.nextInt(6) + 1;\n    }\n}\n","hints":["Each call of `rollDie` starts a new generator from the seed 2024, and takes its first number. Every call takes the same first number.","The second loop keeps one generator and takes 100 numbers from it, one after the other. In 100 rolls, every face of a die comes up."],"tests":[{"name":"Output","stdin":"","expect":"1 6","hidden":false}],"require":[],"forbid":[],"lines":["1 6"]},{"task":"A game's weather simulation starts at a given temperature, and every day the temperature changes by a random whole number of degrees from -3 to 3. In `WeatherSim.java`, write the class `WeatherSim` with:\n\n- a constructor `WeatherSim(int start, Random random)`\n- `nextDay()`, which changes the temperature by `random.nextInt(7) - 3`, one call a day, and returns the new temperature\n- `getTemperature()`, which returns the temperature now\n\nThe check runs:\n\n```java\nWeatherSim sim = new WeatherSim(15, new Random(42));\nfor (int day = 1; day <= 5; day++) {\n    System.out.println(\"Day \" + day + \": \" + sim.nextDay());\n}\n```\n\nand expects:\n\n```\nDay 1: 13\nDay 2: 15\nDay 3: 18\nDay 4: 18\nDay 5: 20\n```\n","kind":"code","seed":"// ==== WeatherSim.java ====\nimport java.util.Random;\n\npublic class WeatherSim {\n\n}\n\n// ==== Main.java ====\nimport java.util.Random;\n\npublic class Main {\n    public static void main(String[] args) {\n        WeatherSim sim = new WeatherSim(15, new Random(42));\n        for (int day = 1; day <= 5; day++) {\n            System.out.println(\"Day \" + day + \": \" + sim.nextDay());\n        }\n    }\n}\n","solution":"// ==== WeatherSim.java ====\nimport java.util.Random;\n\npublic class WeatherSim {\n    private int temperature;\n    private Random random;\n\n    public WeatherSim(int start, Random random) {\n        this.temperature = start;\n        this.random = random;\n    }\n\n    public int nextDay() {\n        this.temperature += this.random.nextInt(7) - 3;\n        return this.temperature;\n    }\n\n    public int getTemperature() {\n        return this.temperature;\n    }\n}\n\n// ==== Main.java ====\nimport java.util.Random;\n\npublic class Main {\n    public static void main(String[] args) {\n        WeatherSim sim = new WeatherSim(15, new Random(42));\n        for (int day = 1; day <= 5; day++) {\n            System.out.println(\"Day \" + day + \": \" + sim.nextDay());\n        }\n    }\n}\n","hints":["Keep the temperature and the Random in instance variables, set in the constructor, like DiceCup's in the lesson.","`nextDay` adds `this.random.nextInt(7) - 3` to the temperature and returns it. Don't create a Random in the class: use the one from the constructor."],"tests":[{"name":"Test 1","stdin":"","call":"WeatherSim sim = new WeatherSim(15, new Random(42));\nfor (int day = 1; day <= 5; day++) {\n    System.out.println(\"Day \" + day + \": \" + sim.nextDay());\n}\n","expect":"Day 1: 13\nDay 2: 15\nDay 3: 18\nDay 4: 18\nDay 5: 20","hidden":false},{"name":"Test 2","stdin":"","call":"WeatherSim sim = new WeatherSim(-4, new Random(7));\nSystem.out.println(sim.getTemperature());\nfor (int day = 1; day <= 6; day++) {\n    sim.nextDay();\n}\nSystem.out.println(sim.getTemperature());\n","expect":"-4\n3","hidden":true},{"name":"Test 3","stdin":"","call":"WeatherSim one = new WeatherSim(20, new Random(99));\nWeatherSim two = new WeatherSim(20, new Random(99));\nboolean same = true;\nfor (int day = 0; day < 50; day++) {\n    if (one.nextDay() != two.nextDay()) {\n        same = false;\n    }\n}\nSystem.out.println(\"same seed, same weather: \" + same);\n","expect":"same seed, same weather: true","hidden":true},{"name":"Test 4","stdin":"","call":"WeatherSim sim = new WeatherSim(0, new Random());\nboolean[] seen = new boolean[7];\nboolean inRange = true;\nint before = sim.getTemperature();\nfor (int day = 0; day < 7000; day++) {\n    int change = sim.nextDay() - before;\n    if (change < -3 || change > 3) {\n        inRange = false;\n    } else {\n        seen[change + 3] = true;\n    }\n    before = sim.getTemperature();\n}\nSystem.out.println(\"changes from -3 to 3: \" + inRange);\nSystem.out.println(\"each change came up: \" + Arrays.toString(seen));\n","expect":"changes from -3 to 3: true\neach change came up: [true, true, true, true, true, true, true]","hidden":true}],"require":[{"pattern":"\\.nextInt\\s*\\(\\s*7\\s*\\)","message":"Change the temperature by `this.random.nextInt(7) - 3`, so that a seed gives the same days as the check's."}],"forbid":[]}]}