{"id":"asteroids-bubbles","slug":"bubbles","title":"Firing bubbles","text":"The submarine fights back: Space fires a **bubble** from its nose, and a bubble that hits a jellyfish pops it. Both disappear from the sea. This step needs things to be **removed** from the game, which takes a little care.\n\n### The class Bubble\n\nA bubble is one more Mover: a small circle that starts where the submarine is, heads where its nose points, and goes fast, 2.5 pixels a frame:\n\n```java\npublic class Bubble extends Mover {\n\n    public Bubble(double x, double y, double heading) {\n        super(new Circle(3, Color.LIGHTCYAN), x, y);\n        turn(heading);\n        accelerate(2.5);\n    }\n}\n```\n\nA circle looks the same however it's turned, but its rotate still says where `accelerate` pushes it. The submarine's own speed isn't added: a bubble always leaves at 2.5 pixels a frame.\n\n### Firing on a key press\n\nTurning and speeding up use the held keys in the game loop. Firing is different: **one press, one bubble** (on your own computer a held Space counts as many presses, as step 8 says). So the bubble is made in the pressed handler, and only for Space. The event line `key SPACE` presses and releases Space with no frame in between, so the game loop would never even see Space held; the pressed handler sees every press. To keep the sea from filling up, the submarine fires only while fewer than 4 bubbles are out:\n\n```java\nscene.setOnKeyPressed(event -> {\n    held.put(event.getCode(), true);\n    if (event.getCode() == KeyCode.SPACE && bubbles.size() < 4) {\n        Bubble bubble = new Bubble(sub.getShape().getTranslateX(), sub.getShape().getTranslateY(), sub.getShape().getRotate());\n        bubbles.add(bubble);\n        pane.getChildren().add(bubble.getShape());\n    }\n});\n```\n\n### Alive or not\n\nWhen a bubble hits a jellyfish, both should go. Removing them from their lists **while a for-each loop goes through those lists** throws a ConcurrentModificationException (module 14 showed it); in the game loop, JavaFX prints it and goes on with the next frame, and the rest of that frame's loop never runs. So the game does it in two passes: first it only **marks** them, with a flag that every Mover now has, and after the loops it removes everything that's marked. Mover gets a field `alive`, `true` at the start, with `isAlive()` and `setAlive(boolean)`.\n\nRemoving has two parts. The shape must leave the pane's children, or it stays on the screen; and the Mover must leave its list, or it keeps moving and colliding unseen. Module 37's streams do both:\n\n```java\nbubbles.stream()\n        .filter(b -> !b.isAlive())\n        .forEach(b -> pane.getChildren().remove(b.getShape()));\nbubbles = bubbles.stream()\n        .filter(b -> b.isAlive())\n        .collect(Collectors.toList());\n```\n\nThe first statement removes the shape of every bubble that isn't alive from the pane. The second keeps the bubbles that are alive in a **new** list and puts it in the field `bubbles`. That's why `bubbles` and `jellyfish` are now fields of `Main` and not local variables of `start`: the code of the timer can assign a field, but not a local variable.\n\n```java\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport javafx.animation.AnimationTimer;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Rectangle;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n    public static final int WIDTH = 360;\n    public static final int HEIGHT = 240;\n\n    private Map<KeyCode, Boolean> held = new HashMap<>();\n    private Random random = new Random(16);\n    private List<Jellyfish> jellyfish = new ArrayList<>();\n    private List<Bubble> bubbles = new ArrayList<>();\n\n    @Override\n    public void start(Stage stage) {\n        Pane pane = new Pane();\n        pane.setPrefSize(WIDTH, HEIGHT);\n        pane.getChildren().add(new Rectangle(WIDTH, HEIGHT, Color.MIDNIGHTBLUE));\n\n        Sub sub = new Sub(WIDTH / 2, HEIGHT / 2);\n        pane.getChildren().add(sub.getShape());\n        for (int i = 0; i < 4; i++) {\n            Jellyfish j = new Jellyfish(random.nextInt(WIDTH), random.nextInt(60), random.nextInt(360));\n            jellyfish.add(j);\n            pane.getChildren().add(j.getShape());\n        }\n\n        Scene scene = new Scene(pane);\n        scene.setOnKeyPressed(event -> {\n            held.put(event.getCode(), true);\n            if (event.getCode() == KeyCode.SPACE && bubbles.size() < 4) {\n                Bubble bubble = new Bubble(sub.getShape().getTranslateX(), sub.getShape().getTranslateY(), sub.getShape().getRotate());\n                bubbles.add(bubble);\n                pane.getChildren().add(bubble.getShape());\n            }\n        });\n        scene.setOnKeyReleased(event -> held.put(event.getCode(), false));\n\n        new AnimationTimer() {\n            @Override\n            public void handle(long now) {\n                if (held.getOrDefault(KeyCode.LEFT, false)) {\n                    sub.turn(-4);\n                }\n                if (held.getOrDefault(KeyCode.RIGHT, false)) {\n                    sub.turn(4);\n                }\n                if (held.getOrDefault(KeyCode.UP, false)) {\n                    sub.accelerate(0.125);\n                }\n                sub.move();\n                jellyfish.forEach(j -> j.move());\n                bubbles.forEach(b -> b.move());\n\n                for (Bubble b : bubbles) {\n                    for (Jellyfish j : jellyfish) {\n                        if (b.collides(j)) {\n                            b.setAlive(false);\n                            j.setAlive(false);\n                            System.out.println(\"Pop!\");\n                        }\n                    }\n                }\n                bubbles.stream()\n                        .filter(b -> !b.isAlive())\n                        .forEach(b -> pane.getChildren().remove(b.getShape()));\n                jellyfish.stream()\n                        .filter(j -> !j.isAlive())\n                        .forEach(j -> pane.getChildren().remove(j.getShape()));\n                bubbles = bubbles.stream()\n                        .filter(b -> b.isAlive())\n                        .collect(Collectors.toList());\n                jellyfish = jellyfish.stream()\n                        .filter(j -> j.isAlive())\n                        .collect(Collectors.toList());\n\n                for (Jellyfish j : jellyfish) {\n                    if (sub.collides(j)) {\n                        sub.getShape().setFill(Color.RED);\n                        System.out.println(\"Bump!\");\n                        stop();\n                        break;\n                    }\n                }\n            }\n        }.start();\n\n        stage.setScene(scene);\n        stage.setTitle(\"Bubbles\");\n        stage.show();\n    }\n\n    public static double wrap(double value, double size) {\n        double result = value % size;\n        if (result < 0) {\n            result = result + size;\n        }\n        return result;\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n// ==== Mover.java ====\nimport javafx.geometry.Point2D;\nimport javafx.scene.shape.Shape;\n\npublic abstract class Mover {\n    private Shape shape;\n    private Point2D velocity;\n    private boolean alive;\n\n    public Mover(Shape shape, double x, double y) {\n        this.shape = shape;\n        this.shape.setTranslateX(x);\n        this.shape.setTranslateY(y);\n        this.velocity = new Point2D(0, 0);\n        this.alive = true;\n    }\n\n    public Shape getShape() {\n        return shape;\n    }\n\n    public boolean isAlive() {\n        return alive;\n    }\n\n    public void setAlive(boolean alive) {\n        this.alive = alive;\n    }\n\n    public void turn(double degrees) {\n        shape.setRotate(shape.getRotate() + degrees);\n    }\n\n    public void accelerate(double amount) {\n        double angle = Math.toRadians(shape.getRotate());\n        velocity = velocity.add(Math.cos(angle) * amount, Math.sin(angle) * amount);\n    }\n\n    public void move() {\n        shape.setTranslateX(Main.wrap(shape.getTranslateX() + velocity.getX(), Main.WIDTH));\n        shape.setTranslateY(Main.wrap(shape.getTranslateY() + velocity.getY(), Main.HEIGHT));\n    }\n\n    public boolean collides(Mover other) {\n        Shape overlap = Shape.intersect(shape, other.getShape());\n        return overlap.getBoundsInLocal().getWidth() != -1;\n    }\n}\n\n// ==== Sub.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Sub extends Mover {\n\n    public Sub(double x, double y) {\n        super(new Polygon(-15, -6, 9, -6, 15, 0, 9, 6, -15, 6), x, y);\n        getShape().setFill(Color.GOLD);\n    }\n}\n\n// ==== Jellyfish.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Jellyfish extends Mover {\n\n    public Jellyfish(double x, double y, double heading) {\n        super(new Polygon(-10, 0, -6, -8, 0, -10, 6, -8, 10, 0, 7, 10, 3, 2, 0, 10, -3, 2, -7, 10), x, y);\n        getShape().setFill(Color.ORCHID);\n        turn(heading);\n        accelerate(0.5);\n    }\n\n    @Override\n    public void move() {\n        super.move();\n        turn(1);\n    }\n}\n\n// ==== Bubble.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Circle;\n\npublic class Bubble extends Mover {\n\n    public Bubble(double x, double y, double heading) {\n        super(new Circle(3, Color.LIGHTCYAN), x, y);\n        turn(heading);\n        accelerate(2.5);\n    }\n}\n```\n\n```window\n{\"events\":[\"press key LEFT\",\"frames 20\",\"release key LEFT\",\"key SPACE\",\"frames 10\",\"key SPACE\",\"frames 10\",\"key SPACE\",\"frames 30\",\"press key RIGHT\",\"frames 30\",\"release key RIGHT\",\"key SPACE\",\"frames 40\"],\"window\":{\"windows\":[{\"title\":\"Bubbles\",\"scene\":{\"width\":360,\"height\":240,\"onKeyPressed\":true,\"onKeyReleased\":true,\"root\":{\"n\":1,\"type\":\"Pane\",\"f\":1,\"prefWidth\":360,\"prefHeight\":240,\"children\":[{\"n\":2,\"type\":\"Rectangle\",\"f\":2,\"x\":0,\"y\":0,\"width\":360,\"height\":240,\"fill\":\"#191970\"},{\"n\":3,\"type\":\"Polygon\",\"f\":3,\"translateX\":180,\"translateY\":120,\"rotate\":40,\"points\":[-15,-6,9,-6,15,0,9,6,-15,6],\"fill\":\"#ffd700\"},{\"n\":4,\"type\":\"Polygon\",\"f\":4,\"translateX\":249.11733315717788,\"translateY\":119.61480784023462,\"rotate\":215,\"points\":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],\"fill\":\"#da70d6\"},{\"n\":5,\"type\":\"Polygon\",\"f\":6,\"translateX\":248.0048673219392,\"translateY\":103.62311101832802,\"rotate\":270,\"points\":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],\"fill\":\"#da70d6\"},{\"n\":6,\"type\":\"Circle\",\"f\":8,\"translateX\":232.09445330007838,\"translateY\":64.55767409633816,\"rotate\":-80,\"centerX\":0,\"centerY\":0,\"radius\":3,\"fill\":\"#e0ffff\"},{\"n\":7,\"type\":\"Circle\",\"f\":11,\"translateX\":256.6044443118983,\"translateY\":184.27876096865367,\"rotate\":40,\"centerX\":0,\"centerY\":0,\"radius\":3,\"fill\":\"#e0ffff\"}]}}}],\"problems\":[],\"frames\":{\"count\":140,\"list\":[[{\"roots\":[1]},{\"k\":1,\"type\":\"Pane\",\"children\":[2,3,4,5,6,7]},{\"k\":2,\"type\":\"Rectangle\",\"width\":360,\"height\":240,\"fill\":\"#191970\"},{\"k\":3,\"type\":\"Polygon\",\"tx\":180,\"ty\":120,\"r\":-4,\"points\":[-15,-6,9,-6,15,0,9,6,-15,6],\"fill\":\"#ffd700\"},{\"k\":4,\"type\":\"Polygon\",\"tx\":231.13,\"ty\":52.48,\"r\":76,\"points\":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],\"fill\":\"#da70d6\"},{\"k\":5,\"type\":\"Polygon\",\"tx\":249.54,\"ty\":12.81,\"r\":203,\"points\":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],\"fill\":\"#da70d6\"},{\"k\":6,\"type\":\"Polygon\",\"tx\":292.68,\"ty\":50.38,\"r\":131,\"points\":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],\"fill\":\"#da70d6\"},{\"k\":7,\"type\":\"Polygon\",\"tx\":155.47,\"ty\":11.17,\"r\":21,\"points\":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],\"fill\":\"#da70d6\"}],[{\"k\":3,\"r\":-8},{\"k\":4,\"tx\":231.26,\"ty\":52.97,\"r\":77},{\"k\":5,\"tx\":249.07,\"ty\":12.63,\"r\":204},{\"k\":6,\"tx\":292.36,\"ty\":50.77,\"r\":132},{\"k\":7,\"tx\":155.94,\"ty\":11.34,\"r\":22}],[{\"k\":3,\"r\":-12},{\"k\":4,\"tx\":231.39,\"ty\":53.45,\"r\":78},{\"k\":5,\"tx\":248.61,\"ty\":12.44,\"r\":205},{\"k\":6,\"tx\":292.04,\"ty\":51.15,\"r\":133},{\"k\":7,\"tx\":156.41,\"ty\":11.51,\"r\":23}],[{\"k\":3,\"r\":-16},{\"k\":4,\"tx\":231.52,\"ty\":53.93,\"r\":79},{\"k\":5,\"tx\":248.15,\"ty\":12.25,\"r\":206},{\"k\":6,\"tx\":291.71,\"ty\":51.53,\"r\":134},{\"k\":7,\"tx\":156.88,\"ty\":11.68,\"r\":24}],[{\"k\":3,\"r\":-20},{\"k\":4,\"tx\":231.65,\"ty\":54.41,\"r\":80},{\"k\":5,\"tx\":247.68,\"ty\":12.06,\"r\":207},{\"k\":6,\"tx\":291.39,\"ty\":51.92,\"r\":135},{\"k\":7,\"tx\":157.35,\"ty\":11.86,\"r\":25}],[{\"k\":3,\"r\":-24},{\"k\":4,\"tx\":231.78,\"ty\":54.9,\"r\":81},{\"k\":5,\"tx\":247.22,\"ty\":11.88,\"r\":208},{\"k\":6,\"tx\":291.07,\"ty\":52.3,\"r\":136},{\"k\":7,\"tx\":157.82,\"ty\":12.03,\"r\":26}],[{\"k\":3,\"r\":-28},{\"k\":4,\"tx\":231.91,\"ty\":55.38,\"r\":82},{\"k\":5,\"tx\":246.75,\"ty\":11.69,\"r\":209},{\"k\":6,\"tx\":290.75,\"ty\":52.68,\"r\":137},{\"k\":7,\"tx\":158.29,\"ty\":12.2,\"r\":27}],[{\"k\":3,\"r\":-32},{\"k\":4,\"tx\":232.04,\"ty\":55.86,\"r\":83},{\"k\":5,\"tx\":246.29,\"ty\":11.5,\"r\":210},{\"k\":6,\"tx\":290.43,\"ty\":53.06,\"r\":138},{\"k\":7,\"tx\":158.76,\"ty\":12.37,\"r\":28}],[{\"k\":3,\"r\":-36},{\"k\":4,\"tx\":232.16,\"ty\":56.35,\"r\":84},{\"k\":5,\"tx\":245.83,\"ty\":11.31,\"r\":211},{\"k\":6,\"tx\":290.11,\"ty\":53.45,\"r\":139},{\"k\":7,\"tx\":159.23,\"ty\":12.54,\"r\":29}],[{\"k\":3,\"r\":-40},{\"k\":4,\"tx\":232.29,\"ty\":56.83,\"r\":85},{\"k\":5,\"tx\":245.36,\"ty\":11.13,\"r\":212},{\"k\":6,\"tx\":289.79,\"ty\":53.83,\"r\":140},{\"k\":7,\"tx\":159.7,\"ty\":12.71,\"r\":30}],[{\"k\":3,\"r\":-44},{\"k\":4,\"tx\":232.42,\"ty\":57.31,\"r\":86},{\"k\":5,\"tx\":244.9,\"ty\":10.94,\"r\":213},{\"k\":6,\"tx\":289.46,\"ty\":54.21,\"r\":141},{\"k\":7,\"tx\":160.17,\"ty\":12.88,\"r\":31}],[{\"k\":3,\"r\":-48},{\"k\":4,\"tx\":232.55,\"ty\":57.8,\"r\":87},{\"k\":5,\"tx\":244.44,\"ty\":10.75,\"r\":214},{\"k\":6,\"tx\":289.14,\"ty\":54.6,\"r\":142},{\"k\":7,\"tx\":160.64,\"ty\":13.05,\"r\":32}],[{\"k\":3,\"r\":-52},{\"k\":4,\"tx\":232.68,\"ty\":58.28,\"r\":88},{\"k\":5,\"tx\":243.97,\"ty\":10.57,\"r\":215},{\"k\":6,\"tx\":288.82,\"ty\":54.98,\"r\":143},{\"k\":7,\"tx\":161.11,\"ty\":13.22,\"r\":33}],[{\"k\":3,\"r\":-56},{\"k\":4,\"tx\":232.81,\"ty\":58.76,\"r\":89},{\"k\":5,\"tx\":243.51,\"ty\":10.38,\"r\":216},{\"k\":6,\"tx\":288.5,\"ty\":55.36,\"r\":144},{\"k\":7,\"tx\":161.58,\"ty\":13.39,\"r\":34}],[{\"k\":3,\"r\":-60},{\"k\":4,\"tx\":232.94,\"ty\":59.24,\"r\":90},{\"k\":5,\"tx\":243.05,\"ty\":10.19,\"r\":217},{\"k\":6,\"tx\":288.18,\"ty\":55.75,\"r\":145},{\"k\":7,\"tx\":162.05,\"ty\":13.57,\"r\":35}],[{\"k\":3,\"r\":-64},{\"k\":4,\"tx\":233.07,\"ty\":59.73,\"r\":91},{\"k\":5,\"tx\":242.58,\"ty\":10,\"r\":218},{\"k\":6,\"tx\":287.86,\"ty\":56.13,\"r\":146},{\"k\":7,\"tx\":162.52,\"ty\":13.74,\"r\":36}],[{\"k\":3,\"r\":-68},{\"k\":4,\"tx\":233.2,\"ty\":60.21,\"r\":92},{\"k\":5,\"tx\":242.12,\"ty\":9.82,\"r\":219},{\"k\":6,\"tx\":287.54,\"ty\":56.51,\"r\":147},{\"k\":7,\"tx\":162.99,\"ty\":13.91,\"r\":37}],[{\"k\":3,\"r\":-72},{\"k\":4,\"tx\":233.33,\"ty\":60.69,\"r\":93},{\"k\":5,\"tx\":241.66,\"ty\":9.63,\"r\":220},{\"k\":6,\"tx\":287.21,\"ty\":56.89,\"r\":148},{\"k\":7,\"tx\":163.46,\"ty\":14.08,\"r\":38}],[{\"k\":3,\"r\":-76},{\"k\":4,\"tx\":233.46,\"ty\":61.18,\"r\":94},{\"k\":5,\"tx\":241.19,\"ty\":9.44,\"r\":221},{\"k\":6,\"tx\":286.89,\"ty\":57.28,\"r\":149},{\"k\":7,\"tx\":163.93,\"ty\":14.25,\"r\":39}],[{\"k\":3,\"r\":-80},{\"k\":4,\"tx\":233.59,\"ty\":61.66,\"r\":95},{\"k\":5,\"tx\":240.73,\"ty\":9.25,\"r\":222},{\"k\":6,\"tx\":286.57,\"ty\":57.66,\"r\":150},{\"k\":7,\"tx\":164.4,\"ty\":14.42,\"r\":40}],[{\"k\":1,\"children\":[2,3,4,5,6,7,8]},{\"k\":4,\"tx\":233.72,\"ty\":62.14,\"r\":96},{\"k\":5,\"tx\":240.26,\"ty\":9.07,\"r\":223},{\"k\":6,\"tx\":286.25,\"ty\":58.04,\"r\":151},{\"k\":7,\"tx\":164.87,\"ty\":14.59,\"r\":41},{\"k\":8,\"type\":\"Circle\",\"tx\":180.43,\"ty\":117.54,\"r\":-80,\"radius\":3,\"fill\":\"#e0ffff\"}],[{\"k\":4,\"tx\":233.85,\"ty\":62.63,\"r\":97},{\"k\":5,\"tx\":239.8,\"ty\":8.88,\"r\":224},{\"k\":6,\"tx\":285.93,\"ty\":58.43,\"r\":152},{\"k\":7,\"tx\":165.34,\"ty\":14.76,\"r\":42},{\"k\":8,\"tx\":180.87,\"ty\":115.08}],[{\"k\":4,\"tx\":233.98,\"ty\":63.11,\"r\":98},{\"k\":5,\"tx\":239.34,\"ty\":8.69,\"r\":225},{\"k\":6,\"tx\":285.61,\"ty\":58.81,\"r\":153},{\"k\":7,\"tx\":165.81,\"ty\":14.93,\"r\":43},{\"k\":8,\"tx\":181.3,\"ty\":112.61}],[{\"k\":4,\"tx\":234.11,\"ty\":63.59,\"r\":99},{\"k\":5,\"tx\":238.87,\"ty\":8.5,\"r\":226},{\"k\":6,\"tx\":285.29,\"ty\":59.19,\"r\":154},{\"k\":7,\"tx\":166.28,\"ty\":15.1,\"r\":44},{\"k\":8,\"tx\":181.74,\"ty\":110.15}],[{\"k\":4,\"tx\":234.24,\"ty\":64.07,\"r\":100},{\"k\":5,\"tx\":238.41,\"ty\":8.32,\"r\":227},{\"k\":6,\"tx\":284.97,\"ty\":59.58,\"r\":155},{\"k\":7,\"tx\":166.75,\"ty\":15.28,\"r\":45},{\"k\":8,\"tx\":182.17,\"ty\":107.69}],[{\"k\":4,\"tx\":234.36,\"ty\":64.56,\"r\":101},{\"k\":5,\"tx\":237.95,\"ty\":8.13,\"r\":228},{\"k\":6,\"tx\":284.64,\"ty\":59.96,\"r\":156},{\"k\":7,\"tx\":167.22,\"ty\":15.45,\"r\":46},{\"k\":8,\"tx\":182.6,\"ty\":105.23}],[{\"k\":4,\"tx\":234.49,\"ty\":65.04,\"r\":102},{\"k\":5,\"tx\":237.48,\"ty\":7.94,\"r\":229},{\"k\":6,\"tx\":284.32,\"ty\":60.34,\"r\":157},{\"k\":7,\"tx\":167.69,\"ty\":15.62,\"r\":47},{\"k\":8,\"tx\":183.04,\"ty\":102.77}],[{\"k\":4,\"tx\":234.62,\"ty\":65.52,\"r\":103},{\"k\":5,\"tx\":237.02,\"ty\":7.76,\"r\":230},{\"k\":6,\"tx\":284,\"ty\":60.72,\"r\":158},{\"k\":7,\"tx\":168.16,\"ty\":15.79,\"r\":48},{\"k\":8,\"tx\":183.47,\"ty\":100.3}],[{\"k\":4,\"tx\":234.75,\"ty\":66.01,\"r\":104},{\"k\":5,\"tx\":236.56,\"ty\":7.57,\"r\":231},{\"k\":6,\"tx\":283.68,\"ty\":61.11,\"r\":159},{\"k\":7,\"tx\":168.63,\"ty\":15.96,\"r\":49},{\"k\":8,\"tx\":183.91,\"ty\":97.84}],[{\"k\":4,\"tx\":234.88,\"ty\":66.49,\"r\":105},{\"k\":5,\"tx\":236.09,\"ty\":7.38,\"r\":232},{\"k\":6,\"tx\":283.36,\"ty\":61.49,\"r\":160},{\"k\":7,\"tx\":169.1,\"ty\":16.13,\"r\":50},{\"k\":8,\"tx\":184.34,\"ty\":95.38}],[{\"k\":1,\"children\":[2,3,4,5,6,7,8,9]},{\"k\":4,\"tx\":235.01,\"ty\":66.97,\"r\":106},{\"k\":5,\"tx\":235.63,\"ty\":7.19,\"r\":233},{\"k\":6,\"tx\":283.04,\"ty\":61.87,\"r\":161},{\"k\":7,\"tx\":169.57,\"ty\":16.3,\"r\":51},{\"k\":8,\"tx\":184.78,\"ty\":92.92},{\"k\":9,\"type\":\"Circle\",\"tx\":180.43,\"ty\":117.54,\"r\":-80,\"radius\":3,\"fill\":\"#e0ffff\"}],[{\"k\":4,\"tx\":235.14,\"ty\":67.45,\"r\":107},{\"k\":5,\"tx\":235.17,\"ty\":7.01,\"r\":234},{\"k\":6,\"tx\":282.72,\"ty\":62.26,\"r\":162},{\"k\":7,\"tx\":170.04,\"ty\":16.47,\"r\":52},{\"k\":8,\"tx\":185.21,\"ty\":90.46},{\"k\":9,\"tx\":180.87,\"ty\":115.08}],[{\"k\":4,\"tx\":235.27,\"ty\":67.94,\"r\":108},{\"k\":5,\"tx\":234.7,\"ty\":6.82,\"r\":235},{\"k\":6,\"tx\":282.39,\"ty\":62.64,\"r\":163},{\"k\":7,\"tx\":170.5,\"ty\":16.64,\"r\":53},{\"k\":8,\"tx\":185.64,\"ty\":87.99},{\"k\":9,\"tx\":181.3,\"ty\":112.61}],[{\"k\":4,\"tx\":235.4,\"ty\":68.42,\"r\":109},{\"k\":5,\"tx\":234.24,\"ty\":6.63,\"r\":236},{\"k\":6,\"tx\":282.07,\"ty\":63.02,\"r\":164},{\"k\":7,\"tx\":170.97,\"ty\":16.81,\"r\":54},{\"k\":8,\"tx\":186.08,\"ty\":85.53},{\"k\":9,\"tx\":181.74,\"ty\":110.15}],[{\"k\":4,\"tx\":235.53,\"ty\":68.9,\"r\":110},{\"k\":5,\"tx\":233.77,\"ty\":6.44,\"r\":237},{\"k\":6,\"tx\":281.75,\"ty\":63.41,\"r\":165},{\"k\":7,\"tx\":171.44,\"ty\":16.99,\"r\":55},{\"k\":8,\"tx\":186.51,\"ty\":83.07},{\"k\":9,\"tx\":182.17,\"ty\":107.69}],[{\"k\":4,\"tx\":235.66,\"ty\":69.39,\"r\":111},{\"k\":5,\"tx\":233.31,\"ty\":6.26,\"r\":238},{\"k\":6,\"tx\":281.43,\"ty\":63.79,\"r\":166},{\"k\":7,\"tx\":171.91,\"ty\":17.16,\"r\":56},{\"k\":8,\"tx\":186.95,\"ty\":80.61},{\"k\":9,\"tx\":182.6,\"ty\":105.23}],[{\"k\":4,\"tx\":235.79,\"ty\":69.87,\"r\":112},{\"k\":5,\"tx\":232.85,\"ty\":6.07,\"r\":239},{\"k\":6,\"tx\":281.11,\"ty\":64.17,\"r\":167},{\"k\":7,\"tx\":172.38,\"ty\":17.33,\"r\":57},{\"k\":8,\"tx\":187.38,\"ty\":78.15},{\"k\":9,\"tx\":183.04,\"ty\":102.77}],[{\"k\":4,\"tx\":235.92,\"ty\":70.35,\"r\":113},{\"k\":5,\"tx\":232.38,\"ty\":5.88,\"r\":240},{\"k\":6,\"tx\":280.79,\"ty\":64.55,\"r\":168},{\"k\":7,\"tx\":172.85,\"ty\":17.5,\"r\":58},{\"k\":8,\"tx\":187.81,\"ty\":75.68},{\"k\":9,\"tx\":183.47,\"ty\":100.3}],[{\"k\":4,\"tx\":236.05,\"ty\":70.84,\"r\":114},{\"k\":5,\"tx\":231.92,\"ty\":5.7,\"r\":241},{\"k\":6,\"tx\":280.47,\"ty\":64.94,\"r\":169},{\"k\":7,\"tx\":173.32,\"ty\":17.67,\"r\":59},{\"k\":8,\"tx\":188.25,\"ty\":73.22},{\"k\":9,\"tx\":183.91,\"ty\":97.84}],[{\"k\":4,\"tx\":236.18,\"ty\":71.32,\"r\":115},{\"k\":5,\"tx\":231.46,\"ty\":5.51,\"r\":242},{\"k\":6,\"tx\":280.14,\"ty\":65.32,\"r\":170},{\"k\":7,\"tx\":173.79,\"ty\":17.84,\"r\":60},{\"k\":8,\"tx\":188.68,\"ty\":70.76},{\"k\":9,\"tx\":184.34,\"ty\":95.38}],[{\"k\":1,\"children\":[2,3,4,5,6,7,8,9,10]},{\"k\":4,\"tx\":236.31,\"ty\":71.8,\"r\":116},{\"k\":5,\"tx\":230.99,\"ty\":5.32,\"r\":243},{\"k\":6,\"tx\":279.82,\"ty\":65.7,\"r\":171},{\"k\":7,\"tx\":174.26,\"ty\":18.01,\"r\":61},{\"k\":8,\"tx\":189.12,\"ty\":68.3},{\"k\":9,\"tx\":184.78,\"ty\":92.92},{\"k\":10,\"type\":\"Circle\",\"tx\":180.43,\"ty\":117.54,\"r\":-80,\"radius\":3,\"fill\":\"#e0ffff\"}],[{\"k\":4,\"tx\":236.44,\"ty\":72.28,\"r\":117},{\"k\":5,\"tx\":230.53,\"ty\":5.13,\"r\":244},{\"k\":6,\"tx\":279.5,\"ty\":66.09,\"r\":172},{\"k\":7,\"tx\":174.73,\"ty\":18.18,\"r\":62},{\"k\":8,\"tx\":189.55,\"ty\":65.84},{\"k\":9,\"tx\":185.21,\"ty\":90.46},{\"k\":10,\"tx\":180.87,\"ty\":115.08}],[{\"k\":4,\"tx\":236.56,\"ty\":72.77,\"r\":118},{\"k\":5,\"tx\":230.07,\"ty\":4.95,\"r\":245},{\"k\":6,\"tx\":279.18,\"ty\":66.47,\"r\":173},{\"k\":7,\"tx\":175.2,\"ty\":18.35,\"r\":63},{\"k\":8,\"tx\":189.98,\"ty\":63.37},{\"k\":9,\"tx\":185.64,\"ty\":87.99},{\"k\":10,\"tx\":181.3,\"ty\":112.61}],[{\"k\":4,\"tx\":236.69,\"ty\":73.25,\"r\":119},{\"k\":5,\"tx\":229.6,\"ty\":4.76,\"r\":246},{\"k\":6,\"tx\":278.86,\"ty\":66.85,\"r\":174},{\"k\":7,\"tx\":175.67,\"ty\":18.52,\"r\":64},{\"k\":8,\"tx\":190.42,\"ty\":60.91},{\"k\":9,\"tx\":186.08,\"ty\":85.53},{\"k\":10,\"tx\":181.74,\"ty\":110.15}],[{\"k\":4,\"tx\":236.82,\"ty\":73.73,\"r\":120},{\"k\":5,\"tx\":229.14,\"ty\":4.57,\"r\":247},{\"k\":6,\"tx\":278.54,\"ty\":67.24,\"r\":175},{\"k\":7,\"tx\":176.14,\"ty\":18.7,\"r\":65},{\"k\":8,\"tx\":190.85,\"ty\":58.45},{\"k\":9,\"tx\":186.51,\"ty\":83.07},{\"k\":10,\"tx\":182.17,\"ty\":107.69}],[{\"k\":4,\"tx\":236.95,\"ty\":74.22,\"r\":121},{\"k\":5,\"tx\":228.67,\"ty\":4.38,\"r\":248},{\"k\":6,\"tx\":278.22,\"ty\":67.62,\"r\":176},{\"k\":7,\"tx\":176.61,\"ty\":18.87,\"r\":66},{\"k\":8,\"tx\":191.29,\"ty\":55.99},{\"k\":9,\"tx\":186.95,\"ty\":80.61},{\"k\":10,\"tx\":182.6,\"ty\":105.23}],[{\"k\":4,\"tx\":237.08,\"ty\":74.7,\"r\":122},{\"k\":5,\"tx\":228.21,\"ty\":4.2,\"r\":249},{\"k\":6,\"tx\":277.89,\"ty\":68,\"r\":177},{\"k\":7,\"tx\":177.08,\"ty\":19.04,\"r\":67},{\"k\":8,\"tx\":191.72,\"ty\":53.53},{\"k\":9,\"tx\":187.38,\"ty\":78.15},{\"k\":10,\"tx\":183.04,\"ty\":102.77}],[{\"k\":4,\"tx\":237.21,\"ty\":75.18,\"r\":123},{\"k\":5,\"tx\":227.75,\"ty\":4.01,\"r\":250},{\"k\":6,\"tx\":277.57,\"ty\":68.39,\"r\":178},{\"k\":7,\"tx\":177.55,\"ty\":19.21,\"r\":68},{\"k\":8,\"tx\":192.16,\"ty\":51.06},{\"k\":9,\"tx\":187.81,\"ty\":75.68},{\"k\":10,\"tx\":183.47,\"ty\":100.3}],[{\"k\":4,\"tx\":237.34,\"ty\":75.67,\"r\":124},{\"k\":5,\"tx\":227.28,\"ty\":3.82,\"r\":251},{\"k\":6,\"tx\":277.25,\"ty\":68.77,\"r\":179},{\"k\":7,\"tx\":178.02,\"ty\":19.38,\"r\":69},{\"k\":8,\"tx\":192.59,\"ty\":48.6},{\"k\":9,\"tx\":188.25,\"ty\":73.22},{\"k\":10,\"tx\":183.91,\"ty\":97.84}],[{\"k\":4,\"tx\":237.47,\"ty\":76.15,\"r\":125},{\"k\":5,\"tx\":226.82,\"ty\":3.63,\"r\":252},{\"k\":6,\"tx\":276.93,\"ty\":69.15,\"r\":180},{\"k\":7,\"tx\":178.49,\"ty\":19.55,\"r\":70},{\"k\":8,\"tx\":193.02,\"ty\":46.14},{\"k\":9,\"tx\":188.68,\"ty\":70.76},{\"k\":10,\"tx\":184.34,\"ty\":95.38}],[{\"k\":4,\"tx\":237.6,\"ty\":76.63,\"r\":126},{\"k\":5,\"tx\":226.36,\"ty\":3.45,\"r\":253},{\"k\":6,\"tx\":276.61,\"ty\":69.53,\"r\":181},{\"k\":7,\"tx\":178.96,\"ty\":19.72,\"r\":71},{\"k\":8,\"tx\":193.46,\"ty\":43.68},{\"k\":9,\"tx\":189.12,\"ty\":68.3},{\"k\":10,\"tx\":184.78,\"ty\":92.92}],[{\"k\":4,\"tx\":237.73,\"ty\":77.11,\"r\":127},{\"k\":5,\"tx\":225.89,\"ty\":3.26,\"r\":254},{\"k\":6,\"tx\":276.29,\"ty\":69.92,\"r\":182},{\"k\":7,\"tx\":179.43,\"ty\":19.89,\"r\":72},{\"k\":8,\"tx\":193.89,\"ty\":41.22},{\"k\":9,\"tx\":189.55,\"ty\":65.84},{\"k\":10,\"tx\":185.21,\"ty\":90.46}],[{\"k\":4,\"tx\":237.86,\"ty\":77.6,\"r\":128},{\"k\":5,\"tx\":225.43,\"ty\":3.07,\"r\":255},{\"k\":6,\"tx\":275.97,\"ty\":70.3,\"r\":183},{\"k\":7,\"tx\":179.9,\"ty\":20.06,\"r\":73},{\"k\":8,\"tx\":194.33,\"ty\":38.75},{\"k\":9,\"tx\":189.98,\"ty\":63.37},{\"k\":10,\"tx\":185.64,\"ty\":87.99}],[{\"k\":4,\"tx\":237.99,\"ty\":78.08,\"r\":129},{\"k\":5,\"tx\":224.97,\"ty\":2.89,\"r\":256},{\"k\":6,\"tx\":275.64,\"ty\":70.68,\"r\":184},{\"k\":7,\"tx\":180.37,\"ty\":20.23,\"r\":74},{\"k\":8,\"tx\":194.76,\"ty\":36.29},{\"k\":9,\"tx\":190.42,\"ty\":60.91},{\"k\":10,\"tx\":186.08,\"ty\":85.53}],[{\"k\":4,\"tx\":238.12,\"ty\":78.56,\"r\":130},{\"k\":5,\"tx\":224.5,\"ty\":2.7,\"r\":257},{\"k\":6,\"tx\":275.32,\"ty\":71.07,\"r\":185},{\"k\":7,\"tx\":180.84,\"ty\":20.41,\"r\":75},{\"k\":8,\"tx\":195.19,\"ty\":33.83},{\"k\":9,\"tx\":190.85,\"ty\":58.45},{\"k\":10,\"tx\":186.51,\"ty\":83.07}],[{\"k\":4,\"tx\":238.25,\"ty\":79.05,\"r\":131},{\"k\":5,\"tx\":224.04,\"ty\":2.51,\"r\":258},{\"k\":6,\"tx\":275,\"ty\":71.45,\"r\":186},{\"k\":7,\"tx\":181.31,\"ty\":20.58,\"r\":76},{\"k\":8,\"tx\":195.63,\"ty\":31.37},{\"k\":9,\"tx\":191.29,\"ty\":55.99},{\"k\":10,\"tx\":186.95,\"ty\":80.61}],[{\"k\":4,\"tx\":238.38,\"ty\":79.53,\"r\":132},{\"k\":5,\"tx\":223.58,\"ty\":2.32,\"r\":259},{\"k\":6,\"tx\":274.68,\"ty\":71.83,\"r\":187},{\"k\":7,\"tx\":181.78,\"ty\":20.75,\"r\":77},{\"k\":8,\"tx\":196.06,\"ty\":28.91},{\"k\":9,\"tx\":191.72,\"ty\":53.53},{\"k\":10,\"tx\":187.38,\"ty\":78.15}],[{\"k\":4,\"tx\":238.51,\"ty\":80.01,\"r\":133},{\"k\":5,\"tx\":223.11,\"ty\":2.14,\"r\":260},{\"k\":6,\"tx\":274.36,\"ty\":72.22,\"r\":188},{\"k\":7,\"tx\":182.25,\"ty\":20.92,\"r\":78},{\"k\":8,\"tx\":196.5,\"ty\":26.44},{\"k\":9,\"tx\":192.16,\"ty\":51.06},{\"k\":10,\"tx\":187.81,\"ty\":75.68}],[{\"k\":4,\"tx\":238.64,\"ty\":80.49,\"r\":134},{\"k\":5,\"tx\":222.65,\"ty\":1.95,\"r\":261},{\"k\":6,\"tx\":274.04,\"ty\":72.6,\"r\":189},{\"k\":7,\"tx\":182.72,\"ty\":21.09,\"r\":79},{\"k\":8,\"tx\":196.93,\"ty\":23.98},{\"k\":9,\"tx\":192.59,\"ty\":48.6},{\"k\":10,\"tx\":188.25,\"ty\":73.22}],[{\"k\":4,\"tx\":238.76,\"ty\":80.98,\"r\":135},{\"k\":5,\"tx\":222.18,\"ty\":1.76,\"r\":262},{\"k\":6,\"tx\":273.72,\"ty\":72.98,\"r\":190},{\"k\":7,\"tx\":183.19,\"ty\":21.26,\"r\":80},{\"k\":8,\"tx\":197.36,\"ty\":21.52},{\"k\":9,\"tx\":193.02,\"ty\":46.14},{\"k\":10,\"tx\":188.68,\"ty\":70.76}],[{\"k\":4,\"tx\":238.89,\"ty\":81.46,\"r\":136},{\"k\":5,\"tx\":221.72,\"ty\":1.57,\"r\":263},{\"k\":6,\"tx\":273.39,\"ty\":73.36,\"r\":191},{\"k\":7,\"tx\":183.66,\"ty\":21.43,\"r\":81},{\"k\":8,\"tx\":197.8,\"ty\":19.06},{\"k\":9,\"tx\":193.46,\"ty\":43.68},{\"k\":10,\"tx\":189.12,\"ty\":68.3}],[{\"k\":4,\"tx\":239.02,\"ty\":81.94,\"r\":137},{\"k\":5,\"tx\":221.26,\"ty\":1.39,\"r\":264},{\"k\":6,\"tx\":273.07,\"ty\":73.75,\"r\":192},{\"k\":7,\"tx\":184.13,\"ty\":21.6,\"r\":82},{\"k\":8,\"tx\":198.23,\"ty\":16.6},{\"k\":9,\"tx\":193.89,\"ty\":41.22},{\"k\":10,\"tx\":189.55,\"ty\":65.84}],[{\"k\":4,\"tx\":239.15,\"ty\":82.43,\"r\":138},{\"k\":5,\"tx\":220.79,\"ty\":1.2,\"r\":265},{\"k\":6,\"tx\":272.75,\"ty\":74.13,\"r\":193},{\"k\":7,\"tx\":184.6,\"ty\":21.77,\"r\":83},{\"k\":8,\"tx\":198.67,\"ty\":14.13},{\"k\":9,\"tx\":194.33,\"ty\":38.75},{\"k\":10,\"tx\":189.98,\"ty\":63.37}],[{\"k\":4,\"tx\":239.28,\"ty\":82.91,\"r\":139},{\"k\":5,\"tx\":220.33,\"ty\":1.01,\"r\":266},{\"k\":6,\"tx\":272.43,\"ty\":74.51,\"r\":194},{\"k\":7,\"tx\":185.07,\"ty\":21.94,\"r\":84},{\"k\":8,\"tx\":199.1,\"ty\":11.67},{\"k\":9,\"tx\":194.76,\"ty\":36.29},{\"k\":10,\"tx\":190.42,\"ty\":60.91}],[{\"k\":4,\"tx\":239.41,\"ty\":83.39,\"r\":140},{\"k\":5,\"tx\":219.87,\"ty\":0.83,\"r\":267},{\"k\":6,\"tx\":272.11,\"ty\":74.9,\"r\":195},{\"k\":7,\"tx\":185.54,\"ty\":22.12,\"r\":85},{\"k\":8,\"tx\":199.54,\"ty\":9.21},{\"k\":9,\"tx\":195.19,\"ty\":33.83},{\"k\":10,\"tx\":190.85,\"ty\":58.45}],[{\"k\":4,\"tx\":239.54,\"ty\":83.88,\"r\":141},{\"k\":5,\"tx\":219.4,\"ty\":0.64,\"r\":268},{\"k\":6,\"tx\":271.79,\"ty\":75.28,\"r\":196},{\"k\":7,\"tx\":186.01,\"ty\":22.29,\"r\":86},{\"k\":8,\"tx\":199.97,\"ty\":6.75},{\"k\":9,\"tx\":195.63,\"ty\":31.37},{\"k\":10,\"tx\":191.29,\"ty\":55.99}],[{\"k\":1,\"children\":[2,3,4,5,6,8,10]},{\"k\":4,\"tx\":239.67,\"ty\":84.36,\"r\":142},{\"k\":5,\"tx\":218.94,\"ty\":0.45,\"r\":269},{\"k\":6,\"tx\":271.47,\"ty\":75.66,\"r\":197},{\"k\":8,\"tx\":200.4,\"ty\":4.29},{\"k\":10,\"tx\":191.72,\"ty\":53.53}],[{\"k\":4,\"tx\":239.8,\"ty\":84.84,\"r\":143},{\"k\":5,\"tx\":218.48,\"ty\":0.26,\"r\":270},{\"k\":6,\"tx\":271.15,\"ty\":76.05,\"r\":198},{\"k\":8,\"tx\":200.84,\"ty\":1.82},{\"k\":10,\"tx\":192.16,\"ty\":51.06}],[{\"k\":4,\"tx\":239.93,\"ty\":85.32,\"r\":144},{\"k\":5,\"tx\":218.01,\"ty\":0.08,\"r\":271},{\"k\":6,\"tx\":270.82,\"ty\":76.43,\"r\":199},{\"k\":8,\"tx\":201.27,\"ty\":239.36},{\"k\":10,\"tx\":192.59,\"ty\":48.6}],[{\"k\":4,\"tx\":240.06,\"ty\":85.81,\"r\":145},{\"k\":5,\"tx\":217.55,\"ty\":239.89,\"r\":272},{\"k\":6,\"tx\":270.5,\"ty\":76.81,\"r\":200},{\"k\":8,\"tx\":201.71,\"ty\":236.9},{\"k\":10,\"tx\":193.02,\"ty\":46.14}],[{\"k\":3,\"r\":-76},{\"k\":4,\"tx\":240.19,\"ty\":86.29,\"r\":146},{\"k\":5,\"tx\":217.08,\"ty\":239.7,\"r\":273},{\"k\":6,\"tx\":270.18,\"ty\":77.19,\"r\":201},{\"k\":8,\"tx\":202.14,\"ty\":234.44},{\"k\":10,\"tx\":193.46,\"ty\":43.68}],[{\"k\":3,\"r\":-72},{\"k\":4,\"tx\":240.32,\"ty\":86.77,\"r\":147},{\"k\":5,\"tx\":216.62,\"ty\":239.51,\"r\":274},{\"k\":6,\"tx\":269.86,\"ty\":77.58,\"r\":202},{\"k\":8,\"tx\":202.57,\"ty\":231.97},{\"k\":10,\"tx\":193.89,\"ty\":41.22}],[{\"k\":3,\"r\":-68},{\"k\":4,\"tx\":240.45,\"ty\":87.26,\"r\":148},{\"k\":5,\"tx\":216.16,\"ty\":239.33,\"r\":275},{\"k\":6,\"tx\":269.54,\"ty\":77.96,\"r\":203},{\"k\":8,\"tx\":203.01,\"ty\":229.51},{\"k\":10,\"tx\":194.33,\"ty\":38.75}],[{\"k\":3,\"r\":-64},{\"k\":4,\"tx\":240.58,\"ty\":87.74,\"r\":149},{\"k\":5,\"tx\":215.69,\"ty\":239.14,\"r\":276},{\"k\":6,\"tx\":269.22,\"ty\":78.34,\"r\":204},{\"k\":8,\"tx\":203.44,\"ty\":227.05},{\"k\":10,\"tx\":194.76,\"ty\":36.29}],[{\"k\":3,\"r\":-60},{\"k\":4,\"tx\":240.71,\"ty\":88.22,\"r\":150},{\"k\":5,\"tx\":215.23,\"ty\":238.95,\"r\":277},{\"k\":6,\"tx\":268.9,\"ty\":78.73,\"r\":205},{\"k\":8,\"tx\":203.88,\"ty\":224.59},{\"k\":10,\"tx\":195.19,\"ty\":33.83}],[{\"k\":3,\"r\":-56},{\"k\":4,\"tx\":240.84,\"ty\":88.71,\"r\":151},{\"k\":5,\"tx\":214.77,\"ty\":238.76,\"r\":278},{\"k\":6,\"tx\":268.57,\"ty\":79.11,\"r\":206},{\"k\":8,\"tx\":204.31,\"ty\":222.13},{\"k\":10,\"tx\":195.63,\"ty\":31.37}],[{\"k\":3,\"r\":-52},{\"k\":4,\"tx\":240.96,\"ty\":89.19,\"r\":152},{\"k\":5,\"tx\":214.3,\"ty\":238.58,\"r\":279},{\"k\":6,\"tx\":268.25,\"ty\":79.49,\"r\":207},{\"k\":8,\"tx\":204.74,\"ty\":219.66},{\"k\":10,\"tx\":196.06,\"ty\":28.91}],[{\"k\":3,\"r\":-48},{\"k\":4,\"tx\":241.09,\"ty\":89.67,\"r\":153},{\"k\":5,\"tx\":213.84,\"ty\":238.39,\"r\":280},{\"k\":6,\"tx\":267.93,\"ty\":79.88,\"r\":208},{\"k\":8,\"tx\":205.18,\"ty\":217.2},{\"k\":10,\"tx\":196.5,\"ty\":26.44}],[{\"k\":3,\"r\":-44},{\"k\":4,\"tx\":241.22,\"ty\":90.15,\"r\":154},{\"k\":5,\"tx\":213.38,\"ty\":238.2,\"r\":281},{\"k\":6,\"tx\":267.61,\"ty\":80.26,\"r\":209},{\"k\":8,\"tx\":205.61,\"ty\":214.74},{\"k\":10,\"tx\":196.93,\"ty\":23.98}],[{\"k\":3,\"r\":-40},{\"k\":4,\"tx\":241.35,\"ty\":90.64,\"r\":155},{\"k\":5,\"tx\":212.91,\"ty\":238.02,\"r\":282},{\"k\":6,\"tx\":267.29,\"ty\":80.64,\"r\":210},{\"k\":8,\"tx\":206.05,\"ty\":212.28},{\"k\":10,\"tx\":197.36,\"ty\":21.52}],[{\"k\":3,\"r\":-36},{\"k\":4,\"tx\":241.48,\"ty\":91.12,\"r\":156},{\"k\":5,\"tx\":212.45,\"ty\":237.83,\"r\":283},{\"k\":6,\"tx\":266.97,\"ty\":81.02,\"r\":211},{\"k\":8,\"tx\":206.48,\"ty\":209.82},{\"k\":10,\"tx\":197.8,\"ty\":19.06}],[{\"k\":3,\"r\":-32},{\"k\":4,\"tx\":241.61,\"ty\":91.6,\"r\":157},{\"k\":5,\"tx\":211.99,\"ty\":237.64,\"r\":284},{\"k\":6,\"tx\":266.65,\"ty\":81.41,\"r\":212},{\"k\":8,\"tx\":206.92,\"ty\":207.35},{\"k\":10,\"tx\":198.23,\"ty\":16.6}],[{\"k\":3,\"r\":-28},{\"k\":4,\"tx\":241.74,\"ty\":92.09,\"r\":158},{\"k\":5,\"tx\":211.52,\"ty\":237.45,\"r\":285},{\"k\":6,\"tx\":266.32,\"ty\":81.79,\"r\":213},{\"k\":8,\"tx\":207.35,\"ty\":204.89},{\"k\":10,\"tx\":198.67,\"ty\":14.13}],[{\"k\":3,\"r\":-24},{\"k\":4,\"tx\":241.87,\"ty\":92.57,\"r\":159},{\"k\":5,\"tx\":211.06,\"ty\":237.27,\"r\":286},{\"k\":6,\"tx\":266,\"ty\":82.17,\"r\":214},{\"k\":8,\"tx\":207.78,\"ty\":202.43},{\"k\":10,\"tx\":199.1,\"ty\":11.67}],[{\"k\":3,\"r\":-20},{\"k\":4,\"tx\":242,\"ty\":93.05,\"r\":160},{\"k\":5,\"tx\":210.59,\"ty\":237.08,\"r\":287},{\"k\":6,\"tx\":265.68,\"ty\":82.56,\"r\":215},{\"k\":8,\"tx\":208.22,\"ty\":199.97},{\"k\":10,\"tx\":199.54,\"ty\":9.21}],[{\"k\":3,\"r\":-16},{\"k\":4,\"tx\":242.13,\"ty\":93.53,\"r\":161},{\"k\":5,\"tx\":210.13,\"ty\":236.89,\"r\":288},{\"k\":6,\"tx\":265.36,\"ty\":82.94,\"r\":216},{\"k\":8,\"tx\":208.65,\"ty\":197.51},{\"k\":10,\"tx\":199.97,\"ty\":6.75}],[{\"k\":3,\"r\":-12},{\"k\":4,\"tx\":242.26,\"ty\":94.02,\"r\":162},{\"k\":5,\"tx\":209.67,\"ty\":236.7,\"r\":289},{\"k\":6,\"tx\":265.04,\"ty\":83.32,\"r\":217},{\"k\":8,\"tx\":209.09,\"ty\":195.04},{\"k\":10,\"tx\":200.4,\"ty\":4.29}],[{\"k\":3,\"r\":-8},{\"k\":4,\"tx\":242.39,\"ty\":94.5,\"r\":163},{\"k\":5,\"tx\":209.2,\"ty\":236.52,\"r\":290},{\"k\":6,\"tx\":264.72,\"ty\":83.71,\"r\":218},{\"k\":8,\"tx\":209.52,\"ty\":192.58},{\"k\":10,\"tx\":200.84,\"ty\":1.82}],[{\"k\":1,\"children\":[2,3,4,6,8]},{\"k\":3,\"r\":-4},{\"k\":4,\"tx\":242.52,\"ty\":94.98,\"r\":164},{\"k\":6,\"tx\":264.4,\"ty\":84.09,\"r\":219},{\"k\":8,\"tx\":209.95,\"ty\":190.12}],[{\"k\":3,\"r\":0},{\"k\":4,\"tx\":242.65,\"ty\":95.47,\"r\":165},{\"k\":6,\"tx\":264.07,\"ty\":84.47,\"r\":220},{\"k\":8,\"tx\":210.39,\"ty\":187.66}],[{\"k\":3,\"r\":4},{\"k\":4,\"tx\":242.78,\"ty\":95.95,\"r\":166},{\"k\":6,\"tx\":263.75,\"ty\":84.86,\"r\":221},{\"k\":8,\"tx\":210.82,\"ty\":185.2}],[{\"k\":3,\"r\":8},{\"k\":4,\"tx\":242.91,\"ty\":96.43,\"r\":167},{\"k\":6,\"tx\":263.43,\"ty\":85.24,\"r\":222},{\"k\":8,\"tx\":211.26,\"ty\":182.73}],[{\"k\":3,\"r\":12},{\"k\":4,\"tx\":243.04,\"ty\":96.92,\"r\":168},{\"k\":6,\"tx\":263.11,\"ty\":85.62,\"r\":223},{\"k\":8,\"tx\":211.69,\"ty\":180.27}],[{\"k\":3,\"r\":16},{\"k\":4,\"tx\":243.16,\"ty\":97.4,\"r\":169},{\"k\":6,\"tx\":262.79,\"ty\":86,\"r\":224},{\"k\":8,\"tx\":212.12,\"ty\":177.81}],[{\"k\":3,\"r\":20},{\"k\":4,\"tx\":243.29,\"ty\":97.88,\"r\":170},{\"k\":6,\"tx\":262.47,\"ty\":86.39,\"r\":225},{\"k\":8,\"tx\":212.56,\"ty\":175.35}],[{\"k\":3,\"r\":24},{\"k\":4,\"tx\":243.42,\"ty\":98.36,\"r\":171},{\"k\":6,\"tx\":262.15,\"ty\":86.77,\"r\":226},{\"k\":8,\"tx\":212.99,\"ty\":172.89}],[{\"k\":3,\"r\":28},{\"k\":4,\"tx\":243.55,\"ty\":98.85,\"r\":172},{\"k\":6,\"tx\":261.82,\"ty\":87.15,\"r\":227},{\"k\":8,\"tx\":213.43,\"ty\":170.42}],[{\"k\":3,\"r\":32},{\"k\":4,\"tx\":243.68,\"ty\":99.33,\"r\":173},{\"k\":6,\"tx\":261.5,\"ty\":87.54,\"r\":228},{\"k\":8,\"tx\":213.86,\"ty\":167.96}],[{\"k\":3,\"r\":36},{\"k\":4,\"tx\":243.81,\"ty\":99.81,\"r\":174},{\"k\":6,\"tx\":261.18,\"ty\":87.92,\"r\":229},{\"k\":8,\"tx\":214.3,\"ty\":165.5}],[{\"k\":3,\"r\":40},{\"k\":4,\"tx\":243.94,\"ty\":100.3,\"r\":175},{\"k\":6,\"tx\":260.86,\"ty\":88.3,\"r\":230},{\"k\":8,\"tx\":214.73,\"ty\":163.04}],[{\"k\":1,\"children\":[2,3,4,6,8,11]},{\"k\":4,\"tx\":244.07,\"ty\":100.78,\"r\":176},{\"k\":6,\"tx\":260.54,\"ty\":88.69,\"r\":231},{\"k\":8,\"tx\":215.16,\"ty\":160.58},{\"k\":11,\"type\":\"Circle\",\"tx\":181.92,\"ty\":121.61,\"r\":40,\"radius\":3,\"fill\":\"#e0ffff\"}],[{\"k\":4,\"tx\":244.2,\"ty\":101.26,\"r\":177},{\"k\":6,\"tx\":260.22,\"ty\":89.07,\"r\":232},{\"k\":8,\"tx\":215.6,\"ty\":158.11},{\"k\":11,\"tx\":183.83,\"ty\":123.21}],[{\"k\":4,\"tx\":244.33,\"ty\":101.75,\"r\":178},{\"k\":6,\"tx\":259.9,\"ty\":89.45,\"r\":233},{\"k\":8,\"tx\":216.03,\"ty\":155.65},{\"k\":11,\"tx\":185.75,\"ty\":124.82}],[{\"k\":4,\"tx\":244.46,\"ty\":102.23,\"r\":179},{\"k\":6,\"tx\":259.58,\"ty\":89.83,\"r\":234},{\"k\":8,\"tx\":216.47,\"ty\":153.19},{\"k\":11,\"tx\":187.66,\"ty\":126.43}],[{\"k\":4,\"tx\":244.59,\"ty\":102.71,\"r\":180},{\"k\":6,\"tx\":259.25,\"ty\":90.22,\"r\":235},{\"k\":8,\"tx\":216.9,\"ty\":150.73},{\"k\":11,\"tx\":189.58,\"ty\":128.03}],[{\"k\":4,\"tx\":244.72,\"ty\":103.19,\"r\":181},{\"k\":6,\"tx\":258.93,\"ty\":90.6,\"r\":236},{\"k\":8,\"tx\":217.33,\"ty\":148.27},{\"k\":11,\"tx\":191.49,\"ty\":129.64}],[{\"k\":4,\"tx\":244.85,\"ty\":103.68,\"r\":182},{\"k\":6,\"tx\":258.61,\"ty\":90.98,\"r\":237},{\"k\":8,\"tx\":217.77,\"ty\":145.8},{\"k\":11,\"tx\":193.41,\"ty\":131.25}],[{\"k\":4,\"tx\":244.98,\"ty\":104.16,\"r\":183},{\"k\":6,\"tx\":258.29,\"ty\":91.37,\"r\":238},{\"k\":8,\"tx\":218.2,\"ty\":143.34},{\"k\":11,\"tx\":195.32,\"ty\":132.86}],[{\"k\":4,\"tx\":245.11,\"ty\":104.64,\"r\":184},{\"k\":6,\"tx\":257.97,\"ty\":91.75,\"r\":239},{\"k\":8,\"tx\":218.64,\"ty\":140.88},{\"k\":11,\"tx\":197.24,\"ty\":134.46}],[{\"k\":4,\"tx\":245.24,\"ty\":105.13,\"r\":185},{\"k\":6,\"tx\":257.65,\"ty\":92.13,\"r\":240},{\"k\":8,\"tx\":219.07,\"ty\":138.42},{\"k\":11,\"tx\":199.15,\"ty\":136.07}],[{\"k\":4,\"tx\":245.36,\"ty\":105.61,\"r\":186},{\"k\":6,\"tx\":257.33,\"ty\":92.52,\"r\":241},{\"k\":8,\"tx\":219.5,\"ty\":135.96},{\"k\":11,\"tx\":201.07,\"ty\":137.68}],[{\"k\":4,\"tx\":245.49,\"ty\":106.09,\"r\":187},{\"k\":6,\"tx\":257,\"ty\":92.9,\"r\":242},{\"k\":8,\"tx\":219.94,\"ty\":133.49},{\"k\":11,\"tx\":202.98,\"ty\":139.28}],[{\"k\":4,\"tx\":245.62,\"ty\":106.57,\"r\":188},{\"k\":6,\"tx\":256.68,\"ty\":93.28,\"r\":243},{\"k\":8,\"tx\":220.37,\"ty\":131.03},{\"k\":11,\"tx\":204.9,\"ty\":140.89}],[{\"k\":4,\"tx\":245.75,\"ty\":107.06,\"r\":189},{\"k\":6,\"tx\":256.36,\"ty\":93.66,\"r\":244},{\"k\":8,\"tx\":220.81,\"ty\":128.57},{\"k\":11,\"tx\":206.81,\"ty\":142.5}],[{\"k\":4,\"tx\":245.88,\"ty\":107.54,\"r\":190},{\"k\":6,\"tx\":256.04,\"ty\":94.05,\"r\":245},{\"k\":8,\"tx\":221.24,\"ty\":126.11},{\"k\":11,\"tx\":208.73,\"ty\":144.1}],[{\"k\":4,\"tx\":246.01,\"ty\":108.02,\"r\":191},{\"k\":6,\"tx\":255.72,\"ty\":94.43,\"r\":246},{\"k\":8,\"tx\":221.68,\"ty\":123.65},{\"k\":11,\"tx\":210.64,\"ty\":145.71}],[{\"k\":4,\"tx\":246.14,\"ty\":108.51,\"r\":192},{\"k\":6,\"tx\":255.4,\"ty\":94.81,\"r\":247},{\"k\":8,\"tx\":222.11,\"ty\":121.18},{\"k\":11,\"tx\":212.56,\"ty\":147.32}],[{\"k\":4,\"tx\":246.27,\"ty\":108.99,\"r\":193},{\"k\":6,\"tx\":255.08,\"ty\":95.2,\"r\":248},{\"k\":8,\"tx\":222.54,\"ty\":118.72},{\"k\":11,\"tx\":214.47,\"ty\":148.93}],[{\"k\":4,\"tx\":246.4,\"ty\":109.47,\"r\":194},{\"k\":6,\"tx\":254.75,\"ty\":95.58,\"r\":249},{\"k\":8,\"tx\":222.98,\"ty\":116.26},{\"k\":11,\"tx\":216.39,\"ty\":150.53}],[{\"k\":4,\"tx\":246.53,\"ty\":109.96,\"r\":195},{\"k\":6,\"tx\":254.43,\"ty\":95.96,\"r\":250},{\"k\":8,\"tx\":223.41,\"ty\":113.8},{\"k\":11,\"tx\":218.3,\"ty\":152.14}],[{\"k\":4,\"tx\":246.66,\"ty\":110.44,\"r\":196},{\"k\":6,\"tx\":254.11,\"ty\":96.35,\"r\":251},{\"k\":8,\"tx\":223.85,\"ty\":111.34},{\"k\":11,\"tx\":220.22,\"ty\":153.75}],[{\"k\":4,\"tx\":246.79,\"ty\":110.92,\"r\":197},{\"k\":6,\"tx\":253.79,\"ty\":96.73,\"r\":252},{\"k\":8,\"tx\":224.28,\"ty\":108.87},{\"k\":11,\"tx\":222.13,\"ty\":155.35}],[{\"k\":4,\"tx\":246.92,\"ty\":111.4,\"r\":198},{\"k\":6,\"tx\":253.47,\"ty\":97.11,\"r\":253},{\"k\":8,\"tx\":224.71,\"ty\":106.41},{\"k\":11,\"tx\":224.05,\"ty\":156.96}],[{\"k\":4,\"tx\":247.05,\"ty\":111.89,\"r\":199},{\"k\":6,\"tx\":253.15,\"ty\":97.49,\"r\":254},{\"k\":8,\"tx\":225.15,\"ty\":103.95},{\"k\":11,\"tx\":225.96,\"ty\":158.57}],[{\"k\":4,\"tx\":247.18,\"ty\":112.37,\"r\":200},{\"k\":6,\"tx\":252.83,\"ty\":97.88,\"r\":255},{\"k\":8,\"tx\":225.58,\"ty\":101.49},{\"k\":11,\"tx\":227.88,\"ty\":160.17}],[{\"k\":4,\"tx\":247.31,\"ty\":112.85,\"r\":201},{\"k\":6,\"tx\":252.5,\"ty\":98.26,\"r\":256},{\"k\":8,\"tx\":226.02,\"ty\":99.03},{\"k\":11,\"tx\":229.79,\"ty\":161.78}],[{\"k\":4,\"tx\":247.44,\"ty\":113.34,\"r\":202},{\"k\":6,\"tx\":252.18,\"ty\":98.64,\"r\":257},{\"k\":8,\"tx\":226.45,\"ty\":96.56},{\"k\":11,\"tx\":231.71,\"ty\":163.39}],[{\"k\":4,\"tx\":247.56,\"ty\":113.82,\"r\":203},{\"k\":6,\"tx\":251.86,\"ty\":99.03,\"r\":258},{\"k\":8,\"tx\":226.89,\"ty\":94.1},{\"k\":11,\"tx\":233.62,\"ty\":165}],[{\"k\":4,\"tx\":247.69,\"ty\":114.3,\"r\":204},{\"k\":6,\"tx\":251.54,\"ty\":99.41,\"r\":259},{\"k\":8,\"tx\":227.32,\"ty\":91.64},{\"k\":11,\"tx\":235.54,\"ty\":166.6}],[{\"k\":4,\"tx\":247.82,\"ty\":114.79,\"r\":205},{\"k\":6,\"tx\":251.22,\"ty\":99.79,\"r\":260},{\"k\":8,\"tx\":227.75,\"ty\":89.18},{\"k\":11,\"tx\":237.45,\"ty\":168.21}],[{\"k\":4,\"tx\":247.95,\"ty\":115.27,\"r\":206},{\"k\":6,\"tx\":250.9,\"ty\":100.18,\"r\":261},{\"k\":8,\"tx\":228.19,\"ty\":86.72},{\"k\":11,\"tx\":239.37,\"ty\":169.82}],[{\"k\":4,\"tx\":248.08,\"ty\":115.75,\"r\":207},{\"k\":6,\"tx\":250.58,\"ty\":100.56,\"r\":262},{\"k\":8,\"tx\":228.62,\"ty\":84.25},{\"k\":11,\"tx\":241.28,\"ty\":171.42}],[{\"k\":4,\"tx\":248.21,\"ty\":116.23,\"r\":208},{\"k\":6,\"tx\":250.25,\"ty\":100.94,\"r\":263},{\"k\":8,\"tx\":229.06,\"ty\":81.79},{\"k\":11,\"tx\":243.2,\"ty\":173.03}],[{\"k\":4,\"tx\":248.34,\"ty\":116.72,\"r\":209},{\"k\":6,\"tx\":249.93,\"ty\":101.32,\"r\":264},{\"k\":8,\"tx\":229.49,\"ty\":79.33},{\"k\":11,\"tx\":245.11,\"ty\":174.64}],[{\"k\":4,\"tx\":248.47,\"ty\":117.2,\"r\":210},{\"k\":6,\"tx\":249.61,\"ty\":101.71,\"r\":265},{\"k\":8,\"tx\":229.92,\"ty\":76.87},{\"k\":11,\"tx\":247.03,\"ty\":176.24}],[{\"k\":4,\"tx\":248.6,\"ty\":117.68,\"r\":211},{\"k\":6,\"tx\":249.29,\"ty\":102.09,\"r\":266},{\"k\":8,\"tx\":230.36,\"ty\":74.41},{\"k\":11,\"tx\":248.94,\"ty\":177.85}],[{\"k\":4,\"tx\":248.73,\"ty\":118.17,\"r\":212},{\"k\":6,\"tx\":248.97,\"ty\":102.47,\"r\":267},{\"k\":8,\"tx\":230.79,\"ty\":71.94},{\"k\":11,\"tx\":250.86,\"ty\":179.46}],[{\"k\":4,\"tx\":248.86,\"ty\":118.65,\"r\":213},{\"k\":6,\"tx\":248.65,\"ty\":102.86,\"r\":268},{\"k\":8,\"tx\":231.23,\"ty\":69.48},{\"k\":11,\"tx\":252.77,\"ty\":181.06}],[{\"k\":4,\"tx\":248.99,\"ty\":119.13,\"r\":214},{\"k\":6,\"tx\":248.33,\"ty\":103.24,\"r\":269},{\"k\":8,\"tx\":231.66,\"ty\":67.02},{\"k\":11,\"tx\":254.69,\"ty\":182.67}],[{\"k\":4,\"tx\":249.12,\"ty\":119.61,\"r\":215},{\"k\":6,\"tx\":248,\"ty\":103.62,\"r\":270},{\"k\":8,\"tx\":232.09,\"ty\":64.56},{\"k\":11,\"tx\":256.6,\"ty\":184.28}]]}},\"outline\":\"Window \\\"Bubbles\\\"\\n  Pane 360 x 240\\n    Rectangle 360 x 240 at (0, 0), fill #191970\\n    Polygon (-15, -6) (9, -6) (15, 0) (9, 6) (-15, 6), at (180, 120), rotate 40, fill #ffd700\\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (249.11733315717788, 119.61480784023462), rotate 215, fill #da70d6\\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (248.0048673219392, 103.62311101832802), rotate 270, fill #da70d6\\n    Circle radius 3 at (0, 0), translate (232.09445330007838, 64.55767409633816), rotate -80, fill #e0ffff\\n    Circle radius 3 at (0, 0), translate (256.6044443118983, 184.27876096865367), rotate 40, fill #e0ffff\\nframes run: 140\"}\n```\n\n```output\nPop!\nPop!\n```\n\nThe submarine turns up and fires three bubbles: two of them pop a jellyfish each, and the other one flies on, wrapping around the sea. Then the submarine turns to 40 degrees and fires a fourth bubble. A bubble that never hits anything flies forever here, and four of them would keep the submarine from ever firing again: the second challenge below gives bubbles a lifetime.\n","task":"In the starter code, bubbles and jellyfish are marked when they collide, but nothing is removed, so a popped jellyfish stays and pops again in every frame. Where the comment is, remove every bubble and every jellyfish that isn't alive: first their shapes from the pane, then the Movers from their lists, with streams, `filter` and `collect` as above. The test fires three bubbles and lets them fly.\n```\nPop!\nPop!\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport javafx.animation.AnimationTimer;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Rectangle;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n    public static final int WIDTH = 360;\n    public static final int HEIGHT = 240;\n\n    private Map<KeyCode, Boolean> held = new HashMap<>();\n    private Random random = new Random(16);\n    private List<Jellyfish> jellyfish = new ArrayList<>();\n    private List<Bubble> bubbles = new ArrayList<>();\n\n    @Override\n    public void start(Stage stage) {\n        Pane pane = new Pane();\n        pane.setPrefSize(WIDTH, HEIGHT);\n        pane.getChildren().add(new Rectangle(WIDTH, HEIGHT, Color.MIDNIGHTBLUE));\n\n        Sub sub = new Sub(WIDTH / 2, HEIGHT / 2);\n        pane.getChildren().add(sub.getShape());\n        for (int i = 0; i < 4; i++) {\n            Jellyfish j = new Jellyfish(random.nextInt(WIDTH), random.nextInt(60), random.nextInt(360));\n            jellyfish.add(j);\n            pane.getChildren().add(j.getShape());\n        }\n\n        Scene scene = new Scene(pane);\n        scene.setOnKeyPressed(event -> {\n            held.put(event.getCode(), true);\n            if (event.getCode() == KeyCode.SPACE && bubbles.size() < 4) {\n                Bubble bubble = new Bubble(sub.getShape().getTranslateX(), sub.getShape().getTranslateY(), sub.getShape().getRotate());\n                bubbles.add(bubble);\n                pane.getChildren().add(bubble.getShape());\n            }\n        });\n        scene.setOnKeyReleased(event -> held.put(event.getCode(), false));\n\n        new AnimationTimer() {\n            @Override\n            public void handle(long now) {\n                if (held.getOrDefault(KeyCode.LEFT, false)) {\n                    sub.turn(-4);\n                }\n                if (held.getOrDefault(KeyCode.RIGHT, false)) {\n                    sub.turn(4);\n                }\n                if (held.getOrDefault(KeyCode.UP, false)) {\n                    sub.accelerate(0.125);\n                }\n                sub.move();\n                jellyfish.forEach(j -> j.move());\n                bubbles.forEach(b -> b.move());\n\n                for (Bubble b : bubbles) {\n                    for (Jellyfish j : jellyfish) {\n                        if (b.collides(j)) {\n                            b.setAlive(false);\n                            j.setAlive(false);\n                            System.out.println(\"Pop!\");\n                        }\n                    }\n                }\n                // remove what popped\n\n                for (Jellyfish j : jellyfish) {\n                    if (sub.collides(j)) {\n                        sub.getShape().setFill(Color.RED);\n                        System.out.println(\"Bump!\");\n                        stop();\n                        break;\n                    }\n                }\n            }\n        }.start();\n\n        stage.setScene(scene);\n        stage.setTitle(\"Bubbles\");\n        stage.show();\n    }\n\n    public static double wrap(double value, double size) {\n        double result = value % size;\n        if (result < 0) {\n            result = result + size;\n        }\n        return result;\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== Mover.java ====\nimport javafx.geometry.Point2D;\nimport javafx.scene.shape.Shape;\n\npublic abstract class Mover {\n    private Shape shape;\n    private Point2D velocity;\n    private boolean alive;\n\n    public Mover(Shape shape, double x, double y) {\n        this.shape = shape;\n        this.shape.setTranslateX(x);\n        this.shape.setTranslateY(y);\n        this.velocity = new Point2D(0, 0);\n        this.alive = true;\n    }\n\n    public Shape getShape() {\n        return shape;\n    }\n\n    public boolean isAlive() {\n        return alive;\n    }\n\n    public void setAlive(boolean alive) {\n        this.alive = alive;\n    }\n\n    public void turn(double degrees) {\n        shape.setRotate(shape.getRotate() + degrees);\n    }\n\n    public void accelerate(double amount) {\n        double angle = Math.toRadians(shape.getRotate());\n        velocity = velocity.add(Math.cos(angle) * amount, Math.sin(angle) * amount);\n    }\n\n    public void move() {\n        shape.setTranslateX(Main.wrap(shape.getTranslateX() + velocity.getX(), Main.WIDTH));\n        shape.setTranslateY(Main.wrap(shape.getTranslateY() + velocity.getY(), Main.HEIGHT));\n    }\n\n    public boolean collides(Mover other) {\n        Shape overlap = Shape.intersect(shape, other.getShape());\n        return overlap.getBoundsInLocal().getWidth() != -1;\n    }\n}\n\n// ==== Sub.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Sub extends Mover {\n\n    public Sub(double x, double y) {\n        super(new Polygon(-15, -6, 9, -6, 15, 0, 9, 6, -15, 6), x, y);\n        getShape().setFill(Color.GOLD);\n    }\n}\n\n// ==== Jellyfish.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Jellyfish extends Mover {\n\n    public Jellyfish(double x, double y, double heading) {\n        super(new Polygon(-10, 0, -6, -8, 0, -10, 6, -8, 10, 0, 7, 10, 3, 2, 0, 10, -3, 2, -7, 10), x, y);\n        getShape().setFill(Color.ORCHID);\n        turn(heading);\n        accelerate(0.5);\n    }\n\n    @Override\n    public void move() {\n        super.move();\n        turn(1);\n    }\n}\n\n// ==== Bubble.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Circle;\n\npublic class Bubble extends Mover {\n\n    public Bubble(double x, double y, double heading) {\n        super(new Circle(3, Color.LIGHTCYAN), x, y);\n        turn(heading);\n        accelerate(2.5);\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport javafx.animation.AnimationTimer;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Rectangle;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n    public static final int WIDTH = 360;\n    public static final int HEIGHT = 240;\n\n    private Map<KeyCode, Boolean> held = new HashMap<>();\n    private Random random = new Random(16);\n    private List<Jellyfish> jellyfish = new ArrayList<>();\n    private List<Bubble> bubbles = new ArrayList<>();\n\n    @Override\n    public void start(Stage stage) {\n        Pane pane = new Pane();\n        pane.setPrefSize(WIDTH, HEIGHT);\n        pane.getChildren().add(new Rectangle(WIDTH, HEIGHT, Color.MIDNIGHTBLUE));\n\n        Sub sub = new Sub(WIDTH / 2, HEIGHT / 2);\n        pane.getChildren().add(sub.getShape());\n        for (int i = 0; i < 4; i++) {\n            Jellyfish j = new Jellyfish(random.nextInt(WIDTH), random.nextInt(60), random.nextInt(360));\n            jellyfish.add(j);\n            pane.getChildren().add(j.getShape());\n        }\n\n        Scene scene = new Scene(pane);\n        scene.setOnKeyPressed(event -> {\n            held.put(event.getCode(), true);\n            if (event.getCode() == KeyCode.SPACE && bubbles.size() < 4) {\n                Bubble bubble = new Bubble(sub.getShape().getTranslateX(), sub.getShape().getTranslateY(), sub.getShape().getRotate());\n                bubbles.add(bubble);\n                pane.getChildren().add(bubble.getShape());\n            }\n        });\n        scene.setOnKeyReleased(event -> held.put(event.getCode(), false));\n\n        new AnimationTimer() {\n            @Override\n            public void handle(long now) {\n                if (held.getOrDefault(KeyCode.LEFT, false)) {\n                    sub.turn(-4);\n                }\n                if (held.getOrDefault(KeyCode.RIGHT, false)) {\n                    sub.turn(4);\n                }\n                if (held.getOrDefault(KeyCode.UP, false)) {\n                    sub.accelerate(0.125);\n                }\n                sub.move();\n                jellyfish.forEach(j -> j.move());\n                bubbles.forEach(b -> b.move());\n\n                for (Bubble b : bubbles) {\n                    for (Jellyfish j : jellyfish) {\n                        if (b.collides(j)) {\n                            b.setAlive(false);\n                            j.setAlive(false);\n                            System.out.println(\"Pop!\");\n                        }\n                    }\n                }\n                bubbles.stream()\n                        .filter(b -> !b.isAlive())\n                        .forEach(b -> pane.getChildren().remove(b.getShape()));\n                jellyfish.stream()\n                        .filter(j -> !j.isAlive())\n                        .forEach(j -> pane.getChildren().remove(j.getShape()));\n                bubbles = bubbles.stream()\n                        .filter(b -> b.isAlive())\n                        .collect(Collectors.toList());\n                jellyfish = jellyfish.stream()\n                        .filter(j -> j.isAlive())\n                        .collect(Collectors.toList());\n\n                for (Jellyfish j : jellyfish) {\n                    if (sub.collides(j)) {\n                        sub.getShape().setFill(Color.RED);\n                        System.out.println(\"Bump!\");\n                        stop();\n                        break;\n                    }\n                }\n            }\n        }.start();\n\n        stage.setScene(scene);\n        stage.setTitle(\"Bubbles\");\n        stage.show();\n    }\n\n    public static double wrap(double value, double size) {\n        double result = value % size;\n        if (result < 0) {\n            result = result + size;\n        }\n        return result;\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== Mover.java ====\nimport javafx.geometry.Point2D;\nimport javafx.scene.shape.Shape;\n\npublic abstract class Mover {\n    private Shape shape;\n    private Point2D velocity;\n    private boolean alive;\n\n    public Mover(Shape shape, double x, double y) {\n        this.shape = shape;\n        this.shape.setTranslateX(x);\n        this.shape.setTranslateY(y);\n        this.velocity = new Point2D(0, 0);\n        this.alive = true;\n    }\n\n    public Shape getShape() {\n        return shape;\n    }\n\n    public boolean isAlive() {\n        return alive;\n    }\n\n    public void setAlive(boolean alive) {\n        this.alive = alive;\n    }\n\n    public void turn(double degrees) {\n        shape.setRotate(shape.getRotate() + degrees);\n    }\n\n    public void accelerate(double amount) {\n        double angle = Math.toRadians(shape.getRotate());\n        velocity = velocity.add(Math.cos(angle) * amount, Math.sin(angle) * amount);\n    }\n\n    public void move() {\n        shape.setTranslateX(Main.wrap(shape.getTranslateX() + velocity.getX(), Main.WIDTH));\n        shape.setTranslateY(Main.wrap(shape.getTranslateY() + velocity.getY(), Main.HEIGHT));\n    }\n\n    public boolean collides(Mover other) {\n        Shape overlap = Shape.intersect(shape, other.getShape());\n        return overlap.getBoundsInLocal().getWidth() != -1;\n    }\n}\n\n// ==== Sub.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Sub extends Mover {\n\n    public Sub(double x, double y) {\n        super(new Polygon(-15, -6, 9, -6, 15, 0, 9, 6, -15, 6), x, y);\n        getShape().setFill(Color.GOLD);\n    }\n}\n\n// ==== Jellyfish.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Jellyfish extends Mover {\n\n    public Jellyfish(double x, double y, double heading) {\n        super(new Polygon(-10, 0, -6, -8, 0, -10, 6, -8, 10, 0, 7, 10, 3, 2, 0, 10, -3, 2, -7, 10), x, y);\n        getShape().setFill(Color.ORCHID);\n        turn(heading);\n        accelerate(0.5);\n    }\n\n    @Override\n    public void move() {\n        super.move();\n        turn(1);\n    }\n}\n\n// ==== Bubble.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Circle;\n\npublic class Bubble extends Mover {\n\n    public Bubble(double x, double y, double heading) {\n        super(new Circle(3, Color.LIGHTCYAN), x, y);\n        turn(heading);\n        accelerate(2.5);\n    }\n}\n","hints":["Two jobs for each list, in this order: take the shapes of the dead ones out of the pane, then keep only the living ones in the list. Streams with `filter` do both.","For the bubbles: `bubbles.stream().filter(b -> !b.isAlive()).forEach(b -> pane.getChildren().remove(b.getShape()));` removes the shapes. Do the same for the jellyfish.","Then keep the living ones: `bubbles = bubbles.stream().filter(b -> b.isAlive()).collect(Collectors.toList());`, and the same for `jellyfish`. Remove the shapes first: once a dead bubble has left the list, nothing knows about it any more."],"tests":[{"name":"Test 1","stdin":"","expect":"Pop!\nPop!","hidden":false,"events":["press key LEFT","frames 20","release key LEFT","key SPACE","frames 10","key SPACE","frames 10","key SPACE","frames 30","press key RIGHT","frames 30","release key RIGHT","key SPACE","frames 40"],"window":{"windows":[{"title":"Bubbles","scene":{"width":360,"height":240,"onKeyPressed":true,"onKeyReleased":true,"root":{"n":1,"type":"Pane","f":1,"prefWidth":360,"prefHeight":240,"children":[{"n":2,"type":"Rectangle","f":2,"x":0,"y":0,"width":360,"height":240,"fill":"#191970"},{"n":3,"type":"Polygon","f":3,"translateX":180,"translateY":120,"rotate":40,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":249.11733315717788,"translateY":119.61480784023462,"rotate":215,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":5,"type":"Polygon","f":6,"translateX":248.0048673219392,"translateY":103.62311101832802,"rotate":270,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":6,"type":"Circle","f":8,"translateX":232.09445330007838,"translateY":64.55767409633816,"rotate":-80,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":7,"type":"Circle","f":11,"translateX":256.6044443118983,"translateY":184.27876096865367,"rotate":40,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"}]}}}],"problems":[],"frames":{"count":140,"list":[]}},"outline":"Window \"Bubbles\"\n  Pane 360 x 240\n    Rectangle 360 x 240 at (0, 0), fill #191970\n    Polygon (-15, -6) (9, -6) (15, 0) (9, 6) (-15, 6), at (180, 120), rotate 40, fill #ffd700\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (249.11733315717788, 119.61480784023462), rotate 215, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (248.0048673219392, 103.62311101832802), rotate 270, fill #da70d6\n    Circle radius 3 at (0, 0), translate (232.09445330007838, 64.55767409633816), rotate -80, fill #e0ffff\n    Circle radius 3 at (0, 0), translate (256.6044443118983, 184.27876096865367), rotate 40, fill #e0ffff\nframes run: 140"},{"name":"Test 2","stdin":"","expect":"Pop!","hidden":true,"events":["press key RIGHT","frames 12","release key RIGHT","key SPACE","frames 5","key SPACE","frames 5","press key LEFT","frames 30","release key LEFT","key SPACE","frames 80","key SPACE","frames 30"],"window":{"windows":[{"title":"Bubbles","scene":{"width":360,"height":240,"onKeyPressed":true,"onKeyReleased":true,"root":{"n":1,"type":"Pane","f":1,"prefWidth":360,"prefHeight":240,"children":[{"n":2,"type":"Rectangle","f":2,"x":0,"y":0,"width":360,"height":240,"fill":"#191970"},{"n":3,"type":"Polygon","f":3,"translateX":180,"translateY":120,"rotate":-72,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":251.96434265330583,"translateY":130.2399919294144,"rotate":237,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":5,"type":"Polygon","f":5,"translateX":174.8981077800887,"translateY":222.65686593331014,"rotate":364,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":6,"type":"Polygon","f":6,"translateX":240.9342036153873,"translateY":112.0495998926367,"rotate":292,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":7,"type":"Circle","f":8,"translateX":70.92397738457298,"translateY":158.67930955402323,"rotate":48,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":8,"type":"Circle","f":9,"translateX":62.55984480508726,"translateY":149.3899992355558,"rotate":48,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":9,"type":"Circle","f":11,"translateX":203.17627457812137,"translateY":48.67076127786348,"rotate":-72,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"}]}}}],"problems":[],"frames":{"count":162,"list":[]}},"outline":"Window \"Bubbles\"\n  Pane 360 x 240\n    Rectangle 360 x 240 at (0, 0), fill #191970\n    Polygon (-15, -6) (9, -6) (15, 0) (9, 6) (-15, 6), at (180, 120), rotate -72, fill #ffd700\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (251.96434265330583, 130.2399919294144), rotate 237, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (174.8981077800887, 222.65686593331014), rotate 364, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (240.9342036153873, 112.0495998926367), rotate 292, fill #da70d6\n    Circle radius 3 at (0, 0), translate (70.92397738457298, 158.67930955402323), rotate 48, fill #e0ffff\n    Circle radius 3 at (0, 0), translate (62.55984480508726, 149.3899992355558), rotate 48, fill #e0ffff\n    Circle radius 3 at (0, 0), translate (203.17627457812137, 48.67076127786348), rotate -72, fill #e0ffff\nframes run: 162"},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["key SPACE","frames 100"],"window":{"windows":[{"title":"Bubbles","scene":{"width":360,"height":240,"onKeyPressed":true,"onKeyReleased":true,"root":{"n":1,"type":"Pane","f":1,"prefWidth":360,"prefHeight":240,"children":[{"n":2,"type":"Rectangle","f":2,"x":0,"y":0,"width":360,"height":240,"fill":"#191970"},{"n":3,"type":"Polygon","f":3,"translateX":180,"translateY":120,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":243.94095225512706,"translateY":100.2962913144533,"rotate":175,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":5,"type":"Polygon","f":5,"translateX":203.6408072716597,"translateY":234.26967032920408,"rotate":302,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":6,"type":"Polygon","f":6,"translateX":260.86061951567035,"translateY":88.30222215594858,"rotate":230,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":7,"type":"Polygon","f":7,"translateX":201.9846310392967,"translateY":28.101007166283466,"rotate":120,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":8,"type":"Circle","f":8,"translateX":70,"translateY":120,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"}]}}}],"problems":[],"frames":{"count":100,"list":[]}},"outline":"Window \"Bubbles\"\n  Pane 360 x 240\n    Rectangle 360 x 240 at (0, 0), fill #191970\n    Polygon (-15, -6) (9, -6) (15, 0) (9, 6) (-15, 6), at (180, 120), fill #ffd700\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (243.94095225512706, 100.2962913144533), rotate 175, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (203.6408072716597, 234.26967032920408), rotate 302, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (260.86061951567035, 88.30222215594858), rotate 230, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (201.9846310392967, 28.101007166283466), rotate 120, fill #da70d6\n    Circle radius 3 at (0, 0), translate (70, 120), fill #e0ffff\nframes run: 100"}],"require":[],"forbid":[],"more":[{"task":"Bubbles that don't hit anything should pop by themselves after a while. Change the class `Bubble`: give it a field `age`, 0 at the start, and override `move()` so that it moves the bubble as a Mover does, then adds 1 to its age, and when the age reaches 50, marks the bubble as not alive. `Main` already removes every Mover that isn't alive.\n\nThe test fires a bubble and runs 50 frames: the bubble is gone, and the submarine can fire four more.\n","kind":"code","seed":"// ==== Bubble.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Circle;\n\npublic class Bubble extends Mover {\n\n    public Bubble(double x, double y, double heading) {\n        super(new Circle(3, Color.LIGHTCYAN), x, y);\n        turn(heading);\n        accelerate(2.5);\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport javafx.animation.AnimationTimer;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Rectangle;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n    public static final int WIDTH = 360;\n    public static final int HEIGHT = 240;\n\n    private Map<KeyCode, Boolean> held = new HashMap<>();\n    private Random random = new Random(16);\n    private List<Jellyfish> jellyfish = new ArrayList<>();\n    private List<Bubble> bubbles = new ArrayList<>();\n\n    @Override\n    public void start(Stage stage) {\n        Pane pane = new Pane();\n        pane.setPrefSize(WIDTH, HEIGHT);\n        pane.getChildren().add(new Rectangle(WIDTH, HEIGHT, Color.MIDNIGHTBLUE));\n\n        Sub sub = new Sub(WIDTH / 2, HEIGHT / 2);\n        pane.getChildren().add(sub.getShape());\n        for (int i = 0; i < 4; i++) {\n            Jellyfish j = new Jellyfish(random.nextInt(WIDTH), random.nextInt(60), random.nextInt(360));\n            jellyfish.add(j);\n            pane.getChildren().add(j.getShape());\n        }\n\n        Scene scene = new Scene(pane);\n        scene.setOnKeyPressed(event -> {\n            held.put(event.getCode(), true);\n            if (event.getCode() == KeyCode.SPACE && bubbles.size() < 4) {\n                Bubble bubble = new Bubble(sub.getShape().getTranslateX(), sub.getShape().getTranslateY(), sub.getShape().getRotate());\n                bubbles.add(bubble);\n                pane.getChildren().add(bubble.getShape());\n            }\n        });\n        scene.setOnKeyReleased(event -> held.put(event.getCode(), false));\n\n        new AnimationTimer() {\n            @Override\n            public void handle(long now) {\n                if (held.getOrDefault(KeyCode.LEFT, false)) {\n                    sub.turn(-4);\n                }\n                if (held.getOrDefault(KeyCode.RIGHT, false)) {\n                    sub.turn(4);\n                }\n                if (held.getOrDefault(KeyCode.UP, false)) {\n                    sub.accelerate(0.125);\n                }\n                sub.move();\n                jellyfish.forEach(j -> j.move());\n                bubbles.forEach(b -> b.move());\n\n                for (Bubble b : bubbles) {\n                    for (Jellyfish j : jellyfish) {\n                        if (b.collides(j)) {\n                            b.setAlive(false);\n                            j.setAlive(false);\n                            System.out.println(\"Pop!\");\n                        }\n                    }\n                }\n                bubbles.stream()\n                        .filter(b -> !b.isAlive())\n                        .forEach(b -> pane.getChildren().remove(b.getShape()));\n                jellyfish.stream()\n                        .filter(j -> !j.isAlive())\n                        .forEach(j -> pane.getChildren().remove(j.getShape()));\n                bubbles = bubbles.stream()\n                        .filter(b -> b.isAlive())\n                        .collect(Collectors.toList());\n                jellyfish = jellyfish.stream()\n                        .filter(j -> j.isAlive())\n                        .collect(Collectors.toList());\n\n                for (Jellyfish j : jellyfish) {\n                    if (sub.collides(j)) {\n                        sub.getShape().setFill(Color.RED);\n                        System.out.println(\"Bump!\");\n                        stop();\n                        break;\n                    }\n                }\n            }\n        }.start();\n\n        stage.setScene(scene);\n        stage.setTitle(\"Bubbles\");\n        stage.show();\n    }\n\n    public static double wrap(double value, double size) {\n        double result = value % size;\n        if (result < 0) {\n            result = result + size;\n        }\n        return result;\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== Mover.java ====\nimport javafx.geometry.Point2D;\nimport javafx.scene.shape.Shape;\n\npublic abstract class Mover {\n    private Shape shape;\n    private Point2D velocity;\n    private boolean alive;\n\n    public Mover(Shape shape, double x, double y) {\n        this.shape = shape;\n        this.shape.setTranslateX(x);\n        this.shape.setTranslateY(y);\n        this.velocity = new Point2D(0, 0);\n        this.alive = true;\n    }\n\n    public Shape getShape() {\n        return shape;\n    }\n\n    public boolean isAlive() {\n        return alive;\n    }\n\n    public void setAlive(boolean alive) {\n        this.alive = alive;\n    }\n\n    public void turn(double degrees) {\n        shape.setRotate(shape.getRotate() + degrees);\n    }\n\n    public void accelerate(double amount) {\n        double angle = Math.toRadians(shape.getRotate());\n        velocity = velocity.add(Math.cos(angle) * amount, Math.sin(angle) * amount);\n    }\n\n    public void move() {\n        shape.setTranslateX(Main.wrap(shape.getTranslateX() + velocity.getX(), Main.WIDTH));\n        shape.setTranslateY(Main.wrap(shape.getTranslateY() + velocity.getY(), Main.HEIGHT));\n    }\n\n    public boolean collides(Mover other) {\n        Shape overlap = Shape.intersect(shape, other.getShape());\n        return overlap.getBoundsInLocal().getWidth() != -1;\n    }\n}\n\n// ==== Sub.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Sub extends Mover {\n\n    public Sub(double x, double y) {\n        super(new Polygon(-15, -6, 9, -6, 15, 0, 9, 6, -15, 6), x, y);\n        getShape().setFill(Color.GOLD);\n    }\n}\n\n// ==== Jellyfish.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Jellyfish extends Mover {\n\n    public Jellyfish(double x, double y, double heading) {\n        super(new Polygon(-10, 0, -6, -8, 0, -10, 6, -8, 10, 0, 7, 10, 3, 2, 0, 10, -3, 2, -7, 10), x, y);\n        getShape().setFill(Color.ORCHID);\n        turn(heading);\n        accelerate(0.5);\n    }\n\n    @Override\n    public void move() {\n        super.move();\n        turn(1);\n    }\n}\n","solution":"// ==== Bubble.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Circle;\n\npublic class Bubble extends Mover {\n    private int age;\n\n    public Bubble(double x, double y, double heading) {\n        super(new Circle(3, Color.LIGHTCYAN), x, y);\n        turn(heading);\n        accelerate(2.5);\n        age = 0;\n    }\n\n    @Override\n    public void move() {\n        super.move();\n        age++;\n        if (age == 50) {\n            setAlive(false);\n        }\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport javafx.animation.AnimationTimer;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Rectangle;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n    public static final int WIDTH = 360;\n    public static final int HEIGHT = 240;\n\n    private Map<KeyCode, Boolean> held = new HashMap<>();\n    private Random random = new Random(16);\n    private List<Jellyfish> jellyfish = new ArrayList<>();\n    private List<Bubble> bubbles = new ArrayList<>();\n\n    @Override\n    public void start(Stage stage) {\n        Pane pane = new Pane();\n        pane.setPrefSize(WIDTH, HEIGHT);\n        pane.getChildren().add(new Rectangle(WIDTH, HEIGHT, Color.MIDNIGHTBLUE));\n\n        Sub sub = new Sub(WIDTH / 2, HEIGHT / 2);\n        pane.getChildren().add(sub.getShape());\n        for (int i = 0; i < 4; i++) {\n            Jellyfish j = new Jellyfish(random.nextInt(WIDTH), random.nextInt(60), random.nextInt(360));\n            jellyfish.add(j);\n            pane.getChildren().add(j.getShape());\n        }\n\n        Scene scene = new Scene(pane);\n        scene.setOnKeyPressed(event -> {\n            held.put(event.getCode(), true);\n            if (event.getCode() == KeyCode.SPACE && bubbles.size() < 4) {\n                Bubble bubble = new Bubble(sub.getShape().getTranslateX(), sub.getShape().getTranslateY(), sub.getShape().getRotate());\n                bubbles.add(bubble);\n                pane.getChildren().add(bubble.getShape());\n            }\n        });\n        scene.setOnKeyReleased(event -> held.put(event.getCode(), false));\n\n        new AnimationTimer() {\n            @Override\n            public void handle(long now) {\n                if (held.getOrDefault(KeyCode.LEFT, false)) {\n                    sub.turn(-4);\n                }\n                if (held.getOrDefault(KeyCode.RIGHT, false)) {\n                    sub.turn(4);\n                }\n                if (held.getOrDefault(KeyCode.UP, false)) {\n                    sub.accelerate(0.125);\n                }\n                sub.move();\n                jellyfish.forEach(j -> j.move());\n                bubbles.forEach(b -> b.move());\n\n                for (Bubble b : bubbles) {\n                    for (Jellyfish j : jellyfish) {\n                        if (b.collides(j)) {\n                            b.setAlive(false);\n                            j.setAlive(false);\n                            System.out.println(\"Pop!\");\n                        }\n                    }\n                }\n                bubbles.stream()\n                        .filter(b -> !b.isAlive())\n                        .forEach(b -> pane.getChildren().remove(b.getShape()));\n                jellyfish.stream()\n                        .filter(j -> !j.isAlive())\n                        .forEach(j -> pane.getChildren().remove(j.getShape()));\n                bubbles = bubbles.stream()\n                        .filter(b -> b.isAlive())\n                        .collect(Collectors.toList());\n                jellyfish = jellyfish.stream()\n                        .filter(j -> j.isAlive())\n                        .collect(Collectors.toList());\n\n                for (Jellyfish j : jellyfish) {\n                    if (sub.collides(j)) {\n                        sub.getShape().setFill(Color.RED);\n                        System.out.println(\"Bump!\");\n                        stop();\n                        break;\n                    }\n                }\n            }\n        }.start();\n\n        stage.setScene(scene);\n        stage.setTitle(\"Bubbles\");\n        stage.show();\n    }\n\n    public static double wrap(double value, double size) {\n        double result = value % size;\n        if (result < 0) {\n            result = result + size;\n        }\n        return result;\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== Mover.java ====\nimport javafx.geometry.Point2D;\nimport javafx.scene.shape.Shape;\n\npublic abstract class Mover {\n    private Shape shape;\n    private Point2D velocity;\n    private boolean alive;\n\n    public Mover(Shape shape, double x, double y) {\n        this.shape = shape;\n        this.shape.setTranslateX(x);\n        this.shape.setTranslateY(y);\n        this.velocity = new Point2D(0, 0);\n        this.alive = true;\n    }\n\n    public Shape getShape() {\n        return shape;\n    }\n\n    public boolean isAlive() {\n        return alive;\n    }\n\n    public void setAlive(boolean alive) {\n        this.alive = alive;\n    }\n\n    public void turn(double degrees) {\n        shape.setRotate(shape.getRotate() + degrees);\n    }\n\n    public void accelerate(double amount) {\n        double angle = Math.toRadians(shape.getRotate());\n        velocity = velocity.add(Math.cos(angle) * amount, Math.sin(angle) * amount);\n    }\n\n    public void move() {\n        shape.setTranslateX(Main.wrap(shape.getTranslateX() + velocity.getX(), Main.WIDTH));\n        shape.setTranslateY(Main.wrap(shape.getTranslateY() + velocity.getY(), Main.HEIGHT));\n    }\n\n    public boolean collides(Mover other) {\n        Shape overlap = Shape.intersect(shape, other.getShape());\n        return overlap.getBoundsInLocal().getWidth() != -1;\n    }\n}\n\n// ==== Sub.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Sub extends Mover {\n\n    public Sub(double x, double y) {\n        super(new Polygon(-15, -6, 9, -6, 15, 0, 9, 6, -15, 6), x, y);\n        getShape().setFill(Color.GOLD);\n    }\n}\n\n// ==== Jellyfish.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Jellyfish extends Mover {\n\n    public Jellyfish(double x, double y, double heading) {\n        super(new Polygon(-10, 0, -6, -8, 0, -10, 6, -8, 10, 0, 7, 10, 3, 2, 0, 10, -3, 2, -7, 10), x, y);\n        getShape().setFill(Color.ORCHID);\n        turn(heading);\n        accelerate(0.5);\n    }\n\n    @Override\n    public void move() {\n        super.move();\n        turn(1);\n    }\n}\n","hints":["Add `private int age;` to Bubble, and the override `@Override public void move() { super.move(); age++; ... }`, as Jellyfish overrides `move` in step 4.","After `age++`: `if (age == 50) { setAlive(false); }`. `setAlive` is Mover's method, which a Bubble has."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["key SPACE","frames 50"],"window":{"windows":[{"title":"Bubbles","scene":{"width":360,"height":240,"onKeyPressed":true,"onKeyReleased":true,"root":{"n":1,"type":"Pane","f":1,"prefWidth":360,"prefHeight":240,"children":[{"n":2,"type":"Rectangle","f":2,"x":0,"y":0,"width":360,"height":240,"fill":"#191970"},{"n":3,"type":"Polygon","f":3,"translateX":180,"translateY":120,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":237.47047612756353,"translateY":76.14814565722665,"rotate":125,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":5,"type":"Polygon","f":5,"translateX":226.82040363582985,"translateY":3.634835164602201,"rotate":252,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":6,"type":"Polygon","f":6,"translateX":276.9303097578352,"translateY":69.15111107797429,"rotate":180,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":7,"type":"Polygon","f":7,"translateX":178.49231551964834,"translateY":19.550503583141733,"rotate":70,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"}]}}}],"problems":[],"frames":{"count":50,"list":[]}},"outline":"Window \"Bubbles\"\n  Pane 360 x 240\n    Rectangle 360 x 240 at (0, 0), fill #191970\n    Polygon (-15, -6) (9, -6) (15, 0) (9, 6) (-15, 6), at (180, 120), fill #ffd700\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (237.47047612756353, 76.14814565722665), rotate 125, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (226.82040363582985, 3.634835164602201), rotate 252, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (276.9303097578352, 69.15111107797429), rotate 180, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (178.49231551964834, 19.550503583141733), rotate 70, fill #da70d6\nframes run: 50"},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":["key SPACE","frames 20","key SPACE","frames 30","key SPACE","key SPACE","key SPACE","key SPACE","frames 10"],"window":{"windows":[{"title":"Bubbles","scene":{"width":360,"height":240,"onKeyPressed":true,"onKeyReleased":true,"root":{"n":1,"type":"Pane","f":1,"prefWidth":360,"prefHeight":240,"children":[{"n":2,"type":"Rectangle","f":2,"x":0,"y":0,"width":360,"height":240,"fill":"#191970"},{"n":3,"type":"Polygon","f":3,"translateX":180,"translateY":120,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":238.76457135307624,"translateY":80.97777478867198,"rotate":135,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":5,"type":"Polygon","f":5,"translateX":222.18448436299582,"translateY":1.7618021975226412,"rotate":262,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":6,"type":"Polygon","f":6,"translateX":273.7163717094022,"translateY":72.98133329356915,"rotate":190,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":7,"type":"Polygon","f":7,"translateX":183.190778623578,"translateY":21.26060429977008,"rotate":80,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":8,"type":"Circle","f":9,"translateX":280,"translateY":120,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":9,"type":"Circle","f":10,"translateX":205,"translateY":120,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":10,"type":"Circle","f":11,"translateX":205,"translateY":120,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":11,"type":"Circle","f":12,"translateX":205,"translateY":120,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"}]}}}],"problems":[],"frames":{"count":60,"list":[]}},"outline":"Window \"Bubbles\"\n  Pane 360 x 240\n    Rectangle 360 x 240 at (0, 0), fill #191970\n    Polygon (-15, -6) (9, -6) (15, 0) (9, 6) (-15, 6), at (180, 120), fill #ffd700\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (238.76457135307624, 80.97777478867198), rotate 135, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (222.18448436299582, 1.7618021975226412), rotate 262, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (273.7163717094022, 72.98133329356915), rotate 190, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (183.190778623578, 21.26060429977008), rotate 80, fill #da70d6\n    Circle radius 3 at (0, 0), translate (280, 120), fill #e0ffff\n    Circle radius 3 at (0, 0), translate (205, 120), fill #e0ffff\n    Circle radius 3 at (0, 0), translate (205, 120), fill #e0ffff\n    Circle radius 3 at (0, 0), translate (205, 120), fill #e0ffff\nframes run: 60"},{"name":"Test 3","stdin":"","expect":"Pop!\nPop!","hidden":true,"events":["press key LEFT","frames 20","release key LEFT","key SPACE","frames 10","key SPACE","frames 10","key SPACE","frames 30","press key RIGHT","frames 30","release key RIGHT","key SPACE","frames 40"],"window":{"windows":[{"title":"Bubbles","scene":{"width":360,"height":240,"onKeyPressed":true,"onKeyReleased":true,"root":{"n":1,"type":"Pane","f":1,"prefWidth":360,"prefHeight":240,"children":[{"n":2,"type":"Rectangle","f":2,"x":0,"y":0,"width":360,"height":240,"fill":"#191970"},{"n":3,"type":"Polygon","f":3,"translateX":180,"translateY":120,"rotate":40,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":249.11733315717788,"translateY":119.61480784023462,"rotate":215,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":5,"type":"Polygon","f":6,"translateX":248.0048673219392,"translateY":103.62311101832802,"rotate":270,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":6,"type":"Circle","f":11,"translateX":256.6044443118983,"translateY":184.27876096865367,"rotate":40,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"}]}}}],"problems":[],"frames":{"count":140,"list":[]}},"outline":"Window \"Bubbles\"\n  Pane 360 x 240\n    Rectangle 360 x 240 at (0, 0), fill #191970\n    Polygon (-15, -6) (9, -6) (15, 0) (9, 6) (-15, 6), at (180, 120), rotate 40, fill #ffd700\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (249.11733315717788, 119.61480784023462), rotate 215, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (248.0048673219392, 103.62311101832802), rotate 270, fill #da70d6\n    Circle radius 3 at (0, 0), translate (256.6044443118983, 184.27876096865367), rotate 40, fill #e0ffff\nframes run: 140"}],"require":[],"forbid":[]},{"task":"Rapid fire: holding Space fires a bubble every 15 frames. Move the firing from the pressed handler into the game loop. Add a field `reload`, 0 at the start. At the very beginning of `handle`, before the keys that turn and push:\n\n1. if `reload` is more than 0, subtract 1 from it\n2. then, if Space is held, `reload` is 0 and fewer than 4 bubbles are out, fire a bubble as the handler did (from the submarine's place, in its heading, added to the list and the pane) and set `reload` to 15\n\nThe pressed handler then only puts `true` in the map. The test holds Space for 40 frames and runs 10 more: bubbles were fired in frames 1, 16 and 31.\n","kind":"code","seed":"// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport javafx.animation.AnimationTimer;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Rectangle;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n    public static final int WIDTH = 360;\n    public static final int HEIGHT = 240;\n\n    private Map<KeyCode, Boolean> held = new HashMap<>();\n    private Random random = new Random(16);\n    private List<Jellyfish> jellyfish = new ArrayList<>();\n    private List<Bubble> bubbles = new ArrayList<>();\n\n    @Override\n    public void start(Stage stage) {\n        Pane pane = new Pane();\n        pane.setPrefSize(WIDTH, HEIGHT);\n        pane.getChildren().add(new Rectangle(WIDTH, HEIGHT, Color.MIDNIGHTBLUE));\n\n        Sub sub = new Sub(WIDTH / 2, HEIGHT / 2);\n        pane.getChildren().add(sub.getShape());\n        for (int i = 0; i < 4; i++) {\n            Jellyfish j = new Jellyfish(random.nextInt(WIDTH), random.nextInt(60), random.nextInt(360));\n            jellyfish.add(j);\n            pane.getChildren().add(j.getShape());\n        }\n\n        Scene scene = new Scene(pane);\n        scene.setOnKeyPressed(event -> {\n            held.put(event.getCode(), true);\n            if (event.getCode() == KeyCode.SPACE && bubbles.size() < 4) {\n                Bubble bubble = new Bubble(sub.getShape().getTranslateX(), sub.getShape().getTranslateY(), sub.getShape().getRotate());\n                bubbles.add(bubble);\n                pane.getChildren().add(bubble.getShape());\n            }\n        });\n        scene.setOnKeyReleased(event -> held.put(event.getCode(), false));\n\n        new AnimationTimer() {\n            @Override\n            public void handle(long now) {\n                if (held.getOrDefault(KeyCode.LEFT, false)) {\n                    sub.turn(-4);\n                }\n                if (held.getOrDefault(KeyCode.RIGHT, false)) {\n                    sub.turn(4);\n                }\n                if (held.getOrDefault(KeyCode.UP, false)) {\n                    sub.accelerate(0.125);\n                }\n                sub.move();\n                jellyfish.forEach(j -> j.move());\n                bubbles.forEach(b -> b.move());\n\n                for (Bubble b : bubbles) {\n                    for (Jellyfish j : jellyfish) {\n                        if (b.collides(j)) {\n                            b.setAlive(false);\n                            j.setAlive(false);\n                            System.out.println(\"Pop!\");\n                        }\n                    }\n                }\n                bubbles.stream()\n                        .filter(b -> !b.isAlive())\n                        .forEach(b -> pane.getChildren().remove(b.getShape()));\n                jellyfish.stream()\n                        .filter(j -> !j.isAlive())\n                        .forEach(j -> pane.getChildren().remove(j.getShape()));\n                bubbles = bubbles.stream()\n                        .filter(b -> b.isAlive())\n                        .collect(Collectors.toList());\n                jellyfish = jellyfish.stream()\n                        .filter(j -> j.isAlive())\n                        .collect(Collectors.toList());\n\n                for (Jellyfish j : jellyfish) {\n                    if (sub.collides(j)) {\n                        sub.getShape().setFill(Color.RED);\n                        System.out.println(\"Bump!\");\n                        stop();\n                        break;\n                    }\n                }\n            }\n        }.start();\n\n        stage.setScene(scene);\n        stage.setTitle(\"Rapid fire\");\n        stage.show();\n    }\n\n    public static double wrap(double value, double size) {\n        double result = value % size;\n        if (result < 0) {\n            result = result + size;\n        }\n        return result;\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== Mover.java ====\nimport javafx.geometry.Point2D;\nimport javafx.scene.shape.Shape;\n\npublic abstract class Mover {\n    private Shape shape;\n    private Point2D velocity;\n    private boolean alive;\n\n    public Mover(Shape shape, double x, double y) {\n        this.shape = shape;\n        this.shape.setTranslateX(x);\n        this.shape.setTranslateY(y);\n        this.velocity = new Point2D(0, 0);\n        this.alive = true;\n    }\n\n    public Shape getShape() {\n        return shape;\n    }\n\n    public boolean isAlive() {\n        return alive;\n    }\n\n    public void setAlive(boolean alive) {\n        this.alive = alive;\n    }\n\n    public void turn(double degrees) {\n        shape.setRotate(shape.getRotate() + degrees);\n    }\n\n    public void accelerate(double amount) {\n        double angle = Math.toRadians(shape.getRotate());\n        velocity = velocity.add(Math.cos(angle) * amount, Math.sin(angle) * amount);\n    }\n\n    public void move() {\n        shape.setTranslateX(Main.wrap(shape.getTranslateX() + velocity.getX(), Main.WIDTH));\n        shape.setTranslateY(Main.wrap(shape.getTranslateY() + velocity.getY(), Main.HEIGHT));\n    }\n\n    public boolean collides(Mover other) {\n        Shape overlap = Shape.intersect(shape, other.getShape());\n        return overlap.getBoundsInLocal().getWidth() != -1;\n    }\n}\n\n// ==== Sub.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Sub extends Mover {\n\n    public Sub(double x, double y) {\n        super(new Polygon(-15, -6, 9, -6, 15, 0, 9, 6, -15, 6), x, y);\n        getShape().setFill(Color.GOLD);\n    }\n}\n\n// ==== Jellyfish.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Jellyfish extends Mover {\n\n    public Jellyfish(double x, double y, double heading) {\n        super(new Polygon(-10, 0, -6, -8, 0, -10, 6, -8, 10, 0, 7, 10, 3, 2, 0, 10, -3, 2, -7, 10), x, y);\n        getShape().setFill(Color.ORCHID);\n        turn(heading);\n        accelerate(0.5);\n    }\n\n    @Override\n    public void move() {\n        super.move();\n        turn(1);\n    }\n}\n\n// ==== Bubble.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Circle;\n\npublic class Bubble extends Mover {\n\n    public Bubble(double x, double y, double heading) {\n        super(new Circle(3, Color.LIGHTCYAN), x, y);\n        turn(heading);\n        accelerate(2.5);\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport javafx.animation.AnimationTimer;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.input.KeyCode;\nimport javafx.scene.layout.Pane;\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Rectangle;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n    public static final int WIDTH = 360;\n    public static final int HEIGHT = 240;\n\n    private Map<KeyCode, Boolean> held = new HashMap<>();\n    private Random random = new Random(16);\n    private List<Jellyfish> jellyfish = new ArrayList<>();\n    private List<Bubble> bubbles = new ArrayList<>();\n    private int reload = 0;\n\n    @Override\n    public void start(Stage stage) {\n        Pane pane = new Pane();\n        pane.setPrefSize(WIDTH, HEIGHT);\n        pane.getChildren().add(new Rectangle(WIDTH, HEIGHT, Color.MIDNIGHTBLUE));\n\n        Sub sub = new Sub(WIDTH / 2, HEIGHT / 2);\n        pane.getChildren().add(sub.getShape());\n        for (int i = 0; i < 4; i++) {\n            Jellyfish j = new Jellyfish(random.nextInt(WIDTH), random.nextInt(60), random.nextInt(360));\n            jellyfish.add(j);\n            pane.getChildren().add(j.getShape());\n        }\n\n        Scene scene = new Scene(pane);\n        scene.setOnKeyPressed(event -> held.put(event.getCode(), true));\n        scene.setOnKeyReleased(event -> held.put(event.getCode(), false));\n\n        new AnimationTimer() {\n            @Override\n            public void handle(long now) {\n                if (reload > 0) {\n                    reload--;\n                }\n                if (held.getOrDefault(KeyCode.SPACE, false) && reload == 0 && bubbles.size() < 4) {\n                    Bubble bubble = new Bubble(sub.getShape().getTranslateX(), sub.getShape().getTranslateY(), sub.getShape().getRotate());\n                    bubbles.add(bubble);\n                    pane.getChildren().add(bubble.getShape());\n                    reload = 15;\n                }\n                if (held.getOrDefault(KeyCode.LEFT, false)) {\n                    sub.turn(-4);\n                }\n                if (held.getOrDefault(KeyCode.RIGHT, false)) {\n                    sub.turn(4);\n                }\n                if (held.getOrDefault(KeyCode.UP, false)) {\n                    sub.accelerate(0.125);\n                }\n                sub.move();\n                jellyfish.forEach(j -> j.move());\n                bubbles.forEach(b -> b.move());\n\n                for (Bubble b : bubbles) {\n                    for (Jellyfish j : jellyfish) {\n                        if (b.collides(j)) {\n                            b.setAlive(false);\n                            j.setAlive(false);\n                            System.out.println(\"Pop!\");\n                        }\n                    }\n                }\n                bubbles.stream()\n                        .filter(b -> !b.isAlive())\n                        .forEach(b -> pane.getChildren().remove(b.getShape()));\n                jellyfish.stream()\n                        .filter(j -> !j.isAlive())\n                        .forEach(j -> pane.getChildren().remove(j.getShape()));\n                bubbles = bubbles.stream()\n                        .filter(b -> b.isAlive())\n                        .collect(Collectors.toList());\n                jellyfish = jellyfish.stream()\n                        .filter(j -> j.isAlive())\n                        .collect(Collectors.toList());\n\n                for (Jellyfish j : jellyfish) {\n                    if (sub.collides(j)) {\n                        sub.getShape().setFill(Color.RED);\n                        System.out.println(\"Bump!\");\n                        stop();\n                        break;\n                    }\n                }\n            }\n        }.start();\n\n        stage.setScene(scene);\n        stage.setTitle(\"Rapid fire\");\n        stage.show();\n    }\n\n    public static double wrap(double value, double size) {\n        double result = value % size;\n        if (result < 0) {\n            result = result + size;\n        }\n        return result;\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== Mover.java ====\nimport javafx.geometry.Point2D;\nimport javafx.scene.shape.Shape;\n\npublic abstract class Mover {\n    private Shape shape;\n    private Point2D velocity;\n    private boolean alive;\n\n    public Mover(Shape shape, double x, double y) {\n        this.shape = shape;\n        this.shape.setTranslateX(x);\n        this.shape.setTranslateY(y);\n        this.velocity = new Point2D(0, 0);\n        this.alive = true;\n    }\n\n    public Shape getShape() {\n        return shape;\n    }\n\n    public boolean isAlive() {\n        return alive;\n    }\n\n    public void setAlive(boolean alive) {\n        this.alive = alive;\n    }\n\n    public void turn(double degrees) {\n        shape.setRotate(shape.getRotate() + degrees);\n    }\n\n    public void accelerate(double amount) {\n        double angle = Math.toRadians(shape.getRotate());\n        velocity = velocity.add(Math.cos(angle) * amount, Math.sin(angle) * amount);\n    }\n\n    public void move() {\n        shape.setTranslateX(Main.wrap(shape.getTranslateX() + velocity.getX(), Main.WIDTH));\n        shape.setTranslateY(Main.wrap(shape.getTranslateY() + velocity.getY(), Main.HEIGHT));\n    }\n\n    public boolean collides(Mover other) {\n        Shape overlap = Shape.intersect(shape, other.getShape());\n        return overlap.getBoundsInLocal().getWidth() != -1;\n    }\n}\n\n// ==== Sub.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Sub extends Mover {\n\n    public Sub(double x, double y) {\n        super(new Polygon(-15, -6, 9, -6, 15, 0, 9, 6, -15, 6), x, y);\n        getShape().setFill(Color.GOLD);\n    }\n}\n\n// ==== Jellyfish.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Polygon;\n\npublic class Jellyfish extends Mover {\n\n    public Jellyfish(double x, double y, double heading) {\n        super(new Polygon(-10, 0, -6, -8, 0, -10, 6, -8, 10, 0, 7, 10, 3, 2, 0, 10, -3, 2, -7, 10), x, y);\n        getShape().setFill(Color.ORCHID);\n        turn(heading);\n        accelerate(0.5);\n    }\n\n    @Override\n    public void move() {\n        super.move();\n        turn(1);\n    }\n}\n\n// ==== Bubble.java ====\nimport javafx.scene.paint.Color;\nimport javafx.scene.shape.Circle;\n\npublic class Bubble extends Mover {\n\n    public Bubble(double x, double y, double heading) {\n        super(new Circle(3, Color.LIGHTCYAN), x, y);\n        turn(heading);\n        accelerate(2.5);\n    }\n}\n","hints":["The pressed handler becomes `scene.setOnKeyPressed(event -> held.put(event.getCode(), true));`. The field is `private int reload = 0;`.","At the start of `handle`: `if (reload > 0) { reload--; }`, then `if (held.getOrDefault(KeyCode.SPACE, false) && reload == 0 && bubbles.size() < 4) { ... reload = 15; }` with the three lines that make and add the bubble."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["press key SPACE","frames 40","release key SPACE","frames 10"],"window":{"windows":[{"title":"Rapid fire","scene":{"width":360,"height":240,"onKeyPressed":true,"onKeyReleased":true,"root":{"n":1,"type":"Pane","f":1,"prefWidth":360,"prefHeight":240,"children":[{"n":2,"type":"Rectangle","f":2,"x":0,"y":0,"width":360,"height":240,"fill":"#191970"},{"n":3,"type":"Polygon","f":3,"translateX":180,"translateY":120,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":237.47047612756353,"translateY":76.14814565722665,"rotate":125,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":5,"type":"Polygon","f":5,"translateX":226.82040363582985,"translateY":3.634835164602201,"rotate":252,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":6,"type":"Polygon","f":6,"translateX":276.9303097578352,"translateY":69.15111107797429,"rotate":180,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":7,"type":"Polygon","f":7,"translateX":178.49231551964834,"translateY":19.550503583141733,"rotate":70,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":8,"type":"Circle","f":8,"translateX":305,"translateY":120,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":9,"type":"Circle","f":9,"translateX":267.5,"translateY":120,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":10,"type":"Circle","f":10,"translateX":230,"translateY":120,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"}]}}}],"problems":[],"frames":{"count":50,"list":[]}},"outline":"Window \"Rapid fire\"\n  Pane 360 x 240\n    Rectangle 360 x 240 at (0, 0), fill #191970\n    Polygon (-15, -6) (9, -6) (15, 0) (9, 6) (-15, 6), at (180, 120), fill #ffd700\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (237.47047612756353, 76.14814565722665), rotate 125, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (226.82040363582985, 3.634835164602201), rotate 252, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (276.9303097578352, 69.15111107797429), rotate 180, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (178.49231551964834, 19.550503583141733), rotate 70, fill #da70d6\n    Circle radius 3 at (0, 0), translate (305, 120), fill #e0ffff\n    Circle radius 3 at (0, 0), translate (267.5, 120), fill #e0ffff\n    Circle radius 3 at (0, 0), translate (230, 120), fill #e0ffff\nframes run: 50"},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":["press key RIGHT","frames 10","press key SPACE","frames 20","release key SPACE","release key RIGHT","frames 3","press key SPACE","frames 2","release key SPACE","frames 30"],"window":{"windows":[{"title":"Rapid fire","scene":{"width":360,"height":240,"onKeyPressed":true,"onKeyReleased":true,"root":{"n":1,"type":"Pane","f":1,"prefWidth":360,"prefHeight":240,"children":[{"n":2,"type":"Rectangle","f":2,"x":0,"y":0,"width":360,"height":240,"fill":"#191970"},{"n":3,"type":"Polygon","f":3,"translateX":180,"translateY":120,"rotate":120,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":239.4116189658326,"translateY":83.39258935439464,"rotate":140,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":5,"type":"Polygon","f":5,"translateX":219.8665247265788,"translateY":0.8252857139828613,"rotate":267,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":6,"type":"Polygon","f":6,"translateX":272.1094026851857,"translateY":74.89644440136658,"rotate":195,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":7,"type":"Polygon","f":7,"translateX":185.54001017554285,"translateY":22.115654658084253,"rotate":85,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":8,"type":"Circle","f":8,"translateX":285.33111092885974,"translateY":208.38329633189878,"rotate":40,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":9,"type":"Circle","f":9,"translateX":162.6351822333072,"translateY":218.48077530122055,"rotate":100,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"}]}}}],"problems":[],"frames":{"count":65,"list":[]}},"outline":"Window \"Rapid fire\"\n  Pane 360 x 240\n    Rectangle 360 x 240 at (0, 0), fill #191970\n    Polygon (-15, -6) (9, -6) (15, 0) (9, 6) (-15, 6), at (180, 120), rotate 120, fill #ffd700\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (239.4116189658326, 83.39258935439464), rotate 140, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (219.8665247265788, 0.8252857139828613), rotate 267, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (272.1094026851857, 74.89644440136658), rotate 195, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (185.54001017554285, 22.115654658084253), rotate 85, fill #da70d6\n    Circle radius 3 at (0, 0), translate (285.33111092885974, 208.38329633189878), rotate 40, fill #e0ffff\n    Circle radius 3 at (0, 0), translate (162.6351822333072, 218.48077530122055), rotate 100, fill #e0ffff\nframes run: 65"},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["key SPACE","frames 20","press key LEFT","press key SPACE","frames 90"],"window":{"windows":[{"title":"Rapid fire","scene":{"width":360,"height":240,"onKeyPressed":true,"onKeyReleased":true,"root":{"n":1,"type":"Pane","f":1,"prefWidth":360,"prefHeight":240,"children":[{"n":2,"type":"Rectangle","f":2,"x":0,"y":0,"width":360,"height":240,"fill":"#191970"},{"n":3,"type":"Polygon","f":3,"translateX":180,"translateY":120,"rotate":-360,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":245.23504748063976,"translateY":105.12592044589863,"rotate":185,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":5,"type":"Polygon","f":5,"translateX":199.00488799882567,"translateY":232.3966373621244,"rotate":312,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":6,"type":"Polygon","f":6,"translateX":257.6466814672374,"translateY":92.13244437154344,"rotate":240,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":7,"type":"Polygon","f":7,"translateX":206.68309414322636,"translateY":29.811107882911813,"rotate":130,"points":[-10,0,-6,-8,0,-10,6,-8,10,0,7,10,3,2,0,10,-3,2,-7,10],"fill":"#da70d6"},{"n":8,"type":"Circle","f":8,"translateX":45,"translateY":120,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":9,"type":"Circle","f":9,"translateX":273.75,"translateY":197.62023679041766,"rotate":-60,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":10,"type":"Circle","f":10,"translateX":105,"translateY":230.09618943233426,"rotate":-120,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"},{"n":11,"type":"Circle","f":11,"translateX":67.5,"translateY":120,"rotate":-180,"centerX":0,"centerY":0,"radius":3,"fill":"#e0ffff"}]}}}],"problems":[],"frames":{"count":110,"list":[]}},"outline":"Window \"Rapid fire\"\n  Pane 360 x 240\n    Rectangle 360 x 240 at (0, 0), fill #191970\n    Polygon (-15, -6) (9, -6) (15, 0) (9, 6) (-15, 6), at (180, 120), rotate -360, fill #ffd700\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (245.23504748063976, 105.12592044589863), rotate 185, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (199.00488799882567, 232.3966373621244), rotate 312, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (257.6466814672374, 92.13244437154344), rotate 240, fill #da70d6\n    Polygon (-10, 0) (-6, -8) (0, -10) (6, -8) (10, 0) (7, 10) (3, 2) (0, 10) (-3, 2) (-7, 10), at (206.68309414322636, 29.811107882911813), rotate 130, fill #da70d6\n    Circle radius 3 at (0, 0), translate (45, 120), fill #e0ffff\n    Circle radius 3 at (0, 0), translate (273.75, 197.62023679041766), rotate -60, fill #e0ffff\n    Circle radius 3 at (0, 0), translate (105, 230.09618943233426), rotate -120, fill #e0ffff\n    Circle radius 3 at (0, 0), translate (67.5, 120), rotate -180, fill #e0ffff\nframes run: 110"}],"require":[],"forbid":[]}]}