{"id":"gui-views-end-of-part-13","slug":"end-of-part-13","title":"A board of buttons, and the end of part 13","text":"The last step of part 13 builds a small board game for two players, with a button for each cell of a grid, and then looks back at the whole part.\n\n### A button for each cell\n\nA grid of buttons is built with two nested loops, as module 47 went through a two-dimensional array, and `grid.add(button, column, row)` puts each button in its cell (module 48). Each button needs a handler that knows its cell. The first try uses the loop variables in the lambda:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.GridPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        GridPane board = new GridPane();\n        for (int row = 0; row < 2; row++) {\n            for (int column = 0; column < 3; column++) {\n                Button cell = new Button(\"?\");\n                cell.setOnAction(event -> System.out.println(\"Clicked row \" + row + \", column \" + column));\n                board.add(cell, column, row);\n            }\n        }\n\n        stage.setScene(new Scene(board));\n        stage.setTitle(\"Board\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```javac\nMain.java:15: error: local variables referenced from a lambda expression must be final or effectively final\n                cell.setOnAction(event -> System.out.println(\"Clicked row \" + row + \", column \" + column));\n                                                                              ^\nMain.java:15: error: local variables referenced from a lambda expression must be final or effectively final\n                cell.setOnAction(event -> System.out.println(\"Clicked row \" + row + \", column \" + column));\n                                                                                                  ^\n2 errors\n```\n\nIt's the effectively final rule of module 49 again. `row` and `column` change at every round of their loops, so a lambda can't use them. The cure is a copy: inside the inner loop, `int r = row;` and `int c = column;` declare new variables in every round, and each of them keeps its value from then on. So each button's lambda gets the copies of its own round, which are its own cell:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.layout.GridPane;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        GridPane board = new GridPane();\n        for (int row = 0; row < 2; row++) {\n            for (int column = 0; column < 3; column++) {\n                int r = row;\n                int c = column;\n                Button cell = new Button(\"?\");\n                cell.setOnAction(event -> System.out.println(\"Clicked row \" + r + \", column \" + c));\n                board.add(cell, c, r);\n            }\n        }\n\n        stage.setScene(new Scene(board));\n        stage.setTitle(\"Board\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[\"click Button 5\",\"click Button 3\"],\"window\":{\"windows\":[{\"title\":\"Board\",\"scene\":{\"root\":{\"n\":1,\"type\":\"GridPane\",\"children\":[{\"n\":2,\"type\":\"Button\",\"column\":0,\"row\":0,\"text\":\"?\",\"onAction\":true},{\"n\":3,\"type\":\"Button\",\"column\":1,\"row\":0,\"text\":\"?\",\"onAction\":true},{\"n\":4,\"type\":\"Button\",\"column\":2,\"row\":0,\"text\":\"?\",\"onAction\":true},{\"n\":5,\"type\":\"Button\",\"column\":0,\"row\":1,\"text\":\"?\",\"onAction\":true},{\"n\":6,\"type\":\"Button\",\"column\":1,\"row\":1,\"text\":\"?\",\"onAction\":true},{\"n\":7,\"type\":\"Button\",\"column\":2,\"row\":1,\"text\":\"?\",\"onAction\":true}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Board\\\"\\n  GridPane\\n    (0, 0) Button \\\"?\\\"\\n    (1, 0) Button \\\"?\\\"\\n    (2, 0) Button \\\"?\\\"\\n    (0, 1) Button \\\"?\\\"\\n    (1, 1) Button \\\"?\\\"\\n    (2, 1) Button \\\"?\\\"\"}\n```\n\n```output\nClicked row 1, column 1\nClicked row 0, column 2\n```\n\nThe clicks were on the fifth and the third button, counted row by row. The variable `cell` is new in every round too, so a handler may use it to change its own button. (The variable of a for-each loop is also new in every round, so a lambda can use it without a copy.)\n\n### Number grab\n\nThe game: a board of numbers, and two players, Red and Blue, who take turns, Red first. A player clicks a number that nobody has taken yet, and it's theirs: it's added to their points, and the turn goes to the other player. A number that is taken already can't be taken again.\n\nAs in steps 5 to 7, the rules go in a class of their own. `NumberGrab` keeps the numbers in a two-dimensional array, and who took each one in a second array of the same size, `null` for a number nobody has taken. `take` checks the rules and returns whether the move happened. The window asks it at every click:\n\n```java\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        int[][] numbers = {{5, 2, 8, 3}, {1, 9, 4, 7}, {6, 3, 2, 8}};\n        NumberGrab game = new NumberGrab(numbers);\n        Label status = new Label(\"Red's turn\");\n        Label score = new Label(\"Red 0, Blue 0\");\n\n        GridPane board = new GridPane();\n        board.setHgap(4);\n        board.setVgap(4);\n        board.setPadding(new Insets(10));\n        for (int row = 0; row < numbers.length; row++) {\n            for (int column = 0; column < numbers[row].length; column++) {\n                int r = row;\n                int c = column;\n                Button cell = new Button(\"\" + numbers[r][c]);\n                cell.setOnAction(event -> {\n                    if (game.take(r, c)) {\n                        cell.setText(game.ownerAt(r, c) + \" \" + game.numberAt(r, c));\n                        status.setText(game.turn() + \"'s turn\");\n                        score.setText(\"Red \" + game.score(\"Red\") + \", Blue \" + game.score(\"Blue\"));\n                    }\n                });\n                board.add(cell, c, r);\n            }\n        }\n\n        BorderPane frame = new BorderPane();\n        frame.setCenter(board);\n        frame.setBottom(new VBox(4, status, score));\n\n        stage.setScene(new Scene(frame));\n        stage.setTitle(\"Number grab\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n// ==== NumberGrab.java ====\npublic class NumberGrab {\n    private int[][] numbers;\n    private String[][] owners;\n    private String turn;\n    private int red;\n    private int blue;\n\n    public NumberGrab(int[][] numbers) {\n        this.numbers = numbers;\n        this.owners = new String[numbers.length][numbers[0].length];\n        this.turn = \"Red\";\n    }\n\n    public int numberAt(int row, int column) {\n        return this.numbers[row][column];\n    }\n\n    public String ownerAt(int row, int column) {\n        return this.owners[row][column];\n    }\n\n    public String turn() {\n        return this.turn;\n    }\n\n    public int score(String player) {\n        if (player.equals(\"Red\")) {\n            return this.red;\n        }\n        return this.blue;\n    }\n\n    public boolean take(int row, int column) {\n        if (this.owners[row][column] != null) {\n            return false;\n        }\n        this.owners[row][column] = this.turn;\n        if (this.turn.equals(\"Red\")) {\n            this.red += this.numbers[row][column];\n            this.turn = \"Blue\";\n        } else {\n            this.blue += this.numbers[row][column];\n            this.turn = \"Red\";\n        }\n        return true;\n    }\n}\n```\n\n```window\n{\"events\":[\"click Button \\\"9\\\"\",\"click Button \\\"8\\\"\",\"click Button \\\"Red 9\\\"\",\"click Button \\\"7\\\"\"],\"window\":{\"windows\":[{\"title\":\"Number grab\",\"scene\":{\"root\":{\"n\":1,\"type\":\"BorderPane\",\"center\":{\"n\":2,\"type\":\"GridPane\",\"padding\":[10,10,10,10],\"hgap\":4,\"vgap\":4,\"children\":[{\"n\":3,\"type\":\"Button\",\"column\":0,\"row\":0,\"text\":\"5\",\"onAction\":true},{\"n\":4,\"type\":\"Button\",\"column\":1,\"row\":0,\"text\":\"2\",\"onAction\":true},{\"n\":5,\"type\":\"Button\",\"column\":2,\"row\":0,\"text\":\"Blue 8\",\"onAction\":true},{\"n\":6,\"type\":\"Button\",\"column\":3,\"row\":0,\"text\":\"3\",\"onAction\":true},{\"n\":7,\"type\":\"Button\",\"column\":0,\"row\":1,\"text\":\"1\",\"onAction\":true},{\"n\":8,\"type\":\"Button\",\"column\":1,\"row\":1,\"text\":\"Red 9\",\"onAction\":true},{\"n\":9,\"type\":\"Button\",\"column\":2,\"row\":1,\"text\":\"4\",\"onAction\":true},{\"n\":10,\"type\":\"Button\",\"column\":3,\"row\":1,\"text\":\"Red 7\",\"onAction\":true},{\"n\":11,\"type\":\"Button\",\"column\":0,\"row\":2,\"text\":\"6\",\"onAction\":true},{\"n\":12,\"type\":\"Button\",\"column\":1,\"row\":2,\"text\":\"3\",\"onAction\":true},{\"n\":13,\"type\":\"Button\",\"column\":2,\"row\":2,\"text\":\"2\",\"onAction\":true},{\"n\":14,\"type\":\"Button\",\"column\":3,\"row\":2,\"text\":\"8\",\"onAction\":true}]},\"bottom\":{\"n\":15,\"type\":\"VBox\",\"spacing\":4,\"children\":[{\"n\":16,\"type\":\"Label\",\"text\":\"Blue's turn\"},{\"n\":17,\"type\":\"Label\",\"text\":\"Red 16, Blue 8\"}]}}}}],\"problems\":[]},\"outline\":\"Window \\\"Number grab\\\"\\n  BorderPane\\n    center: GridPane hgap 4, vgap 4, padding 10\\n      (0, 0) Button \\\"5\\\"\\n      (1, 0) Button \\\"2\\\"\\n      (2, 0) Button \\\"Blue 8\\\"\\n      (3, 0) Button \\\"3\\\"\\n      (0, 1) Button \\\"1\\\"\\n      (1, 1) Button \\\"Red 9\\\"\\n      (2, 1) Button \\\"4\\\"\\n      (3, 1) Button \\\"Red 7\\\"\\n      (0, 2) Button \\\"6\\\"\\n      (1, 2) Button \\\"3\\\"\\n      (2, 2) Button \\\"2\\\"\\n      (3, 2) Button \\\"8\\\"\\n    bottom: VBox spacing 4\\n      Label \\\"Blue's turn\\\"\\n      Label \\\"Red 16, Blue 8\\\"\"}\n```\n\n- Red took the 9, Blue the first 8, and Red the 7. The click on `Red 9`, a number that was taken already, changed nothing: `take` returned `false`, so the handler did nothing either. The handler doesn't know the rules; it forwards the click and shows what the game says.\n- A taken button shows who took it, like `Red 9`, and the label under the board says whose turn it is, above the score.\n- `numbers[r][c]` is an int, and `\"\" + numbers[r][c]` makes it the button's text.\n\n### When the game ends\n\nAll the numbers together make the board's total, 58 on this board. A player who has more than half of the total has won, since the other player can't catch up any more, and the game ends right there, even with numbers left on the board. If every number is taken and neither player has more than half, they have exactly half each: a draw. Once the game is over, no number can be taken.\n\nThat's a rule, so it belongs in NumberGrab too. The task below writes it.\n\n### This module at a glance\n\n| You want | Write |\n| --- | --- |\n| the window to show another scene | `stage.setScene(otherScene)`, with each scene made once |\n| a menu that stays, and content that changes | a BorderPane with the menu on top, and `frame.setCenter(view)` |\n| a view in a class of its own | a class with `public Parent getView()` |\n| a view that keeps what happens in it | make its nodes in the constructor, and return the same root from `getView()` |\n| a view that always shows the latest data | build it anew in `getView()`, from the logic objects |\n| data and rules that several views share | a class without JavaFX, and one object of it given to each view |\n| a button for each cell of a grid | nested loops, `grid.add(button, column, row)`, and copies such as `int r = row;` for the lambda |\n\n### The end of part 13\n\nThis step ends the material of the MOOC's thirteenth part, about graphical user interfaces. Module 48 built windows with JavaFX: an Application whose `start` method gets the window, a Stage, and gives it a Scene, the controls Label, Button, TextField and TextArea, and the layouts FlowPane, BorderPane, HBox, VBox and GridPane, with spacing, padding and alignment, and layouts inside layouts. Module 49 made the windows react: event handlers given with `setOnAction` as lambdas, handlers that read and change the window, the effectively final rule and the places where a changing value can live instead, listeners that hear every change of a text field, buttons switched on and off, the work done in a plain class, and launch parameters. This module had applications with several views: scenes that replace each other, a frame whose center changes, views as classes, the logic apart from the views, and a larger app built in two parts. Part 14 comes next, with charts, drawing, images and sound, a larger game, and libraries. Part 13 ends with [the MOOC's summary page](https://java-programming.mooc.fi/part-13/6-summary) on its own site.\n","task":"Finish the game's logic. `NumberGrab.java` is the class from above, with two placeholder methods at its end, and `Main.java` is the window, which now shows `status()` in the label under the board. In NumberGrab:\n\n- write `isOver()`: it returns `true` when a player has more than half of the total of all the board's numbers, or when every number is taken, and `false` otherwise\n- write `status()`: while the game goes on, it returns `Red's turn` or `Blue's turn`; when it's over, `Red wins 32 to 6` or `Blue wins 32 to 6` (the winner's points first), or for equal points `A draw: 29 each`\n- change `take`: once the game is over, it takes nothing and returns `false`\n\nThe total and the number of taken cells can be kept in instance variables, worked out in the constructor and in `take`. The check runs:\n\n```java\nNumberGrab game = new NumberGrab(new int[][] {{9, 1}, {8, 2}});\nSystem.out.println(game.status());\ngame.take(0, 0);\ngame.take(1, 0);\nSystem.out.println(game.isOver() + \" \" + game.status());\ngame.take(0, 1);\ngame.take(1, 1);\nSystem.out.println(game.isOver() + \" \" + game.status());\n```\n\nand expects:\n\n```\nRed's turn\nfalse Red's turn\ntrue A draw: 10 each\n```\n","kind":"code","seed":"// ==== NumberGrab.java ====\npublic class NumberGrab {\n    private int[][] numbers;\n    private String[][] owners;\n    private String turn;\n    private int red;\n    private int blue;\n\n    public NumberGrab(int[][] numbers) {\n        this.numbers = numbers;\n        this.owners = new String[numbers.length][numbers[0].length];\n        this.turn = \"Red\";\n    }\n\n    public int numberAt(int row, int column) {\n        return this.numbers[row][column];\n    }\n\n    public String ownerAt(int row, int column) {\n        return this.owners[row][column];\n    }\n\n    public String turn() {\n        return this.turn;\n    }\n\n    public int score(String player) {\n        if (player.equals(\"Red\")) {\n            return this.red;\n        }\n        return this.blue;\n    }\n\n    public boolean take(int row, int column) {\n        if (this.owners[row][column] != null) {\n            return false;\n        }\n        this.owners[row][column] = this.turn;\n        if (this.turn.equals(\"Red\")) {\n            this.red += this.numbers[row][column];\n            this.turn = \"Blue\";\n        } else {\n            this.blue += this.numbers[row][column];\n            this.turn = \"Red\";\n        }\n        return true;\n    }\n\n    public boolean isOver() {\n        return false;\n    }\n\n    public String status() {\n        return this.turn + \"'s turn\";\n    }\n}\n\n// ==== Main.java ====\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        int[][] numbers = {{5, 2, 8, 3}, {1, 9, 4, 7}, {6, 3, 2, 8}};\n        NumberGrab game = new NumberGrab(numbers);\n        Label status = new Label(game.status());\n        Label score = new Label(\"Red 0, Blue 0\");\n\n        GridPane board = new GridPane();\n        board.setHgap(4);\n        board.setVgap(4);\n        board.setPadding(new Insets(10));\n        for (int row = 0; row < numbers.length; row++) {\n            for (int column = 0; column < numbers[row].length; column++) {\n                int r = row;\n                int c = column;\n                Button cell = new Button(\"\" + numbers[r][c]);\n                cell.setOnAction(event -> {\n                    if (game.take(r, c)) {\n                        cell.setText(game.ownerAt(r, c) + \" \" + game.numberAt(r, c));\n                        status.setText(game.status());\n                        score.setText(\"Red \" + game.score(\"Red\") + \", Blue \" + game.score(\"Blue\"));\n                    }\n                });\n                board.add(cell, c, r);\n            }\n        }\n\n        BorderPane frame = new BorderPane();\n        frame.setCenter(board);\n        frame.setBottom(new VBox(4, status, score));\n\n        stage.setScene(new Scene(frame));\n        stage.setTitle(\"Number grab\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"// ==== NumberGrab.java ====\npublic class NumberGrab {\n    private int[][] numbers;\n    private String[][] owners;\n    private String turn;\n    private int red;\n    private int blue;\n    private int taken;\n    private int total;\n\n    public NumberGrab(int[][] numbers) {\n        this.numbers = numbers;\n        this.owners = new String[numbers.length][numbers[0].length];\n        this.turn = \"Red\";\n        for (int[] row : numbers) {\n            for (int number : row) {\n                this.total += number;\n            }\n        }\n    }\n\n    public int numberAt(int row, int column) {\n        return this.numbers[row][column];\n    }\n\n    public String ownerAt(int row, int column) {\n        return this.owners[row][column];\n    }\n\n    public String turn() {\n        return this.turn;\n    }\n\n    public int score(String player) {\n        if (player.equals(\"Red\")) {\n            return this.red;\n        }\n        return this.blue;\n    }\n\n    public boolean take(int row, int column) {\n        if (this.isOver() || this.owners[row][column] != null) {\n            return false;\n        }\n        this.owners[row][column] = this.turn;\n        if (this.turn.equals(\"Red\")) {\n            this.red += this.numbers[row][column];\n            this.turn = \"Blue\";\n        } else {\n            this.blue += this.numbers[row][column];\n            this.turn = \"Red\";\n        }\n        this.taken++;\n        return true;\n    }\n\n    public boolean isOver() {\n        int cells = this.numbers.length * this.numbers[0].length;\n        return 2 * this.red > this.total || 2 * this.blue > this.total || this.taken == cells;\n    }\n\n    public String status() {\n        if (!this.isOver()) {\n            return this.turn + \"'s turn\";\n        }\n        if (this.red > this.blue) {\n            return \"Red wins \" + this.red + \" to \" + this.blue;\n        }\n        if (this.blue > this.red) {\n            return \"Blue wins \" + this.blue + \" to \" + this.red;\n        }\n        return \"A draw: \" + this.red + \" each\";\n    }\n}\n\n// ==== Main.java ====\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        int[][] numbers = {{5, 2, 8, 3}, {1, 9, 4, 7}, {6, 3, 2, 8}};\n        NumberGrab game = new NumberGrab(numbers);\n        Label status = new Label(game.status());\n        Label score = new Label(\"Red 0, Blue 0\");\n\n        GridPane board = new GridPane();\n        board.setHgap(4);\n        board.setVgap(4);\n        board.setPadding(new Insets(10));\n        for (int row = 0; row < numbers.length; row++) {\n            for (int column = 0; column < numbers[row].length; column++) {\n                int r = row;\n                int c = column;\n                Button cell = new Button(\"\" + numbers[r][c]);\n                cell.setOnAction(event -> {\n                    if (game.take(r, c)) {\n                        cell.setText(game.ownerAt(r, c) + \" \" + game.numberAt(r, c));\n                        status.setText(game.status());\n                        score.setText(\"Red \" + game.score(\"Red\") + \", Blue \" + game.score(\"Blue\"));\n                    }\n                });\n                board.add(cell, c, r);\n            }\n        }\n\n        BorderPane frame = new BorderPane();\n        frame.setCenter(board);\n        frame.setBottom(new VBox(4, status, score));\n\n        stage.setScene(new Scene(frame));\n        stage.setTitle(\"Number grab\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["Add `private int total;` and `private int taken;`. The constructor adds up every number with two loops (for-each loops over the rows and their numbers work), and `take` adds one to `taken` for each move it makes. Then `isOver()` is `2 * this.red > this.total || 2 * this.blue > this.total || this.taken == cells`, with `cells` the number of rows times the number of columns.","`status()` returns the turn's text first `if (!this.isOver())`; then compares `this.red` and `this.blue` for the three endings. At the start of `take`, `if (this.isOver() || ...)` returns `false`."],"tests":[{"name":"Test 1","stdin":"","call":"NumberGrab game = new NumberGrab(new int[][] {{9, 1}, {8, 2}});\nSystem.out.println(game.status());\ngame.take(0, 0);\ngame.take(1, 0);\nSystem.out.println(game.isOver() + \" \" + game.status());\ngame.take(0, 1);\ngame.take(1, 1);\nSystem.out.println(game.isOver() + \" \" + game.status());\n","expect":"Red's turn\nfalse Red's turn\ntrue A draw: 10 each","hidden":false},{"name":"Test 2","stdin":"","call":"NumberGrab game = new NumberGrab(new int[][] {{7, 1, 1}, {6, 1, 1}});\ngame.take(0, 0);\ngame.take(0, 1);\nSystem.out.println(game.isOver());\nSystem.out.println(game.take(1, 0));\nSystem.out.println(game.isOver() + \" \" + game.status());\nSystem.out.println(game.take(1, 1) + \" \" + game.score(\"Blue\") + \" \" + game.ownerAt(1, 1));\n","expect":"false\ntrue\ntrue Red wins 13 to 1\nfalse 1 null","hidden":true},{"name":"Test 3","stdin":"","call":"NumberGrab game = new NumberGrab(new int[][] {{1, 9, 2}, {8, 1, 1}});\ngame.take(0, 0);\ngame.take(0, 1);\ngame.take(0, 2);\nSystem.out.println(game.status());\ngame.take(1, 0);\nSystem.out.println(game.status());\nSystem.out.println(game.take(1, 1) + \" \" + game.turn());\n","expect":"Blue's turn\nBlue wins 17 to 3\nfalse Red","hidden":true},{"name":"Test 4","stdin":"","call":"NumberGrab game = new NumberGrab(new int[][] {{2, 3}});\nSystem.out.println(game.take(0, 0) + \" \" + game.take(0, 0) + \" \" + game.turn());\nSystem.out.println(game.isOver() + \" \" + game.status());\ngame.take(0, 1);\nSystem.out.println(game.isOver() + \" \" + game.status());\n","expect":"true false Blue\nfalse Blue's turn\ntrue Blue wins 3 to 2","hidden":true}],"require":[],"forbid":[],"more":[{"task":"Make the board show which numbers are gone. `NumberGrab` is complete now, with `isOver()` and `status()`. Change the window in `Main`:\n\n- the button of a number that has just been taken is disabled\n- when the game is over, every button of the board is disabled: call `setDisable(true)` on each button itself, not on the GridPane or the frame\n\nA disabled button can't be clicked, so the window no longer even tries to take a number twice. Everything else stays as it is. A list of the buttons, filled in the loops, lets a handler reach all of them. The task card shows the window after clicks on 9, 8, the taken 9 and 7.\n","kind":"code","seed":"// ==== Main.java ====\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        int[][] numbers = {{5, 2, 8, 3}, {1, 9, 4, 7}, {6, 3, 2, 8}};\n        NumberGrab game = new NumberGrab(numbers);\n        Label status = new Label(game.status());\n        Label score = new Label(\"Red 0, Blue 0\");\n\n        GridPane board = new GridPane();\n        board.setHgap(4);\n        board.setVgap(4);\n        board.setPadding(new Insets(10));\n        for (int row = 0; row < numbers.length; row++) {\n            for (int column = 0; column < numbers[row].length; column++) {\n                int r = row;\n                int c = column;\n                Button cell = new Button(\"\" + numbers[r][c]);\n                cell.setOnAction(event -> {\n                    if (game.take(r, c)) {\n                        cell.setText(game.ownerAt(r, c) + \" \" + game.numberAt(r, c));\n                        status.setText(game.status());\n                        score.setText(\"Red \" + game.score(\"Red\") + \", Blue \" + game.score(\"Blue\"));\n                    }\n                });\n                board.add(cell, c, r);\n            }\n        }\n\n        BorderPane frame = new BorderPane();\n        frame.setCenter(board);\n        frame.setBottom(new VBox(4, status, score));\n\n        stage.setScene(new Scene(frame));\n        stage.setTitle(\"Number grab\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== NumberGrab.java ====\npublic class NumberGrab {\n    private int[][] numbers;\n    private String[][] owners;\n    private String turn;\n    private int red;\n    private int blue;\n    private int taken;\n    private int total;\n\n    public NumberGrab(int[][] numbers) {\n        this.numbers = numbers;\n        this.owners = new String[numbers.length][numbers[0].length];\n        this.turn = \"Red\";\n        for (int[] row : numbers) {\n            for (int number : row) {\n                this.total += number;\n            }\n        }\n    }\n\n    public int numberAt(int row, int column) {\n        return this.numbers[row][column];\n    }\n\n    public String ownerAt(int row, int column) {\n        return this.owners[row][column];\n    }\n\n    public String turn() {\n        return this.turn;\n    }\n\n    public int score(String player) {\n        if (player.equals(\"Red\")) {\n            return this.red;\n        }\n        return this.blue;\n    }\n\n    public boolean take(int row, int column) {\n        if (this.isOver() || this.owners[row][column] != null) {\n            return false;\n        }\n        this.owners[row][column] = this.turn;\n        if (this.turn.equals(\"Red\")) {\n            this.red += this.numbers[row][column];\n            this.turn = \"Blue\";\n        } else {\n            this.blue += this.numbers[row][column];\n            this.turn = \"Red\";\n        }\n        this.taken++;\n        return true;\n    }\n\n    public boolean isOver() {\n        int cells = this.numbers.length * this.numbers[0].length;\n        return 2 * this.red > this.total || 2 * this.blue > this.total || this.taken == cells;\n    }\n\n    public String status() {\n        if (!this.isOver()) {\n            return this.turn + \"'s turn\";\n        }\n        if (this.red > this.blue) {\n            return \"Red wins \" + this.red + \" to \" + this.blue;\n        }\n        if (this.blue > this.red) {\n            return \"Blue wins \" + this.blue + \" to \" + this.red;\n        }\n        return \"A draw: \" + this.red + \" each\";\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.util.ArrayList;\nimport javafx.application.Application;\nimport javafx.geometry.Insets;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.BorderPane;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        int[][] numbers = {{5, 2, 8, 3}, {1, 9, 4, 7}, {6, 3, 2, 8}};\n        NumberGrab game = new NumberGrab(numbers);\n        Label status = new Label(game.status());\n        Label score = new Label(\"Red 0, Blue 0\");\n\n        GridPane board = new GridPane();\n        board.setHgap(4);\n        board.setVgap(4);\n        board.setPadding(new Insets(10));\n        ArrayList<Button> cells = new ArrayList<>();\n        for (int row = 0; row < numbers.length; row++) {\n            for (int column = 0; column < numbers[row].length; column++) {\n                int r = row;\n                int c = column;\n                Button cell = new Button(\"\" + numbers[r][c]);\n                cell.setOnAction(event -> {\n                    if (game.take(r, c)) {\n                        cell.setText(game.ownerAt(r, c) + \" \" + game.numberAt(r, c));\n                        cell.setDisable(true);\n                        status.setText(game.status());\n                        score.setText(\"Red \" + game.score(\"Red\") + \", Blue \" + game.score(\"Blue\"));\n                        if (game.isOver()) {\n                            for (Button other : cells) {\n                                other.setDisable(true);\n                            }\n                        }\n                    }\n                });\n                cells.add(cell);\n                board.add(cell, c, r);\n            }\n        }\n\n        BorderPane frame = new BorderPane();\n        frame.setCenter(board);\n        frame.setBottom(new VBox(4, status, score));\n\n        stage.setScene(new Scene(frame));\n        stage.setTitle(\"Number grab\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n\n// ==== NumberGrab.java ====\npublic class NumberGrab {\n    private int[][] numbers;\n    private String[][] owners;\n    private String turn;\n    private int red;\n    private int blue;\n    private int taken;\n    private int total;\n\n    public NumberGrab(int[][] numbers) {\n        this.numbers = numbers;\n        this.owners = new String[numbers.length][numbers[0].length];\n        this.turn = \"Red\";\n        for (int[] row : numbers) {\n            for (int number : row) {\n                this.total += number;\n            }\n        }\n    }\n\n    public int numberAt(int row, int column) {\n        return this.numbers[row][column];\n    }\n\n    public String ownerAt(int row, int column) {\n        return this.owners[row][column];\n    }\n\n    public String turn() {\n        return this.turn;\n    }\n\n    public int score(String player) {\n        if (player.equals(\"Red\")) {\n            return this.red;\n        }\n        return this.blue;\n    }\n\n    public boolean take(int row, int column) {\n        if (this.isOver() || this.owners[row][column] != null) {\n            return false;\n        }\n        this.owners[row][column] = this.turn;\n        if (this.turn.equals(\"Red\")) {\n            this.red += this.numbers[row][column];\n            this.turn = \"Blue\";\n        } else {\n            this.blue += this.numbers[row][column];\n            this.turn = \"Red\";\n        }\n        this.taken++;\n        return true;\n    }\n\n    public boolean isOver() {\n        int cells = this.numbers.length * this.numbers[0].length;\n        return 2 * this.red > this.total || 2 * this.blue > this.total || this.taken == cells;\n    }\n\n    public String status() {\n        if (!this.isOver()) {\n            return this.turn + \"'s turn\";\n        }\n        if (this.red > this.blue) {\n            return \"Red wins \" + this.red + \" to \" + this.blue;\n        }\n        if (this.blue > this.red) {\n            return \"Blue wins \" + this.blue + \" to \" + this.red;\n        }\n        return \"A draw: \" + this.red + \" each\";\n    }\n}\n","hints":["In the handler, after a successful `take`, `cell.setDisable(true);` disables the button that was clicked.","Before the loops, make `ArrayList<Button> cells = new ArrayList<>();` (and import `java.util.ArrayList`), and add each button to it in the loop. In the handler, `if (game.isOver())`, go through `cells` and disable every one."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["click Button \"9\"","click Button \"8\"","click Button \"Red 9\"","click Button \"7\""],"window":{"windows":[{"title":"Number grab","scene":{"root":{"n":1,"type":"BorderPane","center":{"n":2,"type":"GridPane","padding":[10,10,10,10],"hgap":4,"vgap":4,"children":[{"n":3,"type":"Button","column":0,"row":0,"text":"5","onAction":true},{"n":4,"type":"Button","column":1,"row":0,"text":"2","onAction":true},{"n":5,"type":"Button","column":2,"row":0,"text":"Blue 8","disable":true,"onAction":true},{"n":6,"type":"Button","column":3,"row":0,"text":"3","onAction":true},{"n":7,"type":"Button","column":0,"row":1,"text":"1","onAction":true},{"n":8,"type":"Button","column":1,"row":1,"text":"Red 9","disable":true,"onAction":true},{"n":9,"type":"Button","column":2,"row":1,"text":"4","onAction":true},{"n":10,"type":"Button","column":3,"row":1,"text":"Red 7","disable":true,"onAction":true},{"n":11,"type":"Button","column":0,"row":2,"text":"6","onAction":true},{"n":12,"type":"Button","column":1,"row":2,"text":"3","onAction":true},{"n":13,"type":"Button","column":2,"row":2,"text":"2","onAction":true},{"n":14,"type":"Button","column":3,"row":2,"text":"8","onAction":true}]},"bottom":{"n":15,"type":"VBox","spacing":4,"children":[{"n":16,"type":"Label","text":"Blue's turn"},{"n":17,"type":"Label","text":"Red 16, Blue 8"}]}}}}],"problems":[]},"outline":"Window \"Number grab\"\n  BorderPane\n    center: GridPane hgap 4, vgap 4, padding 10\n      (0, 0) Button \"5\"\n      (1, 0) Button \"2\"\n      (2, 0) Button \"Blue 8\" (disabled)\n      (3, 0) Button \"3\"\n      (0, 1) Button \"1\"\n      (1, 1) Button \"Red 9\" (disabled)\n      (2, 1) Button \"4\"\n      (3, 1) Button \"Red 7\" (disabled)\n      (0, 2) Button \"6\"\n      (1, 2) Button \"3\"\n      (2, 2) Button \"2\"\n      (3, 2) Button \"8\"\n    bottom: VBox spacing 4\n      Label \"Blue's turn\"\n      Label \"Red 16, Blue 8\""},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":[],"window":{"windows":[{"title":"Number grab","scene":{"root":{"n":1,"type":"BorderPane","center":{"n":2,"type":"GridPane","padding":[10,10,10,10],"hgap":4,"vgap":4,"children":[{"n":3,"type":"Button","column":0,"row":0,"text":"5","onAction":true},{"n":4,"type":"Button","column":1,"row":0,"text":"2","onAction":true},{"n":5,"type":"Button","column":2,"row":0,"text":"8","onAction":true},{"n":6,"type":"Button","column":3,"row":0,"text":"3","onAction":true},{"n":7,"type":"Button","column":0,"row":1,"text":"1","onAction":true},{"n":8,"type":"Button","column":1,"row":1,"text":"9","onAction":true},{"n":9,"type":"Button","column":2,"row":1,"text":"4","onAction":true},{"n":10,"type":"Button","column":3,"row":1,"text":"7","onAction":true},{"n":11,"type":"Button","column":0,"row":2,"text":"6","onAction":true},{"n":12,"type":"Button","column":1,"row":2,"text":"3","onAction":true},{"n":13,"type":"Button","column":2,"row":2,"text":"2","onAction":true},{"n":14,"type":"Button","column":3,"row":2,"text":"8","onAction":true}]},"bottom":{"n":15,"type":"VBox","spacing":4,"children":[{"n":16,"type":"Label","text":"Red's turn"},{"n":17,"type":"Label","text":"Red 0, Blue 0"}]}}}}],"problems":[]},"outline":"Window \"Number grab\"\n  BorderPane\n    center: GridPane hgap 4, vgap 4, padding 10\n      (0, 0) Button \"5\"\n      (1, 0) Button \"2\"\n      (2, 0) Button \"8\"\n      (3, 0) Button \"3\"\n      (0, 1) Button \"1\"\n      (1, 1) Button \"9\"\n      (2, 1) Button \"4\"\n      (3, 1) Button \"7\"\n      (0, 2) Button \"6\"\n      (1, 2) Button \"3\"\n      (2, 2) Button \"2\"\n      (3, 2) Button \"8\"\n    bottom: VBox spacing 4\n      Label \"Red's turn\"\n      Label \"Red 0, Blue 0\""},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["click Button \"9\"","click Button \"1\"","click Button \"8\"","click Button \"2\"","click Button \"8\"","click Button \"3\"","click Button \"7\"","click Button \"5\""],"window":{"windows":[{"title":"Number grab","scene":{"root":{"n":1,"type":"BorderPane","center":{"n":2,"type":"GridPane","padding":[10,10,10,10],"hgap":4,"vgap":4,"children":[{"n":3,"type":"Button","column":0,"row":0,"text":"5","disable":true,"onAction":true},{"n":4,"type":"Button","column":1,"row":0,"text":"Blue 2","disable":true,"onAction":true},{"n":5,"type":"Button","column":2,"row":0,"text":"Red 8","disable":true,"onAction":true},{"n":6,"type":"Button","column":3,"row":0,"text":"Blue 3","disable":true,"onAction":true},{"n":7,"type":"Button","column":0,"row":1,"text":"Blue 1","disable":true,"onAction":true},{"n":8,"type":"Button","column":1,"row":1,"text":"Red 9","disable":true,"onAction":true},{"n":9,"type":"Button","column":2,"row":1,"text":"4","disable":true,"onAction":true},{"n":10,"type":"Button","column":3,"row":1,"text":"Red 7","disable":true,"onAction":true},{"n":11,"type":"Button","column":0,"row":2,"text":"6","disable":true,"onAction":true},{"n":12,"type":"Button","column":1,"row":2,"text":"3","disable":true,"onAction":true},{"n":13,"type":"Button","column":2,"row":2,"text":"2","disable":true,"onAction":true},{"n":14,"type":"Button","column":3,"row":2,"text":"Red 8","disable":true,"onAction":true}]},"bottom":{"n":15,"type":"VBox","spacing":4,"children":[{"n":16,"type":"Label","text":"Red wins 32 to 6"},{"n":17,"type":"Label","text":"Red 32, Blue 6"}]}}}}],"problems":[]},"outline":"Window \"Number grab\"\n  BorderPane\n    center: GridPane hgap 4, vgap 4, padding 10\n      (0, 0) Button \"5\" (disabled)\n      (1, 0) Button \"Blue 2\" (disabled)\n      (2, 0) Button \"Red 8\" (disabled)\n      (3, 0) Button \"Blue 3\" (disabled)\n      (0, 1) Button \"Blue 1\" (disabled)\n      (1, 1) Button \"Red 9\" (disabled)\n      (2, 1) Button \"4\" (disabled)\n      (3, 1) Button \"Red 7\" (disabled)\n      (0, 2) Button \"6\" (disabled)\n      (1, 2) Button \"3\" (disabled)\n      (2, 2) Button \"2\" (disabled)\n      (3, 2) Button \"Red 8\" (disabled)\n    bottom: VBox spacing 4\n      Label \"Red wins 32 to 6\"\n      Label \"Red 32, Blue 6\""},{"name":"Test 4","stdin":"","expect":"","hidden":true,"events":["click Button \"5\"","click Button \"6\"","click Button \"Blue 6\"","click Button \"4\""],"window":{"windows":[{"title":"Number grab","scene":{"root":{"n":1,"type":"BorderPane","center":{"n":2,"type":"GridPane","padding":[10,10,10,10],"hgap":4,"vgap":4,"children":[{"n":3,"type":"Button","column":0,"row":0,"text":"Red 5","disable":true,"onAction":true},{"n":4,"type":"Button","column":1,"row":0,"text":"2","onAction":true},{"n":5,"type":"Button","column":2,"row":0,"text":"8","onAction":true},{"n":6,"type":"Button","column":3,"row":0,"text":"3","onAction":true},{"n":7,"type":"Button","column":0,"row":1,"text":"1","onAction":true},{"n":8,"type":"Button","column":1,"row":1,"text":"9","onAction":true},{"n":9,"type":"Button","column":2,"row":1,"text":"Red 4","disable":true,"onAction":true},{"n":10,"type":"Button","column":3,"row":1,"text":"7","onAction":true},{"n":11,"type":"Button","column":0,"row":2,"text":"Blue 6","disable":true,"onAction":true},{"n":12,"type":"Button","column":1,"row":2,"text":"3","onAction":true},{"n":13,"type":"Button","column":2,"row":2,"text":"2","onAction":true},{"n":14,"type":"Button","column":3,"row":2,"text":"8","onAction":true}]},"bottom":{"n":15,"type":"VBox","spacing":4,"children":[{"n":16,"type":"Label","text":"Blue's turn"},{"n":17,"type":"Label","text":"Red 9, Blue 6"}]}}}}],"problems":[]},"outline":"Window \"Number grab\"\n  BorderPane\n    center: GridPane hgap 4, vgap 4, padding 10\n      (0, 0) Button \"Red 5\" (disabled)\n      (1, 0) Button \"2\"\n      (2, 0) Button \"8\"\n      (3, 0) Button \"3\"\n      (0, 1) Button \"1\"\n      (1, 1) Button \"9\"\n      (2, 1) Button \"Red 4\" (disabled)\n      (3, 1) Button \"7\"\n      (0, 2) Button \"Blue 6\" (disabled)\n      (1, 2) Button \"3\"\n      (2, 2) Button \"2\"\n      (3, 2) Button \"8\"\n    bottom: VBox spacing 4\n      Label \"Blue's turn\"\n      Label \"Red 9, Blue 6\""}],"require":[],"forbid":[]},{"task":"A grid of buttons made in loops, with each button's cell copied for its lambda. The buttons also go into a list, in the order the loops make them. `GridPane.getRowIndex(node)` and `GridPane.getColumnIndex(node)` tell which cell of the grid a node is in. What does the program print?\n","kind":"predict","seed":"import java.util.ArrayList;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label last = new Label(\"none\");\n        GridPane grid = new GridPane();\n        ArrayList<Button> buttons = new ArrayList<>();\n        for (int row = 0; row < 2; row++) {\n            for (int column = 0; column < 3; column++) {\n                int r = row;\n                int c = column;\n                Button button = new Button(\"?\");\n                button.setOnAction(event -> last.setText(\"row \" + r + \", column \" + c));\n                grid.add(button, c, r);\n                buttons.add(button);\n            }\n        }\n        System.out.println(last.getText() + \" \" + buttons.size());\n        buttons.get(4).fire();\n        System.out.println(last.getText());\n        buttons.get(2).fire();\n        System.out.println(last.getText());\n        System.out.println(GridPane.getRowIndex(buttons.get(3)) + \" \" + GridPane.getColumnIndex(buttons.get(3)));\n\n        stage.setScene(new Scene(new VBox(grid, last)));\n        stage.setTitle(\"Grid\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import java.util.ArrayList;\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.GridPane;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label last = new Label(\"none\");\n        GridPane grid = new GridPane();\n        ArrayList<Button> buttons = new ArrayList<>();\n        for (int row = 0; row < 2; row++) {\n            for (int column = 0; column < 3; column++) {\n                int r = row;\n                int c = column;\n                Button button = new Button(\"?\");\n                button.setOnAction(event -> last.setText(\"row \" + r + \", column \" + c));\n                grid.add(button, c, r);\n                buttons.add(button);\n            }\n        }\n        System.out.println(last.getText() + \" \" + buttons.size());\n        buttons.get(4).fire();\n        System.out.println(last.getText());\n        buttons.get(2).fire();\n        System.out.println(last.getText());\n        System.out.println(GridPane.getRowIndex(buttons.get(3)) + \" \" + GridPane.getColumnIndex(buttons.get(3)));\n\n        stage.setScene(new Scene(new VBox(grid, last)));\n        stage.setTitle(\"Grid\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["The loops make the buttons row by row: row 0 gets columns 0, 1 and 2, then row 1 does. So the list's indexes 0 to 2 are row 0, and 3 to 5 are row 1.","No handler runs until `fire()`, so the label says `none` at first. Each handler sets the label from its own copies `r` and `c`."],"tests":[{"name":"Output","stdin":"","expect":"none 6\nrow 1, column 1\nrow 0, column 2\n1 0","hidden":false}],"require":[],"forbid":[],"lines":["none 6","row 1, column 1","row 0, column 2","1 0"]}]}