{"id":"asteroids-edges","slug":"edges","title":"Around the edges of the sea","text":"So far, the submarine and the jellyfish drift out of the window and never come back. In games like this one, the edges **wrap around**: something that leaves on the right comes back on the left, and something that sinks out at the bottom comes back at the top.\n\n### The window's size, in one place\n\nWrapping needs the window's width and height, and not only `Main` needs them: Mover's `move` does too. So they become **constants** of `Main`: variables that belong to the class itself rather than to an object (`static`, like the static methods of the first modules) and that can never change (`final`: assigning to one doesn't compile). By custom, a constant's name is in capital letters, like an enum's constants:\n\n```java\npublic class Main extends Application {\n    public static final int WIDTH = 360;\n    public static final int HEIGHT = 240;\n    ...\n        pane.setPrefSize(WIDTH, HEIGHT);\n```\n\nAny class can now read `Main.WIDTH` and `Main.HEIGHT`, and a bigger sea is a change in one place.\n\n### Wrapping a number\n\nWrapping x into the width 360 means: 370 becomes 10, -10 becomes 350, and 100 stays 100. The remainder operator `%` does most of it, but in Java a remainder has the **sign of the number divided**, so a negative x gives a negative remainder:\n\n```java\nSystem.out.println(370 % 360);\nSystem.out.println(-10 % 360);\nSystem.out.println(-10.5 % 360);\nSystem.out.println(-10.5 % 360 + 360);\nSystem.out.println(370.5 % 360);\n```\n\n```output\n10\n-10\n-10.5\n349.5\n10.5\n```\n\n`%` works for doubles too. `value % size` is between `-size` and `size`: a value that was positive gives a remainder from 0 up to `size`, which is already right, and only a negative remainder needs `size` added to it. So this method wraps a value, positive or negative, into the sea (a value already inside stays exactly as it is):\n\n```java\npublic 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\nFor whole numbers Java has `Math.floorMod(-10, 360)`, which gives 350; it takes `int` and `long` values, and a game's positions are doubles, so `wrap` does it by hand.\n\n### Wrapping in move\n\nEvery Mover moves in `move`, so that's the one place to wrap, and the submarine, the jellyfish and later the bubbles all wrap at once:\n\n```java\npublic 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\n```java\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\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\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(180, 120);\n        pane.getChildren().add(sub.getShape());\n        List<Jellyfish> jellyfish = new ArrayList<>();\n        jellyfish.add(new Jellyfish(30, 200, 135));\n        jellyfish.add(new Jellyfish(330, 30, -60));\n        jellyfish.forEach(j -> pane.getChildren().add(j.getShape()));\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 (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            }\n        }.start();\n\n        stage.setScene(scene);\n        stage.setTitle(\"Around the edges\");\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\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    }\n\n    public Shape getShape() {\n        return shape;\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\n```window\n{\"events\":[\"press key LEFT\",\"frames 45\",\"release key LEFT\",\"press key UP\",\"frames 24\",\"release key UP\",\"frames 60\"],\"window\":{\"windows\":[{\"title\":\"Around the edges\",\"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\":322.5,\"translateY\":120,\"rotate\":-180,\"points\":[-15,-6,9,-6,15,0,9,6,-15,6],\"fill\":\"#ffd700\"},{\"n\":4,\"type\":\"Polygon\",\"f\":4,\"translateX\":344.3916126134676,\"translateY\":5.608387386532643,\"rotate\":264,\"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\":2.2500000000000004,\"translateY\":214.14136145590425,\"rotate\":69,\"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\":129,\"list\":[[{\"roots\":[1]},{\"k\":1,\"type\":\"Pane\",\"children\":[2,3,4,5]},{\"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\":29.65,\"ty\":200.35,\"r\":136,\"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\":330.25,\"ty\":29.57,\"r\":-59,\"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\":29.29,\"ty\":200.71,\"r\":137},{\"k\":5,\"tx\":330.5,\"ty\":29.13,\"r\":-58}],[{\"k\":3,\"r\":-12},{\"k\":4,\"tx\":28.94,\"ty\":201.06,\"r\":138},{\"k\":5,\"tx\":330.75,\"ty\":28.7,\"r\":-57}],[{\"k\":3,\"r\":-16},{\"k\":4,\"tx\":28.59,\"ty\":201.41,\"r\":139},{\"k\":5,\"tx\":331,\"ty\":28.27,\"r\":-56}],[{\"k\":3,\"r\":-20},{\"k\":4,\"tx\":28.23,\"ty\":201.77,\"r\":140},{\"k\":5,\"tx\":331.25,\"ty\":27.83,\"r\":-55}],[{\"k\":3,\"r\":-24},{\"k\":4,\"tx\":27.88,\"ty\":202.12,\"r\":141},{\"k\":5,\"tx\":331.5,\"ty\":27.4,\"r\":-54}],[{\"k\":3,\"r\":-28},{\"k\":4,\"tx\":27.53,\"ty\":202.47,\"r\":142},{\"k\":5,\"tx\":331.75,\"ty\":26.97,\"r\":-53}],[{\"k\":3,\"r\":-32},{\"k\":4,\"tx\":27.17,\"ty\":202.83,\"r\":143},{\"k\":5,\"tx\":332,\"ty\":26.54,\"r\":-52}],[{\"k\":3,\"r\":-36},{\"k\":4,\"tx\":26.82,\"ty\":203.18,\"r\":144},{\"k\":5,\"tx\":332.25,\"ty\":26.1,\"r\":-51}],[{\"k\":3,\"r\":-40},{\"k\":4,\"tx\":26.46,\"ty\":203.54,\"r\":145},{\"k\":5,\"tx\":332.5,\"ty\":25.67,\"r\":-50}],[{\"k\":3,\"r\":-44},{\"k\":4,\"tx\":26.11,\"ty\":203.89,\"r\":146},{\"k\":5,\"tx\":332.75,\"ty\":25.24,\"r\":-49}],[{\"k\":3,\"r\":-48},{\"k\":4,\"tx\":25.76,\"ty\":204.24,\"r\":147},{\"k\":5,\"tx\":333,\"ty\":24.8,\"r\":-48}],[{\"k\":3,\"r\":-52},{\"k\":4,\"tx\":25.4,\"ty\":204.6,\"r\":148},{\"k\":5,\"tx\":333.25,\"ty\":24.37,\"r\":-47}],[{\"k\":3,\"r\":-56},{\"k\":4,\"tx\":25.05,\"ty\":204.95,\"r\":149},{\"k\":5,\"tx\":333.5,\"ty\":23.94,\"r\":-46}],[{\"k\":3,\"r\":-60},{\"k\":4,\"tx\":24.7,\"ty\":205.3,\"r\":150},{\"k\":5,\"tx\":333.75,\"ty\":23.5,\"r\":-45}],[{\"k\":3,\"r\":-64},{\"k\":4,\"tx\":24.34,\"ty\":205.66,\"r\":151},{\"k\":5,\"tx\":334,\"ty\":23.07,\"r\":-44}],[{\"k\":3,\"r\":-68},{\"k\":4,\"tx\":23.99,\"ty\":206.01,\"r\":152},{\"k\":5,\"tx\":334.25,\"ty\":22.64,\"r\":-43}],[{\"k\":3,\"r\":-72},{\"k\":4,\"tx\":23.64,\"ty\":206.36,\"r\":153},{\"k\":5,\"tx\":334.5,\"ty\":22.21,\"r\":-42}],[{\"k\":3,\"r\":-76},{\"k\":4,\"tx\":23.28,\"ty\":206.72,\"r\":154},{\"k\":5,\"tx\":334.75,\"ty\":21.77,\"r\":-41}],[{\"k\":3,\"r\":-80},{\"k\":4,\"tx\":22.93,\"ty\":207.07,\"r\":155},{\"k\":5,\"tx\":335,\"ty\":21.34,\"r\":-40}],[{\"k\":3,\"r\":-84},{\"k\":4,\"tx\":22.58,\"ty\":207.42,\"r\":156},{\"k\":5,\"tx\":335.25,\"ty\":20.91,\"r\":-39}],[{\"k\":3,\"r\":-88},{\"k\":4,\"tx\":22.22,\"ty\":207.78,\"r\":157},{\"k\":5,\"tx\":335.5,\"ty\":20.47,\"r\":-38}],[{\"k\":3,\"r\":-92},{\"k\":4,\"tx\":21.87,\"ty\":208.13,\"r\":158},{\"k\":5,\"tx\":335.75,\"ty\":20.04,\"r\":-37}],[{\"k\":3,\"r\":-96},{\"k\":4,\"tx\":21.51,\"ty\":208.49,\"r\":159},{\"k\":5,\"tx\":336,\"ty\":19.61,\"r\":-36}],[{\"k\":3,\"r\":-100},{\"k\":4,\"tx\":21.16,\"ty\":208.84,\"r\":160},{\"k\":5,\"tx\":336.25,\"ty\":19.17,\"r\":-35}],[{\"k\":3,\"r\":-104},{\"k\":4,\"tx\":20.81,\"ty\":209.19,\"r\":161},{\"k\":5,\"tx\":336.5,\"ty\":18.74,\"r\":-34}],[{\"k\":3,\"r\":-108},{\"k\":4,\"tx\":20.45,\"ty\":209.55,\"r\":162},{\"k\":5,\"tx\":336.75,\"ty\":18.31,\"r\":-33}],[{\"k\":3,\"r\":-112},{\"k\":4,\"tx\":20.1,\"ty\":209.9,\"r\":163},{\"k\":5,\"tx\":337,\"ty\":17.88,\"r\":-32}],[{\"k\":3,\"r\":-116},{\"k\":4,\"tx\":19.75,\"ty\":210.25,\"r\":164},{\"k\":5,\"tx\":337.25,\"ty\":17.44,\"r\":-31}],[{\"k\":3,\"r\":-120},{\"k\":4,\"tx\":19.39,\"ty\":210.61,\"r\":165},{\"k\":5,\"tx\":337.5,\"ty\":17.01,\"r\":-30}],[{\"k\":3,\"r\":-124},{\"k\":4,\"tx\":19.04,\"ty\":210.96,\"r\":166},{\"k\":5,\"tx\":337.75,\"ty\":16.58,\"r\":-29}],[{\"k\":3,\"r\":-128},{\"k\":4,\"tx\":18.69,\"ty\":211.31,\"r\":167},{\"k\":5,\"tx\":338,\"ty\":16.14,\"r\":-28}],[{\"k\":3,\"r\":-132},{\"k\":4,\"tx\":18.33,\"ty\":211.67,\"r\":168},{\"k\":5,\"tx\":338.25,\"ty\":15.71,\"r\":-27}],[{\"k\":3,\"r\":-136},{\"k\":4,\"tx\":17.98,\"ty\":212.02,\"r\":169},{\"k\":5,\"tx\":338.5,\"ty\":15.28,\"r\":-26}],[{\"k\":3,\"r\":-140},{\"k\":4,\"tx\":17.63,\"ty\":212.37,\"r\":170},{\"k\":5,\"tx\":338.75,\"ty\":14.84,\"r\":-25}],[{\"k\":3,\"r\":-144},{\"k\":4,\"tx\":17.27,\"ty\":212.73,\"r\":171},{\"k\":5,\"tx\":339,\"ty\":14.41,\"r\":-24}],[{\"k\":3,\"r\":-148},{\"k\":4,\"tx\":16.92,\"ty\":213.08,\"r\":172},{\"k\":5,\"tx\":339.25,\"ty\":13.98,\"r\":-23}],[{\"k\":3,\"r\":-152},{\"k\":4,\"tx\":16.56,\"ty\":213.44,\"r\":173},{\"k\":5,\"tx\":339.5,\"ty\":13.55,\"r\":-22}],[{\"k\":3,\"r\":-156},{\"k\":4,\"tx\":16.21,\"ty\":213.79,\"r\":174},{\"k\":5,\"tx\":339.75,\"ty\":13.11,\"r\":-21}],[{\"k\":3,\"r\":-160},{\"k\":4,\"tx\":15.86,\"ty\":214.14,\"r\":175},{\"k\":5,\"tx\":340,\"ty\":12.68,\"r\":-20}],[{\"k\":3,\"r\":-164},{\"k\":4,\"tx\":15.5,\"ty\":214.5,\"r\":176},{\"k\":5,\"tx\":340.25,\"ty\":12.25,\"r\":-19}],[{\"k\":3,\"r\":-168},{\"k\":4,\"tx\":15.15,\"ty\":214.85,\"r\":177},{\"k\":5,\"tx\":340.5,\"ty\":11.81,\"r\":-18}],[{\"k\":3,\"r\":-172},{\"k\":4,\"tx\":14.8,\"ty\":215.2,\"r\":178},{\"k\":5,\"tx\":340.75,\"ty\":11.38,\"r\":-17}],[{\"k\":3,\"r\":-176},{\"k\":4,\"tx\":14.44,\"ty\":215.56,\"r\":179},{\"k\":5,\"tx\":341,\"ty\":10.95,\"r\":-16}],[{\"k\":3,\"r\":-180},{\"k\":4,\"tx\":14.09,\"ty\":215.91,\"r\":180},{\"k\":5,\"tx\":341.25,\"ty\":10.51,\"r\":-15}],[{\"k\":3,\"tx\":179.88},{\"k\":4,\"tx\":13.74,\"ty\":216.26,\"r\":181},{\"k\":5,\"tx\":341.5,\"ty\":10.08,\"r\":-14}],[{\"k\":3,\"tx\":179.63},{\"k\":4,\"tx\":13.38,\"ty\":216.62,\"r\":182},{\"k\":5,\"tx\":341.75,\"ty\":9.65,\"r\":-13}],[{\"k\":3,\"tx\":179.25},{\"k\":4,\"tx\":13.03,\"ty\":216.97,\"r\":183},{\"k\":5,\"tx\":342,\"ty\":9.22,\"r\":-12}],[{\"k\":3,\"tx\":178.75},{\"k\":4,\"tx\":12.68,\"ty\":217.32,\"r\":184},{\"k\":5,\"tx\":342.25,\"ty\":8.78,\"r\":-11}],[{\"k\":3,\"tx\":178.13},{\"k\":4,\"tx\":12.32,\"ty\":217.68,\"r\":185},{\"k\":5,\"tx\":342.5,\"ty\":8.35,\"r\":-10}],[{\"k\":3,\"tx\":177.38},{\"k\":4,\"tx\":11.97,\"ty\":218.03,\"r\":186},{\"k\":5,\"tx\":342.75,\"ty\":7.92,\"r\":-9}],[{\"k\":3,\"tx\":176.5},{\"k\":4,\"tx\":11.62,\"ty\":218.38,\"r\":187},{\"k\":5,\"tx\":343,\"ty\":7.48,\"r\":-8}],[{\"k\":3,\"tx\":175.5},{\"k\":4,\"tx\":11.26,\"ty\":218.74,\"r\":188},{\"k\":5,\"tx\":343.25,\"ty\":7.05,\"r\":-7}],[{\"k\":3,\"tx\":174.38},{\"k\":4,\"tx\":10.91,\"ty\":219.09,\"r\":189},{\"k\":5,\"tx\":343.5,\"ty\":6.62,\"r\":-6}],[{\"k\":3,\"tx\":173.13},{\"k\":4,\"tx\":10.55,\"ty\":219.45,\"r\":190},{\"k\":5,\"tx\":343.75,\"ty\":6.18,\"r\":-5}],[{\"k\":3,\"tx\":171.75},{\"k\":4,\"tx\":10.2,\"ty\":219.8,\"r\":191},{\"k\":5,\"tx\":344,\"ty\":5.75,\"r\":-4}],[{\"k\":3,\"tx\":170.25},{\"k\":4,\"tx\":9.85,\"ty\":220.15,\"r\":192},{\"k\":5,\"tx\":344.25,\"ty\":5.32,\"r\":-3}],[{\"k\":3,\"tx\":168.63},{\"k\":4,\"tx\":9.49,\"ty\":220.51,\"r\":193},{\"k\":5,\"tx\":344.5,\"ty\":4.89,\"r\":-2}],[{\"k\":3,\"tx\":166.88},{\"k\":4,\"tx\":9.14,\"ty\":220.86,\"r\":194},{\"k\":5,\"tx\":344.75,\"ty\":4.45,\"r\":-1}],[{\"k\":3,\"tx\":165},{\"k\":4,\"tx\":8.79,\"ty\":221.21,\"r\":195},{\"k\":5,\"tx\":345,\"ty\":4.02,\"r\":0}],[{\"k\":3,\"tx\":163},{\"k\":4,\"tx\":8.43,\"ty\":221.57,\"r\":196},{\"k\":5,\"tx\":345.25,\"ty\":3.59,\"r\":1}],[{\"k\":3,\"tx\":160.88},{\"k\":4,\"tx\":8.08,\"ty\":221.92,\"r\":197},{\"k\":5,\"tx\":345.5,\"ty\":3.15,\"r\":2}],[{\"k\":3,\"tx\":158.63},{\"k\":4,\"tx\":7.73,\"ty\":222.27,\"r\":198},{\"k\":5,\"tx\":345.75,\"ty\":2.72,\"r\":3}],[{\"k\":3,\"tx\":156.25},{\"k\":4,\"tx\":7.37,\"ty\":222.63,\"r\":199},{\"k\":5,\"tx\":346,\"ty\":2.29,\"r\":4}],[{\"k\":3,\"tx\":153.75},{\"k\":4,\"tx\":7.02,\"ty\":222.98,\"r\":200},{\"k\":5,\"tx\":346.25,\"ty\":1.85,\"r\":5}],[{\"k\":3,\"tx\":151.13},{\"k\":4,\"tx\":6.67,\"ty\":223.33,\"r\":201},{\"k\":5,\"tx\":346.5,\"ty\":1.42,\"r\":6}],[{\"k\":3,\"tx\":148.38},{\"k\":4,\"tx\":6.31,\"ty\":223.69,\"r\":202},{\"k\":5,\"tx\":346.75,\"ty\":0.99,\"r\":7}],[{\"k\":3,\"tx\":145.5},{\"k\":4,\"tx\":5.96,\"ty\":224.04,\"r\":203},{\"k\":5,\"tx\":347,\"ty\":0.56,\"r\":8}],[{\"k\":3,\"tx\":142.5},{\"k\":4,\"tx\":5.6,\"ty\":224.4,\"r\":204},{\"k\":5,\"tx\":347.25,\"ty\":0.12,\"r\":9}],[{\"k\":3,\"tx\":139.5},{\"k\":4,\"tx\":5.25,\"ty\":224.75,\"r\":205},{\"k\":5,\"tx\":347.5,\"ty\":239.69,\"r\":10}],[{\"k\":3,\"tx\":136.5},{\"k\":4,\"tx\":4.9,\"ty\":225.1,\"r\":206},{\"k\":5,\"tx\":347.75,\"ty\":239.26,\"r\":11}],[{\"k\":3,\"tx\":133.5},{\"k\":4,\"tx\":4.54,\"ty\":225.46,\"r\":207},{\"k\":5,\"tx\":348,\"ty\":238.82,\"r\":12}],[{\"k\":3,\"tx\":130.5},{\"k\":4,\"tx\":4.19,\"ty\":225.81,\"r\":208},{\"k\":5,\"tx\":348.25,\"ty\":238.39,\"r\":13}],[{\"k\":3,\"tx\":127.5},{\"k\":4,\"tx\":3.84,\"ty\":226.16,\"r\":209},{\"k\":5,\"tx\":348.5,\"ty\":237.96,\"r\":14}],[{\"k\":3,\"tx\":124.5},{\"k\":4,\"tx\":3.48,\"ty\":226.52,\"r\":210},{\"k\":5,\"tx\":348.75,\"ty\":237.52,\"r\":15}],[{\"k\":3,\"tx\":121.5},{\"k\":4,\"tx\":3.13,\"ty\":226.87,\"r\":211},{\"k\":5,\"tx\":349,\"ty\":237.09,\"r\":16}],[{\"k\":3,\"tx\":118.5},{\"k\":4,\"tx\":2.78,\"ty\":227.22,\"r\":212},{\"k\":5,\"tx\":349.25,\"ty\":236.66,\"r\":17}],[{\"k\":3,\"tx\":115.5},{\"k\":4,\"tx\":2.42,\"ty\":227.58,\"r\":213},{\"k\":5,\"tx\":349.5,\"ty\":236.23,\"r\":18}],[{\"k\":3,\"tx\":112.5},{\"k\":4,\"tx\":2.07,\"ty\":227.93,\"r\":214},{\"k\":5,\"tx\":349.75,\"ty\":235.79,\"r\":19}],[{\"k\":3,\"tx\":109.5},{\"k\":4,\"tx\":1.72,\"ty\":228.28,\"r\":215},{\"k\":5,\"tx\":350,\"ty\":235.36,\"r\":20}],[{\"k\":3,\"tx\":106.5},{\"k\":4,\"tx\":1.36,\"ty\":228.64,\"r\":216},{\"k\":5,\"tx\":350.25,\"ty\":234.93,\"r\":21}],[{\"k\":3,\"tx\":103.5},{\"k\":4,\"tx\":1.01,\"ty\":228.99,\"r\":217},{\"k\":5,\"tx\":350.5,\"ty\":234.49,\"r\":22}],[{\"k\":3,\"tx\":100.5},{\"k\":4,\"tx\":0.66,\"ty\":229.34,\"r\":218},{\"k\":5,\"tx\":350.75,\"ty\":234.06,\"r\":23}],[{\"k\":3,\"tx\":97.5},{\"k\":4,\"tx\":0.3,\"ty\":229.7,\"r\":219},{\"k\":5,\"tx\":351,\"ty\":233.63,\"r\":24}],[{\"k\":3,\"tx\":94.5},{\"k\":4,\"tx\":359.95,\"ty\":230.05,\"r\":220},{\"k\":5,\"tx\":351.25,\"ty\":233.19,\"r\":25}],[{\"k\":3,\"tx\":91.5},{\"k\":4,\"tx\":359.59,\"ty\":230.41,\"r\":221},{\"k\":5,\"tx\":351.5,\"ty\":232.76,\"r\":26}],[{\"k\":3,\"tx\":88.5},{\"k\":4,\"tx\":359.24,\"ty\":230.76,\"r\":222},{\"k\":5,\"tx\":351.75,\"ty\":232.33,\"r\":27}],[{\"k\":3,\"tx\":85.5},{\"k\":4,\"tx\":358.89,\"ty\":231.11,\"r\":223},{\"k\":5,\"tx\":352,\"ty\":231.89,\"r\":28}],[{\"k\":3,\"tx\":82.5},{\"k\":4,\"tx\":358.53,\"ty\":231.47,\"r\":224},{\"k\":5,\"tx\":352.25,\"ty\":231.46,\"r\":29}],[{\"k\":3,\"tx\":79.5},{\"k\":4,\"tx\":358.18,\"ty\":231.82,\"r\":225},{\"k\":5,\"tx\":352.5,\"ty\":231.03,\"r\":30}],[{\"k\":3,\"tx\":76.5},{\"k\":4,\"tx\":357.83,\"ty\":232.17,\"r\":226},{\"k\":5,\"tx\":352.75,\"ty\":230.6,\"r\":31}],[{\"k\":3,\"tx\":73.5},{\"k\":4,\"tx\":357.47,\"ty\":232.53,\"r\":227},{\"k\":5,\"tx\":353,\"ty\":230.16,\"r\":32}],[{\"k\":3,\"tx\":70.5},{\"k\":4,\"tx\":357.12,\"ty\":232.88,\"r\":228},{\"k\":5,\"tx\":353.25,\"ty\":229.73,\"r\":33}],[{\"k\":3,\"tx\":67.5},{\"k\":4,\"tx\":356.77,\"ty\":233.23,\"r\":229},{\"k\":5,\"tx\":353.5,\"ty\":229.3,\"r\":34}],[{\"k\":3,\"tx\":64.5},{\"k\":4,\"tx\":356.41,\"ty\":233.59,\"r\":230},{\"k\":5,\"tx\":353.75,\"ty\":228.86,\"r\":35}],[{\"k\":3,\"tx\":61.5},{\"k\":4,\"tx\":356.06,\"ty\":233.94,\"r\":231},{\"k\":5,\"tx\":354,\"ty\":228.43,\"r\":36}],[{\"k\":3,\"tx\":58.5},{\"k\":4,\"tx\":355.71,\"ty\":234.29,\"r\":232},{\"k\":5,\"tx\":354.25,\"ty\":228,\"r\":37}],[{\"k\":3,\"tx\":55.5},{\"k\":4,\"tx\":355.35,\"ty\":234.65,\"r\":233},{\"k\":5,\"tx\":354.5,\"ty\":227.56,\"r\":38}],[{\"k\":3,\"tx\":52.5},{\"k\":4,\"tx\":355,\"ty\":235,\"r\":234},{\"k\":5,\"tx\":354.75,\"ty\":227.13,\"r\":39}],[{\"k\":3,\"tx\":49.5},{\"k\":4,\"tx\":354.64,\"ty\":235.36,\"r\":235},{\"k\":5,\"tx\":355,\"ty\":226.7,\"r\":40}],[{\"k\":3,\"tx\":46.5},{\"k\":4,\"tx\":354.29,\"ty\":235.71,\"r\":236},{\"k\":5,\"tx\":355.25,\"ty\":226.27,\"r\":41}],[{\"k\":3,\"tx\":43.5},{\"k\":4,\"tx\":353.94,\"ty\":236.06,\"r\":237},{\"k\":5,\"tx\":355.5,\"ty\":225.83,\"r\":42}],[{\"k\":3,\"tx\":40.5},{\"k\":4,\"tx\":353.58,\"ty\":236.42,\"r\":238},{\"k\":5,\"tx\":355.75,\"ty\":225.4,\"r\":43}],[{\"k\":3,\"tx\":37.5},{\"k\":4,\"tx\":353.23,\"ty\":236.77,\"r\":239},{\"k\":5,\"tx\":356,\"ty\":224.97,\"r\":44}],[{\"k\":3,\"tx\":34.5},{\"k\":4,\"tx\":352.88,\"ty\":237.12,\"r\":240},{\"k\":5,\"tx\":356.25,\"ty\":224.53,\"r\":45}],[{\"k\":3,\"tx\":31.5},{\"k\":4,\"tx\":352.52,\"ty\":237.48,\"r\":241},{\"k\":5,\"tx\":356.5,\"ty\":224.1,\"r\":46}],[{\"k\":3,\"tx\":28.5},{\"k\":4,\"tx\":352.17,\"ty\":237.83,\"r\":242},{\"k\":5,\"tx\":356.75,\"ty\":223.67,\"r\":47}],[{\"k\":3,\"tx\":25.5},{\"k\":4,\"tx\":351.82,\"ty\":238.18,\"r\":243},{\"k\":5,\"tx\":357,\"ty\":223.23,\"r\":48}],[{\"k\":3,\"tx\":22.5},{\"k\":4,\"tx\":351.46,\"ty\":238.54,\"r\":244},{\"k\":5,\"tx\":357.25,\"ty\":222.8,\"r\":49}],[{\"k\":3,\"tx\":19.5},{\"k\":4,\"tx\":351.11,\"ty\":238.89,\"r\":245},{\"k\":5,\"tx\":357.5,\"ty\":222.37,\"r\":50}],[{\"k\":3,\"tx\":16.5},{\"k\":4,\"tx\":350.76,\"ty\":239.24,\"r\":246},{\"k\":5,\"tx\":357.75,\"ty\":221.94,\"r\":51}],[{\"k\":3,\"tx\":13.5},{\"k\":4,\"tx\":350.4,\"ty\":239.6,\"r\":247},{\"k\":5,\"tx\":358,\"ty\":221.5,\"r\":52}],[{\"k\":3,\"tx\":10.5},{\"k\":4,\"tx\":350.05,\"ty\":239.95,\"r\":248},{\"k\":5,\"tx\":358.25,\"ty\":221.07,\"r\":53}],[{\"k\":3,\"tx\":7.5},{\"k\":4,\"tx\":349.69,\"ty\":0.31,\"r\":249},{\"k\":5,\"tx\":358.5,\"ty\":220.64,\"r\":54}],[{\"k\":3,\"tx\":4.5},{\"k\":4,\"tx\":349.34,\"ty\":0.66,\"r\":250},{\"k\":5,\"tx\":358.75,\"ty\":220.2,\"r\":55}],[{\"k\":3,\"tx\":1.5},{\"k\":4,\"tx\":348.99,\"ty\":1.01,\"r\":251},{\"k\":5,\"tx\":359,\"ty\":219.77,\"r\":56}],[{\"k\":3,\"tx\":358.5},{\"k\":4,\"tx\":348.63,\"ty\":1.37,\"r\":252},{\"k\":5,\"tx\":359.25,\"ty\":219.34,\"r\":57}],[{\"k\":3,\"tx\":355.5},{\"k\":4,\"tx\":348.28,\"ty\":1.72,\"r\":253},{\"k\":5,\"tx\":359.5,\"ty\":218.9,\"r\":58}],[{\"k\":3,\"tx\":352.5},{\"k\":4,\"tx\":347.93,\"ty\":2.07,\"r\":254},{\"k\":5,\"tx\":359.75,\"ty\":218.47,\"r\":59}],[{\"k\":3,\"tx\":349.5},{\"k\":4,\"tx\":347.57,\"ty\":2.43,\"r\":255},{\"k\":5,\"tx\":0,\"ty\":218.04,\"r\":60}],[{\"k\":3,\"tx\":346.5},{\"k\":4,\"tx\":347.22,\"ty\":2.78,\"r\":256},{\"k\":5,\"tx\":0.25,\"ty\":217.61,\"r\":61}],[{\"k\":3,\"tx\":343.5},{\"k\":4,\"tx\":346.87,\"ty\":3.13,\"r\":257},{\"k\":5,\"tx\":0.5,\"ty\":217.17,\"r\":62}],[{\"k\":3,\"tx\":340.5},{\"k\":4,\"tx\":346.51,\"ty\":3.49,\"r\":258},{\"k\":5,\"tx\":0.75,\"ty\":216.74,\"r\":63}],[{\"k\":3,\"tx\":337.5},{\"k\":4,\"tx\":346.16,\"ty\":3.84,\"r\":259},{\"k\":5,\"tx\":1,\"ty\":216.31,\"r\":64}],[{\"k\":3,\"tx\":334.5},{\"k\":4,\"tx\":345.81,\"ty\":4.19,\"r\":260},{\"k\":5,\"tx\":1.25,\"ty\":215.87,\"r\":65}],[{\"k\":3,\"tx\":331.5},{\"k\":4,\"tx\":345.45,\"ty\":4.55,\"r\":261},{\"k\":5,\"tx\":1.5,\"ty\":215.44,\"r\":66}],[{\"k\":3,\"tx\":328.5},{\"k\":4,\"tx\":345.1,\"ty\":4.9,\"r\":262},{\"k\":5,\"tx\":1.75,\"ty\":215.01,\"r\":67}],[{\"k\":3,\"tx\":325.5},{\"k\":4,\"tx\":344.75,\"ty\":5.25,\"r\":263},{\"k\":5,\"tx\":2,\"ty\":214.57,\"r\":68}],[{\"k\":3,\"tx\":322.5},{\"k\":4,\"tx\":344.39,\"ty\":5.61,\"r\":264},{\"k\":5,\"tx\":2.25,\"ty\":214.14,\"r\":69}]]}},\"outline\":\"Window \\\"Around the edges\\\"\\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 (322.5, 120), rotate -180, 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 (344.3916126134676, 5.608387386532643), rotate 264, 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 (2.2500000000000004, 214.14136145590425), rotate 69, fill #da70d6\\nframes run: 129\"}\n```\n\nThe submarine turns around to -180 degrees (pointing left), speeds up to 3 pixels a frame and leaves on the left: its x passes 0 and goes on from 360, so it ends at (322.5, 120). The jellyfish leave at the bottom left and the top right and come back on the other sides. What's wrapped is the translate, the shape's center, so a shape is cut in half at an edge for a moment before it jumps over; the MOOC's game does the same, and it's barely visible at 60 frames a second. On a phone the sea is wider than the screen: scroll the window sideways to see its right edge, where the shapes come back.\n","task":"Write `wrap(value, size)` in this program without a window. It returns `value` wrapped into the range from 0 up to `size`, for positive and negative values: `wrap(370, 360)` is 10, `wrap(-10, 360)` is 350, and a value already inside stays exactly as it is (`wrap(100, 360)` is 100). The first test runs `main`:\n\n```\n10.0\n350.0\n100.0\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(wrap(370, 360));\n        System.out.println(wrap(-10, 360));\n        System.out.println(wrap(100, 360));\n    }\n\n    public static double wrap(double value, double size) {\n        return value;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(wrap(370, 360));\n        System.out.println(wrap(-10, 360));\n        System.out.println(wrap(100, 360));\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","hints":["Start from the remainder `value % size`. It is already right for positive values; for a negative value it is negative too: -10 % 360 is -10.","Keep the remainder in a variable, and only when it is below 0, add `size` to it: -10 + 360 is 350. Then return it."],"tests":[{"name":"Test 1","stdin":"","expect":"10.0\n350.0\n100.0","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(wrap(-725, 360));\nSystem.out.println(wrap(720, 360));\nSystem.out.println(wrap(-0.5, 240));\n","expect":"355.0\n0.0\n239.5","hidden":true},{"name":"Test 3","stdin":"","call":"System.out.println(wrap(240, 240));\nSystem.out.println(wrap(1000.25, 240));\nSystem.out.println(wrap(0, 360));\n","expect":"0.0\n40.25\n0.0","hidden":true}],"require":[],"forbid":[],"more":[{"task":"Make the sea wrap around. `Main` already has the method `wrap` of the challenge before. Add to `Main` two constants, `public static final int WIDTH = 360;` and `HEIGHT = 240`, and use them for the pane's preferred size and the water's size. Then change Mover's `move` so that the new x is wrapped into `Main.WIDTH` and the new y into `Main.HEIGHT` with `Main.wrap`.\n\nThe test turns the submarine to point left, speeds it up for 24 frames and lets it run off the left edge: it comes back on the right and ends at (322.5, 120).\n","kind":"code","seed":"// ==== 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\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    }\n\n    public Shape getShape() {\n        return shape;\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(shape.getTranslateX() + velocity.getX());\n        shape.setTranslateY(shape.getTranslateY() + velocity.getY());\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// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\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    private Map<KeyCode, Boolean> held = new HashMap<>();\n\n    @Override\n    public void start(Stage stage) {\n        Pane pane = new Pane();\n        pane.setPrefSize(360, 240);\n        pane.getChildren().add(new Rectangle(360, 240, Color.MIDNIGHTBLUE));\n\n        Sub sub = new Sub(180, 120);\n        pane.getChildren().add(sub.getShape());\n        List<Jellyfish> jellyfish = new ArrayList<>();\n        jellyfish.add(new Jellyfish(60, 200, 60));\n        jellyfish.add(new Jellyfish(300, 30, 250));\n        jellyfish.forEach(j -> pane.getChildren().add(j.getShape()));\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 (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            }\n        }.start();\n\n        stage.setScene(scene);\n        stage.setTitle(\"Wrapping\");\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// ==== 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":"// ==== 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\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    }\n\n    public Shape getShape() {\n        return shape;\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// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\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\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(180, 120);\n        pane.getChildren().add(sub.getShape());\n        List<Jellyfish> jellyfish = new ArrayList<>();\n        jellyfish.add(new Jellyfish(60, 200, 60));\n        jellyfish.add(new Jellyfish(300, 30, 250));\n        jellyfish.forEach(j -> pane.getChildren().add(j.getShape()));\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 (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            }\n        }.start();\n\n        stage.setScene(scene);\n        stage.setTitle(\"Wrapping\");\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// ==== 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":["In Main, right after `public class Main extends Application {`: `public static final int WIDTH = 360;` and `public static final int HEIGHT = 240;`. Then `pane.setPrefSize(WIDTH, HEIGHT);`.","In Mover: `shape.setTranslateX(Main.wrap(shape.getTranslateX() + velocity.getX(), Main.WIDTH));`, and the same for y with `Main.HEIGHT`."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["press key LEFT","frames 45","release key LEFT","press key UP","frames 24","release key UP","frames 60"],"window":{"windows":[{"title":"Wrapping","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":322.5,"translateY":120,"rotate":-180,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":92.25,"translateY":15.858638544095404,"rotate":189,"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":277.939700755498,"translateY":209.38982595930813,"rotate":379,"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":129,"list":[]}},"outline":"Window \"Wrapping\"\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 (322.5, 120), rotate -180, 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 (92.25, 15.858638544095404), rotate 189, 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 (277.939700755498, 209.38982595930813), rotate 379, fill #da70d6\nframes run: 129"},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":["press key RIGHT","frames 20","release key RIGHT","press key UP","frames 30","release key UP","frames 120"],"window":{"windows":[{"title":"Wrapping","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":268.2349802770079,"translateY":140.40543949932794,"rotate":80,"points":[-15,-6,9,-6,15,0,9,6,-15,6],"fill":"#ffd700"},{"n":4,"type":"Polygon","f":4,"translateX":102.5,"translateY":33.61215932167643,"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":5,"type":"Polygon","f":5,"translateX":270.92828781732294,"translateY":190.12612723319648,"rotate":420,"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":170,"list":[]}},"outline":"Window \"Wrapping\"\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 (268.2349802770079, 140.40543949932794), rotate 80, 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 (102.5, 33.61215932167643), 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 (270.92828781732294, 190.12612723319648), rotate 420, fill #da70d6\nframes run: 170"},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["frames 250"],"window":{"windows":[{"title":"Wrapping","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":122.5,"translateY":68.25317547305386,"rotate":310,"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":257.24748208429844,"translateY":152.53842240175914,"rotate":500,"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":250,"list":[]}},"outline":"Window \"Wrapping\"\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 (122.5, 68.25317547305386), rotate 310, 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.24748208429844, 152.53842240175914), rotate 500, fill #da70d6\nframes run: 250"}],"require":[{"pattern":"Main\\.WIDTH","message":"Wrap x with `Main.WIDTH`, the constant, rather than the number."},{"pattern":"Main\\.HEIGHT","message":"Wrap y with `Main.HEIGHT`, the constant, rather than the number."},{"pattern":"Main\\.wrap\\s*\\(","message":"Wrap the new position with `Main.wrap`, the method of the challenge before."}],"forbid":[]},{"task":"Remainders of negative numbers. What does this program print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(-7 % 5);\n        System.out.println(7 % -5);\n        System.out.println((-7 % 5 + 5) % 5);\n        System.out.println(-2.5 % 5);\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(-7 % 5);\n        System.out.println(7 % -5);\n        System.out.println((-7 % 5 + 5) % 5);\n        System.out.println(-2.5 % 5);\n    }\n}\n","hints":["In Java, a remainder has the sign of the number on the left of `%`: -7 % 5 is negative, and 7 % -5 is positive.","-7 % 5 is -2, so (-2 + 5) % 5 is 3. With doubles it works the same way: -2.5 % 5 is -2.5."],"tests":[{"name":"Output","stdin":"","expect":"-2\n2\n3\n-2.5","hidden":false}],"require":[],"forbid":[],"lines":["-2","2","3","-2.5"]}]}