all the code in a single file :)

            
        /*
        Conway's Game Of Life
        Brandon Hernandez
        2023-02-11 (approx.)
        */

    import java.io.IOException;
    import java.nio.file.Path;
    import java.nio.file.Files;
    import java.util.InputMismatchException;
    import java.util.Scanner;

    public class Main {
        public static void main(String[] args) {
            // Scanner for the input stream. Delimiter is white-space by default.
            Scanner scanner = new Scanner(System.in);
            String message = "Welcome.";
            int rows = 0;
            int cols = 0;
            int arg0 = 0;

            //If an argument was given from the CLI.
            if (args.length > 0) {
                try {
                    arg0 = Integer.parseInt(args[0]);
                } catch (NumberFormatException e) {
                    message = "Cannot create map from given arguments!";
                }
            }

            if (arg0 <= 0) {
                boolean mapErr = true;
                while (mapErr) {
                    ClearCLI();
                    PrintHeader();
                    PrintMessage(message);
                    PrintMessage("Create your map:");
                    // Input like this: 10 10
                    System.out.printf("Rows Columns --> ");
                    try {
                        rows = scanner.nextInt();
                        cols = scanner.nextInt();
                        // Clear the scanner buffer
                        scanner.nextLine();
                        if (rows == 0 || cols == 0) {
                            throw new InputMismatchException();
                        }
                        mapErr = false;
                        message = "Map created.";
                    } catch (Exception e) {
                        //^^Generic Exception to also catch the one thrown by CTRL+C (abort CLI program).
                        message = "Error: Invalid input.";
                        // Clear the scanner buffer, or else eternal invalid input.
                        scanner.nextLine();
                    }

                }
            } else {
                // Arguments always create a square map!
                rows = arg0;
                cols = rows;
            }

            // Map init
            boolean[][] map = new boolean[rows][cols];
            message = String.format("%dx%d map created.", rows, cols);
            int menuOpt = 0;

            // Game attributes (make this a class!)
            int maxGens = 10;
            int refreshRate = 100;
            boolean mapOverwrite = false;

            while (menuOpt != 99) {
                // Menu
                ClearCLI();
                PrintHeader();
                PrintMap(map, true, true);

                //Last action message, or input error message.
                PrintMessage(message);

                /*
                Improvements
                    -[DONE] Create blank map option. Set dimensions.
                    -Allow game abort.
                    -Create random map option. Set dimensions.
                        -How many spaceships?
                        -How many oscillators?
                    -Create spaceship option.
                    -Create default shape option.
                    -Create oscillator option.
                    -End game when everything is dead, only oscillators, or only still lifes.
                    -Generate statistics and insights. (This map turned into that map after x generations, and so on).
                    -Input filename for save and load.
                    -Develop further in Rust.
                    */
                System.out.printf("%n%s   %s   %s   %s   %s   %s   %s   %s   %s   %s%n%s",
                        "1. Create/kill cell",
                        "2. Set generations",
                        "3. Set refresh rate",
                        "4. Play",
                        "5. Save map",
                        "6. Load map",
                        "7. Random map",
                        "8. Final map overwrite",
                        "9. Create map",
                        "99. Exit",
                        "Your choice: "
                );

                try {
                    menuOpt = scanner.nextInt();
                    scanner.nextLine();
                } catch (InputMismatchException e) {
                    message = String.format("Error: Invalid input.");
                    scanner.nextLine();
                    // Do not execute the code after this. Skip to the next cycle.
                    continue;
                }

                switch (menuOpt) {
                    case 1:
                        // Create/Kill Cell
                        message = CellBirthOrDeath(scanner, map);
                        break;

                    case 2:
                        // Generations
                        System.out.printf("Generations --> ");
                        try {
                            maxGens = scanner.nextInt();
                            scanner.nextLine();
                            message = String.format("Generations = %d", maxGens);
                        } catch (InputMismatchException e) {
                            message = "Invalid input.";
                        }
                        break;

                    case 3:
                        // Refresh rate
                        System.out.printf("Refresh rate ms --> ");
                        try {
                            refreshRate = scanner.nextInt();
                            scanner.nextLine();
                            message = String.format("Refresh rate is %d ms", refreshRate);
                        } catch (InputMismatchException e) {
                            message = "Invalid input.";
                        }
                        break;

                    case 4:
                        // Play
                        if (mapOverwrite) {
                            map = Play(map, maxGens, refreshRate);
                        }
                        else {
                            Play(map, maxGens, refreshRate);
                        }
                        message = "Game finished.";
                        break;

                    case 5:
                        //Save
                        try {
                            SaveMap("maps\\map.txt", map);
                            message = "Map saved.";
                        } catch (IOException e) {
                            message = "Error saving map.";
                        }
                        break;

                    case 6:
                        //Load
                        String filename = String.format("maps\\%s.txt", "map");
                        try {
                            map = LoadMap(filename);
                            message = String.format("%s map loaded.", filename);
                        } catch (Error | IOException | ArrayIndexOutOfBoundsException e) {
                            message = String.format("Error loading map %s", filename);;
                        }
                        break;

                    case 7:
                        //Random
                        message = "Random map function under development!";
                        break;

                    case 8:
                        mapOverwrite = !mapOverwrite;
                        message = mapOverwrite ? "Map Overwrite Enabled" : "Map Overwrite Disabled";
                        break;

                    case 9:
                        // Create map
                        System.out.printf("Create map. Rows Columns --> ");
                        try {
                            rows = scanner.nextInt();
                            cols = scanner.nextInt();
                            // Clear the scanner buffer
                            scanner.nextLine();
                            if (rows == 0 || cols == 0) {
                                throw new InputMismatchException();
                            }
                            message = "Map created.";
                            map = new boolean[rows][cols];
                        } catch (Exception e) {
                            //^^Generic Exception to also catch the one thrown by CTRL+C (abort CLI program).
                            message = "Error: Invalid input.";
                            // Clear the scanner buffer, or else eternal invalid input.
                            scanner.nextLine();
                        }
                        break;

                    case 99:
                        // Exit
                        Bye();
                        break;

                    default:
                        // Default
                        message = "Invalid option.";
                        break;

                }
            }
            scanner.close();
        }

        public static String CellBirthOrDeath (Scanner scanner, boolean[][] map) {
            int r;
            int c;
            System.out.printf("Where? Row Col --> ");
            try {
                r = scanner.nextInt();
                c = scanner.nextInt();
                scanner.nextLine();
            } catch (InputMismatchException e) {
                //e.printStackTrace();
                //Throw away bad input in scanner buffer.
                scanner.nextLine();
                return String.format("Error: Invalid input.");
            }
            if (r >= map.length || r < 0) {
                return String.format("Error: Row value %d is out of bounds. Min = 0, Max = %d.", r, map.length - 1);
            }
            if (c >= map[0].length || c < 0) {
                return String.format("Error: Column value %d is out of bounds. Min = 0, Max = %d.", c, map[0].length - 1);
            }
            map[r][c] = !map[r][c];
            if (map[r][c]) {
                return String.format("[■ ] New cell at [%2d][%2d]", r, c);
            }
            return String.format("[  ] Killed cell at [%2d][%2d]", r, c);
        }

        public static boolean[][] Play(boolean[][] map, int maxGens, int refreshRate) {
            int neighbors;
            int generations = 0;
            int iSize = map.length;
            int jSize = map[0].length;
            int iLast = iSize - 1;
            int jLast = jSize - 1;
            int iIndex = 0;
            int jIndex = 0;

            ClearCLI();
            PrintHeader();
            PrintMap(map, false, false);
            System.out.printf("Generation: %d/%d", generations, maxGens);
            Wait(refreshRate);

            while (generations < maxGens) {
                boolean[][] nextMap = new boolean[iSize][jSize];
                for (int i = 0; i < iSize; i++) {
                    for (int j = 0; j < jSize; j++) {
                        neighbors = 0;
                        /*
                            [i-1,j-1]  [i-1,_j_]  [i-1,j+1]
                            [_i_,j-1]  [_i_,_j_]  [_i_,j+1]
                            [i+1,j-1]  [i+1,_j_]  [i+1,j+1]

                            [i-1,j-1] : NW
                            [i-1,_j_] : N
                            [i-1,j+1] : NE

                            [_i_,j-1] : W
                            [_i_,j+1] : E

                            [i+1,j-1] : SW
                            [i+1,_j_] : S
                            [i+1,j+1] : SE
                            */

                        // [i-1,j-1] : NW
                        iIndex = i - 1;
                        jIndex = j - 1;
                        if (iIndex < 0) {
                            iIndex = iLast;
                        }
                        if (jIndex < 0) {
                            jIndex = jLast;
                        }
                        neighbors = map[iIndex][jIndex] ? neighbors + 1 : neighbors;

                        // [i-1,_j_] : N
                        iIndex = i - 1;
                        jIndex = j;
                        if (iIndex < 0) {
                            iIndex = iLast;
                        }
                        neighbors = map[iIndex][jIndex] ? neighbors + 1 : neighbors;

                        // [i-1,j+1] : NE
                        iIndex = i - 1;
                        jIndex = j + 1;
                        if (iIndex < 0) {
                            iIndex = iLast;
                        }
                        if (jIndex > jLast) {
                            jIndex = 0;
                        }
                        neighbors = map[iIndex][jIndex] ? neighbors + 1 : neighbors;

                        // [_i_,j+1] : W
                        iIndex = i;
                        jIndex = j + 1;
                        if (jIndex > jLast) {
                            jIndex = 0;
                        }
                        neighbors = map[iIndex][jIndex] ? neighbors + 1 : neighbors;

                        // [_i_,j-1] : E
                        iIndex = i;
                        jIndex = j - 1;
                        if (jIndex < 0) {
                            jIndex = jLast;
                        }
                        neighbors = map[iIndex][jIndex] ? neighbors + 1 : neighbors;

                        // [i+1,j-1] : SW
                        iIndex = i + 1;
                        jIndex = j - 1;
                        if (iIndex > iLast) {
                            iIndex = 0;
                        }
                        if (jIndex < 0) {
                            jIndex = jLast;
                        }
                        neighbors = map[iIndex][jIndex] ? neighbors + 1 : neighbors;

                        // [i+1,_j_] : S
                        iIndex = i + 1;
                        jIndex = j;
                        if (iIndex > iLast) {
                            iIndex = 0;
                        }
                        neighbors = map[iIndex][jIndex] ? neighbors + 1 : neighbors;

                        // [i+1,j+1] : SE
                        iIndex = i + 1;
                        jIndex = j + 1;
                        if (iIndex > iLast) {
                            iIndex = 0;
                        }
                        if (jIndex > jLast) {
                            jIndex = 0;
                        }
                        neighbors = map[iIndex][jIndex] ? neighbors + 1 : neighbors;


                        // Create next generation
                        // 1. Any live cell with fewer than 2 live neighbors dies, as if by underpopulation.
                        if (map[i][j] && neighbors < 2) {
                            nextMap[i][j] = false;
                        }
                        // 2. Any live cell with 2 or 3 live neighbors lives on to the next generation.
                        if (map[i][j] && (neighbors == 2 || neighbors == 3)) {
                            nextMap[i][j] = true;
                        }
                        // 3. Any live cell with more than 3 live neighbors dies, as if by overpopulation.
                        if (map[i][j] && neighbors > 3) {
                            nextMap[i][j] = false;
                        }
                        // 4. Any dead cell with exactly 3 live neighbors becomes a live cell, as if by reproduction.
                        if (!map[i][j] && neighbors == 3) {
                            nextMap[i][j] = true;
                        }
                    }
                }

                map = nextMap;
                // Clear console
                ClearCLI();
                PrintHeader();
                PrintMap(map, false, false);
                System.out.printf("Generation: %d/%d", generations, maxGens);
                Wait(refreshRate);
                generations++;
            }
            return map;
        }
        public static void ClearCLI() {
            try {
                new ProcessBuilder("cmd", "/c", "cls").inheritIO().start().waitFor();
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }

        public static void Wait(int millis) {
            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        public static void PrintHeader() {
            System.out.printf("%s%n%s%n%s%n",
                    "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~",
                    "Conway's Game Of Life",
                    "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        }

        public static void PrintMap(boolean[][] map, boolean brackets, boolean headers) {
            String cell = brackets ? "[■ ]" : " ■  ";
            String noCell = brackets ? "[  ]" : "    ";
            int r = map.length;
            int c = map[0].length;
            String header = "";

            // Column header print
            for (int i = 0; i < c; i++) {
                // Top left corner is blank
                if (i == 0) {
                    System.out.print("    ");
                }
                header = headers ? String.format("[%2d]", i) : "    ";
                System.out.print(header);
            }
            System.out.printf("%n");

            // Map print
            for (int i = 0; i < r; i++) {
                // Row header print
                header = headers ? String.format("[%2d]", i) : "    ";
                System.out.print(header);

                for (int j = 0; j < c; j++) {
                    System.out.printf("%s", map[i][j] ? cell : noCell);
                }
                System.out.printf("%n");
            }
        }

        public static void PrintMessage(String message) {
            System.out.printf("%n%s%n", message);
        }

        public static void Bye() {
            System.out.printf("%s%n", "Bye.");
        }

        public static void SaveMap(String fileName, boolean[][] map) throws IOException {
            Path path = Path.of(fileName);
            int i = 0;
            StringBuilder str = new StringBuilder();

            for (boolean[] row : map) {
                for (boolean cell : row) {
                    str.append(cell ? "[x]" : "[ ]");
                }

                // Windows newline sequence is CR LF (Carriage Return Line Feed)
                // If this was the last row, don't set a newline.
                // this isn't very professional is it
                if (i < map.length - 1) {
                    // "\r\n" = "1310"
                    str.append("\r\n");
                }
                i++;
            }
            byte[] chars = str.toString().getBytes();
            Files.write(path, chars);

        }


        public static boolean[][] LoadMap(String filename) throws IOException {
            Path path = Path.of(filename);
            byte[] contents = Files.readAllBytes(path);
            String str = "";

            //Count rows and columns
            int r = 0, c = 0;
            //There is always one row at least.
            r++;
            boolean cCounting = true;
            for (byte ch : contents) {
                str += ch;
                // if [ ] or [x]
                // Clear string. Add column count if still on the first row.
                if (str.equals("913293") || str.equals("9112093")) {
                    str = "";
                    // If we are still counting columns.
                    if (cCounting) {
                        c++;
                    }
                    continue;
                }
                // if newline
                // Clear string. Stop counting columns.
                // "\r\n" = "1310"
                if (str.equals("1310")) {
                    // Stop counting columns when new line detected.
                    cCounting = false;
                    str = "";
                    // If new line, then new row!
                    r++;
                }
            }
            //Don't proceed if attempting to load an empty map.
            if (r == 1 || c == 0) {
                throw new Error();
            }
            boolean[][] map = new boolean[r][c];
            int i = 0;
            int j = 0;
            for (byte ch : contents) {
                str += ch;
                // if [ ]
                if (str.equals("913293")) {
                    map[i][j] = false;
                    str = "";
                    j++;
                }
                // if [x]
                if (str.equals("9112093")) {
                    map[i][j] = true;
                    str = "";
                    j++;
                }
                // if newline
                // "\r\n" = "1310"
                if (str.equals("1310")) {
                    str = "";
                    i++;
                    j = 0;
                }
            }

            return map;
        }

        public static void Dir(String dir) {
            /*
            Get dir contents
                */

        }

    }
            
        
Go back