{"id":"multidimensional-arrays-end-of-part-12","slug":"end-of-part-12","title":"Four in a row, and the end of part 12","text":"This last step builds a small board game with this module's tools, and then looks back at the whole of part 12.\n\n### Two-dimensional arrays at a glance\n\n| You want | Write |\n| --- | --- |\n| a grid of 3 rows and 4 columns | `int[][] grid = new int[3][4];` |\n| a grid with its values | `int[][] grid = {{1, 2}, {3, 4}};` |\n| the cell in row `r`, column `c` | `grid[r][c]` |\n| the number of rows | `grid.length` |\n| the number of cells in row `r` | `grid[r].length` |\n| every cell | a loop over the rows, with a loop over the columns of the row inside it |\n| the cells around a cell | the rows `r - 1` to `r + 1` and the columns `c - 1` to `c + 1`, each index checked first |\n| rows of different lengths | `new int[3][]`, then each row with its own `new int[...]` |\n| the whole grid as text | `Arrays.deepToString(grid)` |\n\n### Four in a row\n\nIn the game Four in a row, two players take turns dropping pieces into the columns of an upright board, often 6 rows by 7 columns. A piece falls down its column to the lowest free place. The first player with four pieces in a line, across, up and down or diagonally, wins.\n\nThe board can be a `char[][]`, with `'.'` for a free place and a player's letter, `'X'` or `'O'`, for a piece. A new `char[][]` holds the character with code 0 in every cell, which doesn't print as anything useful, so the constructor fills the board with `'.'` first. Row 0 is the top row, as the board is printed, so the lowest free place in a column is found by going up the column from the bottom row, `board.length - 1`.\n\nA line of four starts from some cell and takes the same step three times. A step is a number of rows and a number of columns, as in step 4: `0` rows and `1` column goes across, `1` and `0` goes down, `1` and `1` down to the right, and `1` and `-1` down to the left. Every cell on the way must be inside the board and hold the same piece.\n\n### Array or hash map?\n\nAn array finds a cell straight from its indexes, with no `hashCode`, no `equals` and no bucket to search, as module 45's hash map has. On the other hand, a grid reserves room for every cell when it's created. A grid where nearly every place stays empty, such as a map of a whole city with a few marked spots, could keep only the cells that are used in a HashMap, with keys such as `\"120,45\"` for row 120, column 45. For a game board, where many cells are used and the size is small and fixed, the array is the simpler choice.\n\n### The end of part 12\n\nThis step ends the material of the MOOC's twelfth part. Module 44 gave classes type parameters: a class written once with `<T>` and used as `Box<String>` or `Box<Integer>`, classes with several type parameters, and generic interfaces. A type argument is always a reference type (a class, an interface or an array type such as `int[]`), never a primitive type, so `int` becomes `Integer`. Module 45 had you build your own ArrayList and HashMap: a list on top of an array that grows, `remove` that moves the rest along, and a hash map with an array of buckets chosen by hash codes, which is why its lookups stay fast. Module 46 brought random numbers: `Random` and `nextInt`, ranges such as 1 to 6, seeds that give the same numbers again, `nextDouble` and `Math.random()`, shuffling and simulations. This module added grids: two-dimensional arrays, their rows and columns, nested loops and neighbors. Part 13 comes next, with graphical user interfaces. Part 12 ends with [the MOOC's summary page](https://java-programming.mooc.fi/part-12/5-summary) on its own site.\n","task":"`FourInARow.java` has the board, its constructor, which fills the board with `'.'`, and `toString`, which shows the board one row per line. Write `drop(int column, char piece)`: it puts the piece in the lowest free place of the column and returns the row the piece landed in, or, when the column is full, changes nothing and returns -1. The check runs:\n\n```java\nFourInARow game = new FourInARow(4, 5);\nSystem.out.println(game.drop(1, 'X'));\nSystem.out.println(game.drop(1, 'O'));\nSystem.out.println(game.drop(3, 'X'));\nSystem.out.println(game);\n```\n\nand expects:\n\n```\n3\n2\n3\n.....\n.....\n.O...\n.X.X.\n```\n","kind":"code","seed":"// ==== FourInARow.java ====\npublic class FourInARow {\n    private char[][] board;\n\n    public FourInARow(int rows, int columns) {\n        this.board = new char[rows][columns];\n        for (int row = 0; row < rows; row++) {\n            for (int column = 0; column < columns; column++) {\n                this.board[row][column] = '.';\n            }\n        }\n    }\n\n    public int drop(int column, char piece) {\n        return -1;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder text = new StringBuilder();\n        for (int row = 0; row < this.board.length; row++) {\n            if (row > 0) {\n                text.append(\"\\n\");\n            }\n            for (int column = 0; column < this.board[row].length; column++) {\n                text.append(this.board[row][column]);\n            }\n        }\n        return text.toString();\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        FourInARow game = new FourInARow(4, 5);\n        System.out.println(game.drop(1, 'X'));\n        System.out.println(game.drop(1, 'O'));\n        System.out.println(game.drop(3, 'X'));\n        System.out.println(game);\n    }\n}\n","solution":"// ==== FourInARow.java ====\npublic class FourInARow {\n    private char[][] board;\n\n    public FourInARow(int rows, int columns) {\n        this.board = new char[rows][columns];\n        for (int row = 0; row < rows; row++) {\n            for (int column = 0; column < columns; column++) {\n                this.board[row][column] = '.';\n            }\n        }\n    }\n\n    public int drop(int column, char piece) {\n        for (int row = this.board.length - 1; row >= 0; row--) {\n            if (this.board[row][column] == '.') {\n                this.board[row][column] = piece;\n                return row;\n            }\n        }\n        return -1;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder text = new StringBuilder();\n        for (int row = 0; row < this.board.length; row++) {\n            if (row > 0) {\n                text.append(\"\\n\");\n            }\n            for (int column = 0; column < this.board[row].length; column++) {\n                text.append(this.board[row][column]);\n            }\n        }\n        return text.toString();\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        FourInARow game = new FourInARow(4, 5);\n        System.out.println(game.drop(1, 'X'));\n        System.out.println(game.drop(1, 'O'));\n        System.out.println(game.drop(3, 'X'));\n        System.out.println(game);\n    }\n}\n","hints":["Go up the column from the bottom row: `for (int row = this.board.length - 1; row >= 0; row--)`. The column stays the same all the way.","At the first place that holds `'.'`, put the piece there and `return row;` at once. If the loop ends without finding one, the column is full: `return -1;`"],"tests":[{"name":"Test 1","stdin":"","call":"FourInARow game = new FourInARow(4, 5);\nSystem.out.println(game.drop(1, 'X'));\nSystem.out.println(game.drop(1, 'O'));\nSystem.out.println(game.drop(3, 'X'));\nSystem.out.println(game);\n","expect":"3\n2\n3\n.....\n.....\n.O...\n.X.X.","hidden":false},{"name":"Test 2","stdin":"","call":"FourInARow game = new FourInARow(3, 2);\nSystem.out.println(game.drop(0, 'X') + \" \" + game.drop(0, 'O') + \" \" + game.drop(0, 'X') + \" \" + game.drop(0, 'O'));\nSystem.out.println(game);\n","expect":"2 1 0 -1\nX.\nO.\nX.","hidden":true},{"name":"Test 3","stdin":"","call":"FourInARow game = new FourInARow(1, 3);\nSystem.out.println(game.drop(2, 'O') + \" \" + game.drop(2, 'X') + \" \" + game.drop(0, 'X'));\nSystem.out.println(game);\n","expect":"0 -1 0\nX.O","hidden":true},{"name":"Test 4","stdin":"","call":"FourInARow game = new FourInARow(6, 7);\nfor (int i = 0; i < 10; i++) {\n    game.drop(i % 7, 'X');\n    game.drop(6 - i % 4, 'O');\n}\nSystem.out.println(game);\n","expect":".......\n.......\n.....OO\n...OOOX\nXXXOXXO\nXXXXOOO","hidden":true}],"require":[],"forbid":[],"more":[{"task":"What does this program print? `inLine` counts the cells in a line, from a starting cell, that hold the same piece as the starting cell.\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        char[][] board = {\n            {'.', '.', '.', 'O'},\n            {'X', '.', 'O', 'X'},\n            {'X', 'O', 'X', 'X'},\n            {'O', 'X', 'X', 'O'}\n        };\n        System.out.println(inLine(board, 3, 0, -1, 1));\n        System.out.println(inLine(board, 1, 0, 1, 0));\n        System.out.println(inLine(board, 1, 3, 1, -1));\n    }\n\n    public static int inLine(char[][] board, int row, int column, int rowStep, int columnStep) {\n        char piece = board[row][column];\n        int count = 0;\n        while (row >= 0 && row < board.length && column >= 0 && column < board[row].length && board[row][column] == piece) {\n            count++;\n            row += rowStep;\n            column += columnStep;\n        }\n        return count;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        char[][] board = {\n            {'.', '.', '.', 'O'},\n            {'X', '.', 'O', 'X'},\n            {'X', 'O', 'X', 'X'},\n            {'O', 'X', 'X', 'O'}\n        };\n        System.out.println(inLine(board, 3, 0, -1, 1));\n        System.out.println(inLine(board, 1, 0, 1, 0));\n        System.out.println(inLine(board, 1, 3, 1, -1));\n    }\n\n    public static int inLine(char[][] board, int row, int column, int rowStep, int columnStep) {\n        char piece = board[row][column];\n        int count = 0;\n        while (row >= 0 && row < board.length && column >= 0 && column < board[row].length && board[row][column] == piece) {\n            count++;\n            row += rowStep;\n            column += columnStep;\n        }\n        return count;\n    }\n}\n","hints":["Each round of the loop counts one cell and then takes one step. It stops at the first cell that holds another piece, or at the edge of the board.","The first call starts in the bottom left corner and goes up and to the right: rows 3, 2, 1, 0 and columns 0, 1, 2, 3. The last one goes down and to the left from row 1, column 3."],"tests":[{"name":"Output","stdin":"","expect":"4\n2\n3","hidden":false}],"require":[],"forbid":[],"lines":["4","2","3"]},{"task":"To end part 12, the game needs a winner. Add a method `wins(char piece)` to `FourInARow`: it returns `true` when the board has four of that piece in a line, across, up and down, or diagonally in either direction, and `false` otherwise. `drop` is the one from the first task. `Main.java` is a test drive in which the players drop their pieces in turn, X first, and it prints:\n\n```\n.......\n.......\n......X\n.....XX\n....XXO\nO..XOOO\nX wins: true\nO wins: false\n```\n","kind":"code","seed":"// ==== FourInARow.java ====\npublic class FourInARow {\n    private char[][] board;\n\n    public FourInARow(int rows, int columns) {\n        this.board = new char[rows][columns];\n        for (int row = 0; row < rows; row++) {\n            for (int column = 0; column < columns; column++) {\n                this.board[row][column] = '.';\n            }\n        }\n    }\n\n    public int drop(int column, char piece) {\n        for (int row = this.board.length - 1; row >= 0; row--) {\n            if (this.board[row][column] == '.') {\n                this.board[row][column] = piece;\n                return row;\n            }\n        }\n        return -1;\n    }\n\n    public boolean wins(char piece) {\n        return false;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder text = new StringBuilder();\n        for (int row = 0; row < this.board.length; row++) {\n            if (row > 0) {\n                text.append(\"\\n\");\n            }\n            for (int column = 0; column < this.board[row].length; column++) {\n                text.append(this.board[row][column]);\n            }\n        }\n        return text.toString();\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        FourInARow game = new FourInARow(6, 7);\n        int[] moves = {3, 4, 4, 5, 5, 6, 5, 6, 6, 0, 6};\n        char piece = 'X';\n        for (int column : moves) {\n            game.drop(column, piece);\n            if (piece == 'X') {\n                piece = 'O';\n            } else {\n                piece = 'X';\n            }\n        }\n        System.out.println(game);\n        System.out.println(\"X wins: \" + game.wins('X'));\n        System.out.println(\"O wins: \" + game.wins('O'));\n    }\n}\n","solution":"// ==== FourInARow.java ====\npublic class FourInARow {\n    private char[][] board;\n\n    public FourInARow(int rows, int columns) {\n        this.board = new char[rows][columns];\n        for (int row = 0; row < rows; row++) {\n            for (int column = 0; column < columns; column++) {\n                this.board[row][column] = '.';\n            }\n        }\n    }\n\n    public int drop(int column, char piece) {\n        for (int row = this.board.length - 1; row >= 0; row--) {\n            if (this.board[row][column] == '.') {\n                this.board[row][column] = piece;\n                return row;\n            }\n        }\n        return -1;\n    }\n\n    public boolean wins(char piece) {\n        int[] rowSteps = {0, 1, 1, 1};\n        int[] columnSteps = {1, 0, 1, -1};\n        for (int row = 0; row < this.board.length; row++) {\n            for (int column = 0; column < this.board[row].length; column++) {\n                for (int i = 0; i < rowSteps.length; i++) {\n                    if (this.fourFrom(row, column, rowSteps[i], columnSteps[i], piece)) {\n                        return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    private boolean fourFrom(int row, int column, int rowStep, int columnStep, char piece) {\n        for (int i = 0; i < 4; i++) {\n            int r = row + i * rowStep;\n            int c = column + i * columnStep;\n            if (r < 0 || r >= this.board.length || c < 0 || c >= this.board[r].length) {\n                return false;\n            }\n            if (this.board[r][c] != piece) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @Override\n    public String toString() {\n        StringBuilder text = new StringBuilder();\n        for (int row = 0; row < this.board.length; row++) {\n            if (row > 0) {\n                text.append(\"\\n\");\n            }\n            for (int column = 0; column < this.board[row].length; column++) {\n                text.append(this.board[row][column]);\n            }\n        }\n        return text.toString();\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        FourInARow game = new FourInARow(6, 7);\n        int[] moves = {3, 4, 4, 5, 5, 6, 5, 6, 6, 0, 6};\n        char piece = 'X';\n        for (int column : moves) {\n            game.drop(column, piece);\n            if (piece == 'X') {\n                piece = 'O';\n            } else {\n                piece = 'X';\n            }\n        }\n        System.out.println(game);\n        System.out.println(\"X wins: \" + game.wins('X'));\n        System.out.println(\"O wins: \" + game.wins('O'));\n    }\n}\n","hints":["Try every cell as the start of a line, in four directions: across (`0`, `1`), down (`1`, `0`), down to the right (`1`, `1`) and down to the left (`1`, `-1`). Going the other way would find the same lines again, from their other end.","A helper method such as `fourFrom(row, column, rowStep, columnStep, piece)` can look at the cells `row + i * rowStep`, `column + i * columnStep` for `i` from 0 to 3, and return `false` as soon as one is outside the board or holds something else."],"tests":[{"name":"The test drive","stdin":"","expect":".......\n.......\n......X\n.....XX\n....XXO\nO..XOOO\nX wins: true\nO wins: false","hidden":false},{"name":"Test 2","stdin":"","call":"FourInARow game = new FourInARow(4, 6);\ngame.drop(2, 'O');\ngame.drop(3, 'O');\ngame.drop(4, 'O');\nSystem.out.println(game.wins('O'));\ngame.drop(5, 'O');\nSystem.out.println(game.wins('O') + \" \" + game.wins('X'));\n","expect":"false\ntrue false","hidden":true},{"name":"Test 3","stdin":"","call":"FourInARow game = new FourInARow(5, 3);\nfor (int i = 0; i < 3; i++) {\n    game.drop(2, 'X');\n}\ngame.drop(2, 'O');\ngame.drop(2, 'X');\nSystem.out.println(game.wins('X') + \" \" + game.wins('O'));\nFourInARow column = new FourInARow(4, 1);\nfor (int i = 0; i < 4; i++) {\n    column.drop(0, 'O');\n}\nSystem.out.println(column.wins('O') + \" \" + column.wins('X'));\n","expect":"false false\ntrue false","hidden":true},{"name":"Test 4","stdin":"","call":"FourInARow game = new FourInARow(4, 4);\ngame.drop(0, 'X');\ngame.drop(0, 'X');\ngame.drop(0, 'X');\ngame.drop(0, 'O');\ngame.drop(1, 'X');\ngame.drop(1, 'X');\ngame.drop(1, 'O');\ngame.drop(2, 'X');\ngame.drop(2, 'O');\nSystem.out.println(game.wins('O'));\ngame.drop(3, 'O');\nSystem.out.println(game.wins('O') + \" \" + game.wins('X'));\nSystem.out.println(game);\n","expect":"false\ntrue false\nO...\nXO..\nXXO.\nXXXO","hidden":true},{"name":"Test 5","stdin":"","call":"FourInARow game = new FourInARow(3, 7);\ngame.drop(0, 'X');\ngame.drop(1, 'X');\ngame.drop(2, 'X');\ngame.drop(4, 'X');\ngame.drop(5, 'X');\ngame.drop(6, 'X');\ngame.drop(3, 'O');\nSystem.out.println(game.wins('X') + \" \" + game.wins('O'));\ngame.drop(3, 'X');\ngame.drop(2, 'O');\ngame.drop(4, 'O');\ngame.drop(5, 'O');\nSystem.out.println(game.wins('X') + \" \" + game.wins('O'));\nSystem.out.println(game);\n","expect":"false false\nfalse false\n.......\n..OXOO.\nXXXOXXX","hidden":true}],"require":[],"forbid":[]}]}