    /**
     * Saves the DP table as several N x N matrices.  (space costly for large N)
     *
     * @param fileName
     */
    private void saveDB(String fileName) {    // Save dynamic programming table and records
        // Format: numGroups
        //		lowestCost matrix (numGroups x numGroups)
        //		neighbor matrix (numGroups x numGroups)
        // 		paths matrix (with paths). Each path on a line.  A path is a list of subgoals.  Just have 0 if no states.
        try (PrintWriter out = new PrintWriter(fileName)) {
            out.println(numGroups);
            for (int i = 0; i < numGroups; i++) {
                for (int j = 0; j < numGroups; j++)
                    out.print(lowestCost[i][j] + "\t");
                out.println();
            }
            for (int i = 0; i < numGroups; i++) {
                for (int j = 0; j < numGroups; j++)
                    out.print(neighbor[i][j] + "\t");
                out.println();
            }
            for (int i = 0; i < numGroups; i++) {
                for (int j = 0; j < numGroups; j++) {
                    if (paths[i][j] == null) out.println("0");
                    else {
                        out.print(paths[i][j].length);
                        for (int k = 0; k < paths[i][j].length; k++)
                            out.print("\t" + paths[i][j][k]);
                        out.println();
                    }
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Error with output file: " + e);
        }
    }

    /**
     * Saves the DP table as several N x N matrices.  (space costly for large N)
     *
     * @param fileName
     */
    private void saveDB(String fileName) {    // Save dynamic programming table and records
        // Format: numGroups
        //		lowestCost matrix (numGroups x numGroups)
        //		neighbor matrix (numGroups x numGroups)
        // 		paths matrix (with paths). Each path on a line.  A path is a list of subgoals.  Just have 0 if no states.
        PrintWriter out = null;
        try {
            out = new PrintWriter(fileName);
            out.println(numGroups);
            for (int i = 0; i < numGroups; i++) {
                for (int j = 0; j < numGroups; j++)
                    out.print(lowestCost[i][j] + "\t");
                out.println();
            }
            for (int i = 0; i < numGroups; i++) {
                for (int j = 0; j < numGroups; j++)
                    out.print(neighbor[i][j] + "\t");
                out.println();
            }
            for (int i = 0; i < numGroups; i++) {
                for (int j = 0; j < numGroups; j++) {
                    if (paths[i][j] == null) out.println("0");
                    else {
                        out.print(paths[i][j].length);
                        for (int k = 0; k < paths[i][j].length; k++)
                            out.print("\t" + paths[i][j][k]);
                        out.println();
                    }
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Error with output file: " + e);
        } finally {
            if (out != null) out.close();
        }
    }

Try With Resources try(PrintWriter out=new PrintWriter(fileName)) in method private saveDB(fileName String) : void from class database.SubgoalDynamicDB


    /**
     * Saves the DP table as several adjacency lists (uses less space as DP table gets large).
     *
     * @param fileName
     */
    private void saveDB(String fileName) {    // Save dynamic programming table and records
        // Format: numGroups
        //		lowestCost matrix (numGroups x numGroups)
        //		neighbor matrix (numGroups x numGroups)
        // 		paths matrix (with paths). Each path on a line.  A path is a list of subgoals.  Just have 0 if no states.
        try (PrintWriter out = new PrintWriter(fileName)) {
            out.println(numGroups);
            for (int i = 0; i < numGroups; i++) {    // Read each group which has # neighbors as N, neighborId[N], lowest cost[N], neighbor[] and paths on each line
                int numNeighbors = neighborId[i].length;
                out.println(numNeighbors);
                for (int j = 0; j < numNeighbors; j++)
                    out.print(neighborId[i][j] + "\t");
                out.println();
                for (int j = 0; j < numNeighbors; j++)
                    out.print(lowestCost[i][j] + "\t");
                out.println();
                for (int j = 0; j < numNeighbors; j++)
                    out.print(neighbor[i][j] + "\t");
                out.println();
                for (int j = 0; j < numNeighbors; j++) {
                    out.print(paths[i][j].length + "\t");
                    for (int k = 0; k < paths[i][j].length; k++)
                        out.print("\t" + paths[i][j][k]);
                    out.println();
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Error with output file: " + e);
        }
    }

    /**
     * Saves the DP table as several adjacency lists (uses less space as DP table gets large).
     *
     * @param fileName
     */
    private void saveDB(String fileName) {    // Save dynamic programming table and records
        // Format: numGroups
        //		lowestCost matrix (numGroups x numGroups)
        //		neighbor matrix (numGroups x numGroups)
        // 		paths matrix (with paths). Each path on a line.  A path is a list of subgoals.  Just have 0 if no states.
        PrintWriter out = null;
        try {
            out = new PrintWriter(fileName);
            out.println(numGroups);
            for (int i = 0; i < numGroups; i++) {    // Read each group which has # neighbors as N, neighborId[N], lowest cost[N], neighbor[] and paths on each line
                int numNeighbors = neighborId[i].length;
                out.println(numNeighbors);
                for (int j = 0; j < numNeighbors; j++)
                    out.print(neighborId[i][j] + "\t");
                out.println();
                for (int j = 0; j < numNeighbors; j++)
                    out.print(lowestCost[i][j] + "\t");
                out.println();
                for (int j = 0; j < numNeighbors; j++)
                    out.print(neighbor[i][j] + "\t");
                out.println();
                for (int j = 0; j < numNeighbors; j++) {
                    out.print(paths[i][j].length + "\t");
                    for (int k = 0; k < paths[i][j].length; k++)
                        out.print("\t" + paths[i][j][k]);
                    out.println();
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Error with output file: " + e);
        } finally {
            if (out != null) out.close();
        }
    }

Try With Resources try(PrintWriter out=new PrintWriter(fileName)) in method private saveDB(fileName String) : void from class database.SubgoalDynamicDB2


    /**
     * Saves the base paths and neighbors in a file.  Rest of data stored in compressed mapping index (abstraction mapping, and neighbors matrix).
     *
     * @param fileName
     */
    private void saveDB(String fileName) {    // Save dynamic programming table and records
        // Format: numGroups
        //		neighbor matrix (numGroups x []) - just stores direct neighbors
        // 		paths matrix (with paths). Each path on a line.  A path is a list of subgoals.  Just have 0 if no states.
        try (PrintWriter out = new PrintWriter(fileName)) {
            out.println(numGroups);
            for (int i = 0; i < numGroups; i++) {    // Read each group
                int numNeighbors = this.numNeighbors[i];
                out.println(numNeighbors);                    // Don't actually save out neighbors array as that is stored else where
                for (int j = 0; j < numNeighbors; j++)        // Save list of neighbor ids mapping to each path in path array
                    out.print(neighborId[i][j] + "\t");
                out.println();
                for (int j = 0; j < numNeighbors; j++)        // Write out neighbor paths
                {
                    out.print(paths[i][j].length + "\t");
                    for (int k = 0; k < paths[i][j].length; k++)
                        out.print("\t" + paths[i][j][k]);
                    out.println();
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Error with output file: " + e);
        }
    }

    /**
     * Saves the base paths and neighbors in a file.  Rest of data stored in compressed mapping index (abstraction mapping, and neighbors matrix).
     *
     * @param fileName
     */
    private void saveDB(String fileName) {    // Save dynamic programming table and records
        // Format: numGroups
        //		neighbor matrix (numGroups x []) - just stores direct neighbors
        // 		paths matrix (with paths). Each path on a line.  A path is a list of subgoals.  Just have 0 if no states.
        PrintWriter out = null;
        try {
            out = new PrintWriter(fileName);
            out.println(numGroups);
            for (int i = 0; i < numGroups; i++) {    // Read each group
                int numNeighbors = this.numNeighbors[i];
                out.println(numNeighbors);                    // Don't actually save out neighbors array as that is stored else where
                for (int j = 0; j < numNeighbors; j++)        // Save list of neighbor ids mapping to each path in path array
                    out.print(neighborId[i][j] + "\t");
                out.println();
                for (int j = 0; j < numNeighbors; j++)        // Write out neighbor paths
                {
                    out.print(paths[i][j].length + "\t");
                    for (int k = 0; k < paths[i][j].length; k++)
                        out.print("\t" + paths[i][j][k]);
                    out.println();
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Error with output file: " + e);
        } finally {
            if (out != null) out.close();
        }
    }

Try With Resources try(PrintWriter out=new PrintWriter(fileName)) in method private saveDB(fileName String) : void from class database.SubgoalDynamicDB3


    public void load(String fileName) {
        try (Scanner sc = new Scanner(new File(fileName))) {

            String st = sc.nextLine();    // Drop first line which is formatted
            if (!st.contains("type")) {    // Map is in binary format
                sc.close();
                this.loadMap(fileName);
                return;
            }

            st = sc.nextLine();            // Number of rows. e.g. height 139
            rows = Integer.parseInt(st.substring(7).trim());
            st = sc.nextLine();            // Number of cols. e.g. width 148
            cols = Integer.parseInt(st.substring(6).trim());
            st = sc.nextLine();            // Just says map
            squares = new int[rows][cols];
            mapInit();
            states = 0;

            for (int i = 0; i < rows; i++) {
                st = sc.nextLine();
                for (int j = 0; j < cols; j++) {
                    if (st.charAt(j) == '@' || st.charAt(j) == 'O' || st.charAt(j) == 'W' || st.charAt(j) == 'T')
                        squares[i][j] = WALL_CHAR;
                    else {
                        squares[i][j] = EMPTY_CHAR;
                        states++;
                    }
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Did not find input file: " + e);
        }
    }

    public void load(String fileName) {
        Scanner sc = null;
        try {
            sc = new Scanner(new File(fileName));

            String st = sc.nextLine();    // Drop first line which is format
            if (!st.contains("type")) {    // Map is in binary format
                sc.close();
                this.loadMap(fileName);
                return;
            }

            st = sc.nextLine();            // Number of rows. e.g. height 139
            rows = Integer.parseInt(st.substring(7).trim());
            st = sc.nextLine();            // Number of cols. e.g. width 148
            cols = Integer.parseInt(st.substring(6).trim());
            st = sc.nextLine();            // Just says map
            squares = new int[rows][cols];
            mapInit();
            states = 0;

            for (int i = 0; i < rows; i++) {
                st = sc.nextLine();
                for (int j = 0; j < cols; j++) {
                    if (st.charAt(j) == '@' || st.charAt(j) == 'O' || st.charAt(j) == 'W' || st.charAt(j) == 'T')
                        squares[i][j] = WALL_CHAR;
                    else {
                        squares[i][j] = EMPTY_CHAR;
                        states++;
                    }
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Did not find input file: " + e);
        } finally {
            if (sc != null) sc.close();
        }
    }

Try With Resources try(Scanner sc=new Scanner(new File(fileName))) in method public load(fileName String) : void from class map.GameMap


    /**
     * This version loads a number-based map.
     *
     * @param fileName
     */
    public void loadMap(String fileName) {
        try (Scanner sc = new Scanner(new File(fileName))) {

            String st = sc.nextLine();            // Number of rows. e.g. height 139
            rows = Integer.parseInt(st.substring(7).trim());
            st = sc.nextLine();            // Number of cols. e.g. width 148
            cols = Integer.parseInt(st.substring(6).trim());
            squares = new int[rows][cols];
            mapInit();
            states = 0;

            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    squares[i][j] = sc.nextInt();
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Did not find input file: " + e);
        } catch (Exception e) {
            System.out.println("IO Error: " + e);
        }
    }

    /**
     * This version loads a number-based map.
     *
     * @param fileName
     */
    public void loadMap(String fileName) {
        Scanner sc = null;
        try {
            sc = new Scanner(new File(fileName));

            String st = sc.nextLine();            // Number of rows. e.g. height 139
            rows = Integer.parseInt(st.substring(7).trim());
            st = sc.nextLine();            // Number of cols. e.g. width 148
            cols = Integer.parseInt(st.substring(6).trim());
            squares = new int[rows][cols];
            mapInit();
            states = 0;

            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    squares[i][j] = sc.nextInt();
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Did not find input file: " + e);
        } catch (Exception e) {
            System.out.println("IO Error: " + e);
        } finally {
            if (sc != null) sc.close();
        }
    }

Try With Resources try(Scanner sc=new Scanner(new File(fileName))) in method public loadMap(fileName String) : void from class map.GameMap


    public void save(String fileName) {
        try (PrintWriter out = new PrintWriter(fileName)) {
            out.println("height " + rows);
            out.println("width " + cols);

            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    out.print(this.squares[i][j]);
                    out.print("\t");
                }
                out.println();
            }
        } catch (FileNotFoundException e) {
            System.out.println("Error with output file: " + e);
        }
    }

    public void save(String fileName) {
        PrintWriter out = null;
        try {
            out = new PrintWriter(fileName);
            out.println("height " + rows);
            out.println("width " + cols);

            for (int i = 0; i < rows; i++) {
                for (int j = 0; j < cols; j++) {
                    out.print(this.squares[i][j]);
                    out.print("\t");
                }
                out.println();
            }
        } catch (FileNotFoundException e) {
            System.out.println("Error with output file: " + e);
        } finally {
            if (out != null) out.close();
        }
    }

Try With Resources try(PrintWriter out=new PrintWriter(fileName)) in method public save(fileName String) : void from class map.GameMap


    public void copyMatrixNew(int row, int col, GameMap source, GameMap dest, int sourceval, int destval) {
        Stack<MapPoint> currentSet = new Stack<MapPoint>();
        HashMap<Integer, Integer> visited = new HashMap<>();
        currentSet.add(new MapPoint(row, col));
        int count = 0;
        while (!currentSet.isEmpty()) {
            MapPoint curPoint = currentSet.pop();
            int id = getId(curPoint.getRow(), curPoint.getCol());
            if (visited.containsKey(id)) continue;

            visited.put(id, null);
            count++;
            // Expand in all directions
            int r = curPoint.getRow();
            int c = curPoint.getCol();

            if (source.squares[r][c] == sourceval)
                // Add check to make sure square is not already assigned
                if (dest.squares[r][c] == ' ') dest.squares[r][c] = destval;

            if (source.isValid(r - 1, c) && !source.isWall(r - 1, c))
                if (source.squares[r - 1][c] == sourceval)    // Above
                    currentSet.add(new MapPoint(r - 1, c));

            if (source.isValid(r - 1, c + 1) && !source.isWall(r - 1, c + 1))
                if (source.squares[r - 1][c + 1] == sourceval) // Top right
                    currentSet.add(new MapPoint(r - 1, c + 1));

            if (source.isValid(r, c + 1) && !source.isWall(r, c + 1))
                if (source.squares[r][c + 1] == sourceval) // Right
                    currentSet.add(new MapPoint(r, c + 1));

            if (source.isValid(r + 1, c + 1) && !source.isWall(r + 1, c + 1))
                if (source.squares[r + 1][c + 1] == sourceval) // Bottom right
                    currentSet.add(new MapPoint(r + 1, c + 1));

            if (source.isValid(r + 1, c) && !source.isWall(r + 1, c))
                if (source.squares[r + 1][c] == sourceval) // Bottom
                    currentSet.add(new MapPoint(r + 1, c));

            if (source.isValid(r + 1, c - 1) && !source.isWall(r + 1, c - 1))
                if (squares[r + 1][c - 1] == sourceval) // Bottom left
                    currentSet.add(new MapPoint(r + 1, c - 1));

            if (source.isValid(r, c - 1) && !source.isWall(r, c - 1))
                if (source.squares[r][c - 1] == sourceval) // Left
                    currentSet.add(new MapPoint(r, c - 1));

            if (source.isValid(r - 1, c - 1) && !source.isWall(r - 1, c - 1))
                if (source.squares[r - 1][c - 1] == sourceval) // Top left
                    currentSet.add(new MapPoint(r - 1, c - 1));
        }

        // System.out.println("Num: "+sourceval+" Copied: "+count);
    }

    public void copyMatrixNew(int row, int col, GameMap source, GameMap dest, int sourceval, int destval) {
        Stack<MapPoint> currentSet = new Stack<MapPoint>();
        HashMap<Integer, Integer> visited = new HashMap<Integer, Integer>();
        currentSet.add(new MapPoint(row, col));
        int count = 0;
        while (!currentSet.isEmpty()) {
            MapPoint curPoint = currentSet.pop();
            int id = getId(curPoint.getRow(), curPoint.getCol());
            if (visited.containsKey(id)) continue;

            visited.put(id, null);
            count++;
            // Expand in all directions
            int r = curPoint.getRow();
            int c = curPoint.getCol();

            if (source.squares[r][c] == sourceval)
                // Add check to make sure square is not already assigned
                if (dest.squares[r][c] == ' ') dest.squares[r][c] = destval;

            if (source.isValid(r - 1, c) && !source.isWall(r - 1, c))
                if (source.squares[r - 1][c] == sourceval)    // Above
                    currentSet.add(new MapPoint(r - 1, c));

            if (source.isValid(r - 1, c + 1) && !source.isWall(r - 1, c + 1))
                if (source.squares[r - 1][c + 1] == sourceval) // Top right
                    currentSet.add(new MapPoint(r - 1, c + 1));

            if (source.isValid(r, c + 1) && !source.isWall(r, c + 1))
                if (source.squares[r][c + 1] == sourceval) // Right
                    currentSet.add(new MapPoint(r, c + 1));

            if (source.isValid(r + 1, c + 1) && !source.isWall(r + 1, c + 1))
                if (source.squares[r + 1][c + 1] == sourceval) // Bottom right
                    currentSet.add(new MapPoint(r + 1, c + 1));

            if (source.isValid(r + 1, c) && !source.isWall(r + 1, c))
                if (source.squares[r + 1][c] == sourceval) // Bottom
                    currentSet.add(new MapPoint(r + 1, c));

            if (source.isValid(r + 1, c - 1) && !source.isWall(r + 1, c - 1))
                if (squares[r + 1][c - 1] == sourceval) // Bottom left
                    currentSet.add(new MapPoint(r + 1, c - 1));

            if (source.isValid(r, c - 1) && !source.isWall(r, c - 1))
                if (source.squares[r][c - 1] == sourceval) // Left
                    currentSet.add(new MapPoint(r, c - 1));

            if (source.isValid(r - 1, c - 1) && !source.isWall(r - 1, c - 1))
                if (source.squares[r - 1][c - 1] == sourceval) // Top left
                    currentSet.add(new MapPoint(r - 1, c - 1));
        }

        // System.out.println("Num: "+sourceval+" Copied: "+count);
    }

Replace Generic With Diamond HashMap<Integer,Integer> with HashMap<> in method public copyMatrixNew(row int, col int, source GameMap, dest GameMap, sourceval int, destval int) : void from class map.GameMap


    public GameMap reachableGridAbstract(int EXPAND_LIMIT, SearchAbstractAlgorithm searchAlg, DBStatsRecord dbstat) {
        // Idea: Keep adding squares to current one until no longer all elements are reachable in greedy fashion
        int currentNum = 0;
        GameMap baseMap = this.copyMap();
        GroupRecord group;

        baseMap.groups = new HashMap<>();
        SavedSearch currentSet = new SavedSearch(65536);
        SavedSearch database = new SavedSearch(65536);
        long totalSize = 0;

        long currentTime = System.currentTimeMillis();

        int cutoff = EXPAND_LIMIT / 2;
        // Phase 1: Put initial points evenly across the grid
        for (int i = 0; i < rows / cutoff; i++) {
            for (int j = 0; j < cols / cutoff; j++) {
                int r = cutoff / 2 + i * cutoff;
                int c = cutoff / 2 + j * cutoff;
                if (i % 2 == 0) c += cutoff / 2;
                if (!baseMap.isWall(r, c) && baseMap.squares[r][c] == EMPTY_CHAR)    // if we're on a walkable space...
                {
                    database.clear();
                    group = expandSpot2(r, c, currentNum, baseMap, searchAlg, database, currentSet);
                    // baseMap.groups.put(group.groupId, group);
                    totalSize += group.getSize();
                    baseMap.addGroup(group.groupId, group);
                    currentNum++;
                    // 	baseMap.outputImage("tmp_map_"+currentNum+".png", null, null);
                }
                // System.out.println("Expand: i: "+i+" j: "+j);
            }
        }

        // Phase 2: Handle any other points not previously found.
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (!baseMap.isWall(i, j) && baseMap.squares[i][j] == EMPTY_CHAR) {
                    database.clear();
                    group = expandSpot2(i, j, currentNum, baseMap, searchAlg, database, currentSet);
                    // baseMap.groups.put(group.groupId, group);
                    totalSize += group.getSize();
                    baseMap.addGroup(group.groupId, group);
                    currentNum++;
                    //	baseMap.outputImage("tmp_map_"+currentNum+".png", null, null);
                    // System.out.println("Expand: i: "+i+" j: "+j);
                }
            }
        }

        baseMap.states = currentNum;
        System.out.println("Number of areas: " + currentNum + " Average area size: " + (totalSize * 1.0 / currentNum));
        long endTime = System.currentTimeMillis();
        System.out.println("Time to compute map abstraction: " + (endTime - currentTime));
        dbstat.addStat(12, endTime - currentTime);
        dbstat.addStat(11, currentNum);
        dbstat.addStat(7, currentNum);
        return baseMap;
    }

    public GameMap reachableGridAbstract(int EXPAND_LIMIT, SearchAbstractAlgorithm searchAlg, DBStatsRecord dbstat) {
        // Idea: Keep adding squares to current one until no longer all elements are reachable in greedy fashion
        int currentNum = 0;
        GameMap baseMap = this.copyMap();
        GroupRecord group;

        baseMap.groups = new HashMap<Integer, GroupRecord>();
        SavedSearch currentSet = new SavedSearch(65536);
        SavedSearch database = new SavedSearch(65536);
        long totalSize = 0;

        long currentTime = System.currentTimeMillis();

        int cutoff = EXPAND_LIMIT / 2;
        // Phase 1: Put initial points evenly across the grid
        for (int i = 0; i < rows / cutoff; i++) {
            for (int j = 0; j < cols / cutoff; j++) {
                int r = cutoff / 2 + i * cutoff;
                int c = cutoff / 2 + j * cutoff;
                if (i % 2 == 0) c += cutoff / 2;
                if (!baseMap.isWall(r, c) && baseMap.squares[r][c] == EMPTY_CHAR)    // if we're on a walkable space...
                {
                    database.clear();
                    group = expandSpot2(r, c, currentNum, baseMap, searchAlg, database, currentSet);
                    // baseMap.groups.put(group.groupId, group);
                    totalSize += group.getSize();
                    baseMap.addGroup(group.groupId, group);
                    currentNum++;
                    // 	baseMap.outputImage("tmp_map_"+currentNum+".png", null, null);
                }
                // System.out.println("Expand: i: "+i+" j: "+j);
            }
        }

        // Phase 2: Handle any other points not previously found.
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (!baseMap.isWall(i, j) && baseMap.squares[i][j] == EMPTY_CHAR) {
                    database.clear();
                    group = expandSpot2(i, j, currentNum, baseMap, searchAlg, database, currentSet);
                    // baseMap.groups.put(group.groupId, group);
                    totalSize += group.getSize();
                    baseMap.addGroup(group.groupId, group);
                    currentNum++;
                    //	baseMap.outputImage("tmp_map_"+currentNum+".png", null, null);
                    // System.out.println("Expand: i: "+i+" j: "+j);
                }
            }
        }

        baseMap.states = currentNum;
        System.out.println("Number of areas: " + currentNum + " Average area size: " + (totalSize * 1.0 / currentNum));
        long endTime = System.currentTimeMillis();
        System.out.println("Time to compute map abstraction: " + (endTime - currentTime));
        dbstat.addStat(12, endTime - currentTime);
        dbstat.addStat(11, currentNum);
        dbstat.addStat(7, currentNum);
        return baseMap;
    }

Replace Generic With Diamond HashMap<Integer,GroupRecord> with HashMap<> in method public reachableGridAbstract(EXPAND_LIMIT int, searchAlg SearchAbstractAlgorithm, dbstat DBStatsRecord) : GameMap from class map.GameMap


    /**
     * Load a scenario from a text file.
     *
     * @param fileName
     */
	/*
	public Scenario(String fileName)
	{
		Scanner sc = null;
		try
		{	sc = new Scanner(new File(fileName));

			String st = sc.nextLine();
			int numProblems = Integer.parseInt(st);
			problems = new ArrayList<Problem>(numProblems);
			String lastMapName = "";
			GameMap map=null;

			int pointOffset = 2;	//  As co-ordinates are indexed from 1 in Matlab and 1 cell padding around entire map is assumed
			for (int i=0; i < numProblems; i++)
			{	st = sc.nextLine();
				StringTokenizer tokenizer = new StringTokenizer(st);
				@SuppressWarnings("unused")
				String id = tokenizer.nextToken();
				String mapName = tokenizer.nextToken();
				if (!lastMapName.equals(mapName))
				{	map = new GameMap();
					map.load(mapName);
				}
				String start = tokenizer.nextToken();
				int idx = start.indexOf(':');
				int startRow = Integer.parseInt(start.substring(0,idx)) - pointOffset;
				int startCol = Integer.parseInt(start.substring(idx+1)) - pointOffset;
				String goal = tokenizer.nextToken();
				idx = goal.indexOf(':');
				int goalRow = Integer.parseInt(goal.substring(0,idx)) - pointOffset;
				int goalCol = Integer.parseInt(goal.substring(idx+1)) - pointOffset;
				int cost = (int) Math.round(Double.parseDouble(tokenizer.nextToken())*10);
				double difficulty = Double.parseDouble(tokenizer.nextToken());
		        Problem p = new Problem(mapName, new SearchState(map.getId(startRow, startCol)), new SearchState(map.getId(goalRow,goalCol)), cost, difficulty);
		        problems.add(p);
		        lastMapName = mapName;
			}
		}
		catch (FileNotFoundException e)
		{	System.out.println("Did not find input file: "+e);	}
		finally
		{	if (sc != null)
				sc.close();
		}
		write(fileName+"2");
	}
	*/
    public Scenario(String fileName) {
        try (Scanner sc = new Scanner(new File(fileName))) {

            String st = sc.nextLine();
            if (st.contains("version")) {    // Nathan's format: bucket number, map name, map width, map height, start x, start y, goal x, goal y, optimal length
                // Does not know number of problems beforehand
                problems = new ArrayList<Problem>(1000);
                int count = 0;
                while (sc.hasNextLine()) {
                    st = sc.nextLine();
                    if (st.equals("")) continue;
                    StringTokenizer tokenizer = new StringTokenizer(st);
                    tokenizer.nextToken();
                    String mapName = tokenizer.nextToken();
                    int mapw = Integer.parseInt(tokenizer.nextToken());
                    tokenizer.nextToken();
                    int startX = Integer.parseInt(tokenizer.nextToken());
                    int startY = Integer.parseInt(tokenizer.nextToken());
                    int startId = startY * mapw + startX;
                    int goalX = Integer.parseInt(tokenizer.nextToken());
                    int goalY = Integer.parseInt(tokenizer.nextToken());
                    int goalId = goalY * mapw + goalX;
                    int cost = (int) Math.round(Double.parseDouble(tokenizer.nextToken()) * 10);
                    Problem p = new Problem(mapName, new SearchState(startId), new SearchState(goalId), cost, 0);
                    problems.add(p);
                    count++;
                }
                System.out.println("# of problems: " + count);
            } else {    // our format
                int numProblems = Integer.parseInt(st);
                problems = new ArrayList<Problem>(numProblems);

                for (int i = 0; i < numProblems; i++) {
                    st = sc.nextLine();
                    StringTokenizer tokenizer = new StringTokenizer(st);
                    String id = tokenizer.nextToken();
                    String mapName = tokenizer.nextToken();
                    int startId = Integer.parseInt(tokenizer.nextToken());
                    int goalId = Integer.parseInt(tokenizer.nextToken());
                    int cost = (int) Math.round(Double.parseDouble(tokenizer.nextToken()) * 10);
                    double difficulty = Double.parseDouble(tokenizer.nextToken());
                    Problem p = new Problem(mapName, new SearchState(startId), new SearchState(goalId), cost, difficulty);
                    problems.add(p);
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Did not find input file: " + e);
        }
    }

    /**
     * Load a scenario from a text file.
     *
     * @param fileName
     */
	/*
	public Scenario(String fileName)
	{
		Scanner sc = null;
		try
		{	sc = new Scanner(new File(fileName));

			String st = sc.nextLine();
			int numProblems = Integer.parseInt(st);
			problems = new ArrayList<Problem>(numProblems);
			String lastMapName = "";
			GameMap map=null;

			int pointOffset = 2;	//  As co-ordinates are indexed from 1 in Matlab and 1 cell padding around entire map is assumed
			for (int i=0; i < numProblems; i++)
			{	st = sc.nextLine();
				StringTokenizer tokenizer = new StringTokenizer(st);
				@SuppressWarnings("unused")
				String id = tokenizer.nextToken();
				String mapName = tokenizer.nextToken();
				if (!lastMapName.equals(mapName))
				{	map = new GameMap();
					map.load(mapName);
				}
				String start = tokenizer.nextToken();
				int idx = start.indexOf(':');
				int startRow = Integer.parseInt(start.substring(0,idx)) - pointOffset;
				int startCol = Integer.parseInt(start.substring(idx+1)) - pointOffset;
				String goal = tokenizer.nextToken();
				idx = goal.indexOf(':');
				int goalRow = Integer.parseInt(goal.substring(0,idx)) - pointOffset;
				int goalCol = Integer.parseInt(goal.substring(idx+1)) - pointOffset;
				int cost = (int) Math.round(Double.parseDouble(tokenizer.nextToken())*10);
				double difficulty = Double.parseDouble(tokenizer.nextToken());
		        Problem p = new Problem(mapName, new SearchState(map.getId(startRow, startCol)), new SearchState(map.getId(goalRow,goalCol)), cost, difficulty);
		        problems.add(p);
		        lastMapName = mapName;
			}
		}
		catch (FileNotFoundException e)
		{	System.out.println("Did not find input file: "+e);	}
		finally
		{	if (sc != null)
				sc.close();
		}
		write(fileName+"2");
	}
	*/
    public Scenario(String fileName) {
        Scanner sc = null;
        try {
            sc = new Scanner(new File(fileName));

            String st = sc.nextLine();
            if (st.contains("version")) {    // Nathan's format: bucket number, map name, map width, map height, start x, start y, goal x, goal y, optimal length
                // Does not know number of problems before hand
                problems = new ArrayList<Problem>(1000);
                int count = 0;
                while (sc.hasNextLine()) {
                    st = sc.nextLine();
                    if (st.equals("")) continue;
                    StringTokenizer tokenizer = new StringTokenizer(st);
                    String bucketid = tokenizer.nextToken();
                    String mapName = tokenizer.nextToken();
                    int mapw = Integer.parseInt(tokenizer.nextToken());
                    int maph = Integer.parseInt(tokenizer.nextToken());
                    int startX = Integer.parseInt(tokenizer.nextToken());
                    int startY = Integer.parseInt(tokenizer.nextToken());
                    int startId = startY * mapw + startX;
                    int goalX = Integer.parseInt(tokenizer.nextToken());
                    int goalY = Integer.parseInt(tokenizer.nextToken());
                    int goalId = goalY * mapw + goalX;
                    int cost = (int) Math.round(Double.parseDouble(tokenizer.nextToken()) * 10);
                    Problem p = new Problem(mapName, new SearchState(startId), new SearchState(goalId), cost, 0);
                    problems.add(p);
                    count++;
                }
                System.out.println("# of problems: " + count);
            } else {    // our format
                int numProblems = Integer.parseInt(st);
                problems = new ArrayList<Problem>(numProblems);

                for (int i = 0; i < numProblems; i++) {
                    st = sc.nextLine();
                    StringTokenizer tokenizer = new StringTokenizer(st);
                    String id = tokenizer.nextToken();
                    String mapName = tokenizer.nextToken();
                    int startId = Integer.parseInt(tokenizer.nextToken());
                    int goalId = Integer.parseInt(tokenizer.nextToken());
                    int cost = (int) Math.round(Double.parseDouble(tokenizer.nextToken()) * 10);
                    double difficulty = Double.parseDouble(tokenizer.nextToken());
                    Problem p = new Problem(mapName, new SearchState(startId), new SearchState(goalId), cost, difficulty);
                    problems.add(p);
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Did not find input file: " + e);
        } finally {
            if (sc != null) sc.close();
        }
    }

Try With Resources try(Scanner sc=new Scanner(new File(fileName))) in method public Scenario(fileName String) from class scenario.Scenario


    /**
     * Builds a path as found by A*.
     */
    public ArrayList<SearchState> buildPath(SearchState goal, StatsRecord stats) {
        ArrayList<SearchState> path = new ArrayList<>();
        // Construct path now
        SearchState curr = goal;
        int len = 0, cost = 0;
        while (curr != null) {
            path.add(0, curr);
            if (curr.prev != null)
                //	cost += problem.computeDistance(curr, curr.prev);
                cost += problem.getMoveCost(curr, curr.prev);
            curr = curr.prev;
            len++;
        }
        stats.setPathCost(cost);
        stats.setPathLength(len);
        return path;
    }

    /**
     * Builds a path as found by A*.
     */
    public ArrayList<SearchState> buildPath(SearchState goal, StatsRecord stats) {
        ArrayList<SearchState> path = new ArrayList<SearchState>();
        // Construct path now
        SearchState curr = goal;
        int len = 0, cost = 0;
        while (curr != null) {
            path.add(0, curr);
            if (curr.prev != null)
                //	cost += problem.computeDistance(curr, curr.prev);
                cost += problem.getMoveCost(curr, curr.prev);
            curr = curr.prev;
            len++;
        }
        stats.setPathCost(cost);
        stats.setPathLength(len);
        return path;
    }

Replace Generic With Diamond ArrayList<SearchState> with ArrayList<> in method public buildPath(goal SearchState, stats StatsRecord) : ArrayList<SearchState> from class search.AStar


    /**
     * Builds a path as found by A*.
     */
    public ArrayList<SearchState> buildPath(SearchState goal, StatsRecord stats) {
        ArrayList<SearchState> path = new ArrayList<>();
        // Construct path now
        SearchState curr = goal;
        int len = 0, cost = 0;
        while (curr != null) {
            path.add(0, curr);
            if (curr.prev != null)
                //	cost += problem.computeDistance(curr, curr.prev);
                //  cost += problem.computeDistance(curr, curr.prev, heuristic);
                cost += problem.getMoveCost(curr, curr.prev);
            curr = curr.prev;
            len++;
        }
        stats.setPathCost(cost);
        stats.setPathLength(len);
        return path;
    }

    /**
     * Builds a path as found by A*.
     */
    public ArrayList<SearchState> buildPath(SearchState goal, StatsRecord stats) {
        ArrayList<SearchState> path = new ArrayList<SearchState>();
        // Construct path now
        SearchState curr = goal;
        int len = 0, cost = 0;
        while (curr != null) {
            path.add(0, curr);
            if (curr.prev != null)
                //	cost += problem.computeDistance(curr, curr.prev);
                //  cost += problem.computeDistance(curr, curr.prev, heuristic);
                cost += problem.getMoveCost(curr, curr.prev);
            curr = curr.prev;
            len++;
        }
        stats.setPathCost(cost);
        stats.setPathLength(len);
        return path;
    }

Replace Generic With Diamond ArrayList<SearchState> with ArrayList<> in method public buildPath(goal SearchState, stats StatsRecord) : ArrayList<SearchState> from class search.AStarHeuristic


    public ArrayList<SearchState> computePath(SearchState start, SearchState goal, StatsRecord stats) {
        boolean OPTIMIZE_START_DB_PATH = true;
        boolean OPTIMIZE_END_DB_PATH = true;
        boolean OPTIMIZE_CHECK = true;
        ArrayList<SubgoalDBRecord> used = new ArrayList<>();
        ArrayList<SearchState> path, newPath;
        SearchState currentGoal, currentStart;
        SubgoalDBRecord currentRecord = null;
        int currentIndex = 0;
        int[] subgoalList = null;
        boolean startOptimizationDone = false;

        subgoals.clear();

        long startTime, endTime, startTime2;
        startTime = System.nanoTime();
        // path = null;
        path = subgoalSearchAlg.computePath(start, goal, stats);
        // path = optCheckSearchAlg.computePath(start, goal, stats);
        if (path != null) {    // System.out.println("Global goal is HC reachable from start.");
            subgoals.add(goal);
            SearchUtil.computePathCost(path, stats, problem);
            stats.updateMaxTime(System.nanoTime() - startTime);
            return path;        // Search is done using only hill climbing
        }

        endTime = System.nanoTime();
        // System.out.println("Time for HC reachable check at start: "+(endTime-startTime));

        path = new ArrayList<SearchState>();
        currentGoal = goal;        // Default to global goal by default
        currentStart = start;

        // Search the database for records
        startTime2 = System.nanoTime();
        ArrayList<SubgoalDBRecord> records = database.findNearest(problem, start, goal, subgoalSearchAlg, 1, stats, null);
        endTime = System.nanoTime();
        // System.out.println("Time for database lookup: "+(endTime-startTime2));

        if (records.size() >= 1) {
            currentRecord = records.get(0);
            used.add(currentRecord);
            // System.out.println("Using subgoal record from database: "+currentRecord.toString(problem));
            // System.out.println("Using subgoal record from database: "+currentRecord.getId());
            subgoalList = currentRecord.getSubgoalList();
            currentIndex = -1;
            if (OPTIMIZE_START_DB_PATH) {    // Try to hill-climb to first subgoal instead of database goal start as will improve optimality

                if (subgoalList == null || subgoalList.length == 0) {
                    currentGoal = new SearchState(currentRecord.getStartId());
                    subgoals.add(currentGoal);
                    startOptimizationDone = true;
                } else {
                    currentGoal = new SearchState(subgoalList[0]);
                    newPath = optCheckSearchAlg.computePath(currentStart, currentGoal, stats);
                    if (newPath != null) {    // Possible to hill climb to subgoal
                        currentIndex = 1;
                        startOptimizationDone = true;
                        currentStart = currentGoal;
                        subgoals.add(currentGoal);
                        path = SearchUtil.mergePaths(path, newPath);
                        // System.out.println("Start optimization successful.  Currently at: "+problem.idToString(currentGoal.id));
                        // Advance to next subgoal.  Note if only one have to potentially do END PATH optimization as well
                        if (currentIndex >= subgoalList.length) {    // Default is to go to goal of database record then we will hill climb from there.
                            currentGoal = new SearchState(currentRecord.getGoalId());

                            if (OPTIMIZE_END_DB_PATH) {    // newPath = AStar.reachableHillClimbing(map, currentStart, goal, cutoff, stats);
                                newPath = optCheckSearchAlg.computePath(currentStart, goal, stats);
                                if (newPath != null) {    // From last subgoal spot can reach global goal using hill climbing.  That completes the path.
                                    // Trying to go to the global goal from the last subgoal may not work but will often be shorter than going to the database goal point then our desired goal.
                                    path = SearchUtil.mergePaths(path, newPath);
                                    SearchUtil.computePathCost(path, stats, problem);
                                    // System.out.println("End optimization successful from: "+problem.idToString(currentStart.id));
                                    stats.updateMaxTime(System.nanoTime() - startTime);
                                    return path;
                                }
                            }
                        } // Ran out of subgoals in path optimization
                        else {    // Use next subgoal in list
                            currentGoal = new SearchState(subgoalList[1]);
                            subgoals.add(currentGoal);
                        } // Using next subgoal
                    } // Able to directly hill climb to first subgoal of database record from our starting point
                } // No subgoals
            } // optimizing start of db path

            if (!startOptimizationDone && OPTIMIZE_CHECK) {    // Unable to hill climb to first subgoal - try to hill climb somewhere between start and first subgoal
                // Build path from start of record to first subgoal
                subgoalList = currentRecord.getSubgoalList();
                if (subgoalList != null && subgoalList.length > 0) {
                    newPath = subgoalSearchAlg.computePath(new SearchState(currentRecord.getStartId()), new SearchState(subgoalList[0]), stats);
                    // SearchUtil.printPath(problem, newPath);
                    // Now find farthest spot on path that is HC-reachable
                    currentGoal = SearchUtil.computeBinaryReachable(newPath, optCheckSearchAlg, currentStart, stats);
                    //	System.out.println("Binary start optimization successful.  Starting point: "+problem.idToString(currentGoal.id)+" Index in path: "+newPath.indexOf(currentGoal));
                    startOptimizationDone = true;
                }
            }

            if (!startOptimizationDone) {    // If no start optimization succeeds, go to record start
                currentGoal = new SearchState(currentRecord.getStartId());
                subgoals.add(currentGoal);
            }
        } else {
            System.out.println("DATABASE RECORD NOT FOUND.");
        }

        endTime = System.nanoTime();
        // System.out.println("Total Time for processing first step: "+(endTime-startTime));
        stats.updateMaxTime(endTime - startTime);

        // IDEA: Once have one DB record follow it all the way to goal using one subgoal at a time.
        // Use only hill climbing first if possible.
        // If exhausted all subgoals, then go to global goal if it is HC reachable from last subgoal.  Otherwise, go to goal of DB record then to our desired goal.

        SearchUtil.computePathCost(path, stats, problem);        // Compute path costs up to this point

        while (true) {

            newPath = subgoalSearchAlg.computePath(currentStart, currentGoal, stats);

            if (newPath == null) {    // TODO: Not sure what to do in this case
                if (currentGoal == goal) {    // Special case to handle situation where did not guarantee HC-reachability in both directions
                    System.out.println("WARNING: Unable to reach goal " + problem.idToString(currentStart.id) + " from currentGoal: " + problem.idToString(currentGoal.id));
                    System.out.println("Finding reverse path.");
                    newPath = subgoalSearchAlg.computePath(currentGoal, currentStart, stats);
                    if (newPath == null) {
                        System.out.println("CRITICAL ERROR: Reverse path search failed.");
                        return null;
                    }
                    path = SearchUtil.mergeReversePaths(path, newPath);
                    SearchUtil.computePathCost(path, stats, problem);
                    System.out.println("Reverse path search successful");
                    return path;
                }
                System.out.println("Unable to find subgoal path between " + problem.idToString(currentStart.id) + " and " + problem.idToString(currentGoal.id));
                currentGoal = goal;
                //System.exit(1);
                return null;
            }

            path = SearchUtil.mergePaths(path, newPath);

            SearchState curr = newPath.get(newPath.size() - 1);
            if (curr.equals(goal)) break;

            if (!curr.equals(currentGoal)) {    // Must have been interrupted
                int times = cutoff;
                if (times > 100) times = 100;

                currentStart = newPath.get(newPath.size() - 1);
                System.out.println("Detect interruption.  Trying database lookup at: " + currentStart);
                System.out.println("Length of path at interrupt: " + path.size());
                records = database.findNearest(problem, currentStart, goal, subgoalSearchAlg, 1, stats, null);
                if (records.size() >= 1) {
                    currentRecord = records.get(0);
                    subgoalList = currentRecord.getSubgoalList();
                    used.add(currentRecord);
                    // System.out.println("Using subgoal record from database: "+currentRecord.toString());
                    currentIndex = -1;
                    // Always go for start
                    currentGoal = new SearchState(currentRecord.getStartId());
                } else currentGoal = goal;

                continue;
            }

            if (currentGoal == goal) break;

            currentStart = currentGoal;
            currentIndex++;
            if (subgoalList == null) {
                if (currentGoal.id == currentRecord.getGoalId()) currentGoal = goal;
                else currentGoal = new SearchState(currentRecord.getGoalId());
            } else if (currentIndex == subgoalList.length) {
                // Default is to go to goal of database record then we will hill climb from there.
                currentGoal = new SearchState(currentRecord.getGoalId());
                startTime2 = System.nanoTime();

                if (OPTIMIZE_END_DB_PATH) {    // System.out.println("End optimization check from: "+problem.idToString(currentStart.id)+" to "+problem.idToString(goal.id));
                    newPath = optCheckSearchAlg.computePath(currentStart, goal, stats);
                    if (newPath != null) {    // From last subgoal spot can reach global goal using hill climbing.  That completes the path.
                        // Trying to go to the global goal from the last subgoal may not work but will often be shorter than going to the database goal point then our desired goal.
                        path = SearchUtil.mergePaths(path, newPath);
                        SearchUtil.computePathCost(path, stats, problem);
                        // System.out.println("End optimization successful from: "+problem.idToString(currentStart.id));
                        endTime = System.nanoTime();
                        // System.out.println("Time to do end check: "+(endTime-startTime2));
                        stats.updateMaxTime(endTime - startTime2);
                        return path;
                    } else {
                        if (OPTIMIZE_CHECK) {    // Unable to hill climb to goal from last subgoal - try to hill climb somewhere between last subgoal and end to global goal
                            // Build path from a node between last subgoal (current location) and end of record state to goal
                            subgoalList = currentRecord.getSubgoalList();
                            if (subgoalList != null && subgoalList.length > 0) {
                                newPath = subgoalSearchAlg.computePath(currentStart, new SearchState(currentRecord.getGoalId()), stats);
                                // SearchUtil.printPath(problem, newPath);
                                // Now find farthest spot on path that can HC-reach the global goal
                                SearchState tmp = SearchUtil.computeBinaryReachableFrom(newPath, optCheckSearchAlg, goal, stats);
                                if (tmp == null) {
                                    System.out.println("WARNING: Unable to reach goal during end optimization check: " + problem.idToString(goal.id));
                                    System.out.println("Computing path to end of record.");
                                    currentGoal = new SearchState(currentRecord.getGoalId());
                                    newPath = subgoalSearchAlg.computePath(currentStart, currentGoal, stats);
                                    path = SearchUtil.mergePaths(path, newPath);
                                    System.out.println("Finding reverse path.");
                                    newPath = subgoalSearchAlg.computePath(goal, currentGoal, stats);
                                    if (newPath == null) {
                                        System.out.println("CRITICAL ERROR: Reverse path search failed.");
                                        return null;
                                    }
                                    path = SearchUtil.mergeReversePaths(path, newPath);
                                    SearchUtil.computePathCost(path, stats, problem);
                                    endTime = System.nanoTime();
                                    System.out.println("Time to do end check: " + (endTime - startTime2));
                                    stats.updateMaxTime(endTime - startTime2);
                                    System.out.println("Reverse path search successful");
                                    return path;
                                }
                                currentGoal = tmp;

								/*
								if (!subgoalSearchAlg.isPath(currentStart.id, currentGoal.id, stats) || !subgoalSearchAlg.isPath(currentGoal.id, goal.id, stats))
								{
									System.out.println("HERE");
								}
								*/
                                System.out.println("Binary end optimization successful.  Starting point: " + problem.idToString(currentGoal.id) + " Index in path: " + newPath.indexOf(currentGoal));
                                // Now build rest of path.  Note that may not be able to HC from a subgoal to any arbitrary state between subgoals, so compute the path between them then go from found state.
                                int i = newPath.indexOf(currentGoal) + 1;
                                int count = newPath.size() - i;
                                for (int k = 0; k < count; k++)
                                    newPath.remove(i);            // Remove rest of path not needed
                                // SearchUtil.printPath(problem, newPath);
                                path = SearchUtil.mergePaths(path, newPath);
                                newPath = subgoalSearchAlg.computePath(currentGoal, goal, stats);            // Get to global goal
                                path = SearchUtil.mergePaths(path, newPath);
                                SearchUtil.computePathCost(path, stats, problem);
                                endTime = System.nanoTime();
                                // System.out.println("Time to do end check: "+(endTime-startTime2));
                                stats.updateMaxTime(endTime - startTime2);
                                return path;
                            }
                        }

                    }
                }
            } else if (currentIndex < subgoalList.length) {
                currentGoal = new SearchState(subgoalList[currentIndex]);
                subgoals.add(currentGoal);
            } else currentGoal = goal;            // Go towards global goal
        }

        SearchUtil.computePathCost(path, stats, problem);
        return path;

    }

    public ArrayList<SearchState> computePath(SearchState start, SearchState goal, StatsRecord stats) {
        boolean OPTIMIZE_START_DB_PATH = true;
        boolean OPTIMIZE_END_DB_PATH = true;
        boolean OPTIMIZE_CHECK = true;
        ArrayList<SubgoalDBRecord> used = new ArrayList<SubgoalDBRecord>();
        ArrayList<SearchState> path, newPath;
        SearchState currentGoal, currentStart;
        SubgoalDBRecord currentRecord = null;
        int currentIndex = 0;
        int[] subgoalList = null;
        boolean startOptimizationDone = false;

        subgoals.clear();

        long startTime, endTime, startTime2;
        startTime = System.nanoTime();
        // path = null;
        path = subgoalSearchAlg.computePath(start, goal, stats);
        // path = optCheckSearchAlg.computePath(start, goal, stats);
        if (path != null) {    // System.out.println("Global goal is HC reachable from start.");
            subgoals.add(goal);
            SearchUtil.computePathCost(path, stats, problem);
            stats.updateMaxTime(System.nanoTime() - startTime);
            return path;        // Search is done using only hill climbing
        }

        endTime = System.nanoTime();
        // System.out.println("Time for HC reachable check at start: "+(endTime-startTime));

        path = new ArrayList<SearchState>();
        currentGoal = goal;        // Default to global goal by default
        currentStart = start;

        // Search the database for records
        startTime2 = System.nanoTime();
        ArrayList<SubgoalDBRecord> records = database.findNearest(problem, start, goal, subgoalSearchAlg, 1, stats, null);
        endTime = System.nanoTime();
        // System.out.println("Time for database lookup: "+(endTime-startTime2));

        if (records.size() >= 1) {
            currentRecord = records.get(0);
            used.add(currentRecord);
            // System.out.println("Using subgoal record from database: "+currentRecord.toString(problem));
            // System.out.println("Using subgoal record from database: "+currentRecord.getId());
            subgoalList = currentRecord.getSubgoalList();
            currentIndex = -1;
            if (OPTIMIZE_START_DB_PATH) {    // Try to hill-climb to first subgoal instead of database goal start as will improve optimality

                if (subgoalList == null || subgoalList.length == 0) {
                    currentGoal = new SearchState(currentRecord.getStartId());
                    subgoals.add(currentGoal);
                    startOptimizationDone = true;
                } else {
                    currentGoal = new SearchState(subgoalList[0]);
                    newPath = optCheckSearchAlg.computePath(currentStart, currentGoal, stats);
                    if (newPath != null) {    // Possible to hill climb to subgoal
                        currentIndex = 1;
                        startOptimizationDone = true;
                        currentStart = currentGoal;
                        subgoals.add(currentGoal);
                        path = SearchUtil.mergePaths(path, newPath);
                        // System.out.println("Start optimization successful.  Currently at: "+problem.idToString(currentGoal.id));
                        // Advance to next subgoal.  Note if only one have to potentially do END PATH optimization as well
                        if (currentIndex >= subgoalList.length) {    // Default is to go to goal of database record then we will hill climb from there.
                            currentGoal = new SearchState(currentRecord.getGoalId());

                            if (OPTIMIZE_END_DB_PATH) {    // newPath = AStar.reachableHillClimbing(map, currentStart, goal, cutoff, stats);
                                newPath = optCheckSearchAlg.computePath(currentStart, goal, stats);
                                if (newPath != null) {    // From last subgoal spot can reach global goal using hill climbing.  That completes the path.
                                    // Trying to go to the global goal from the last subgoal may not work but will often be shorter than going to the database goal point then our desired goal.
                                    path = SearchUtil.mergePaths(path, newPath);
                                    SearchUtil.computePathCost(path, stats, problem);
                                    // System.out.println("End optimization successful from: "+problem.idToString(currentStart.id));
                                    stats.updateMaxTime(System.nanoTime() - startTime);
                                    return path;
                                }
                            }
                        } // Ran out of subgoals in path optimization
                        else {    // Use next subgoal in list
                            currentGoal = new SearchState(subgoalList[1]);
                            subgoals.add(currentGoal);
                        } // Using next subgoal
                    } // Able to directly hill climb to first subgoal of database record from our starting point
                } // No subgoals
            } // optimizing start of db path

            if (!startOptimizationDone && OPTIMIZE_CHECK) {    // Unable to hill climb to first subgoal - try to hill climb somewhere between start and first subgoal
                // Build path from start of record to first subgoal
                subgoalList = currentRecord.getSubgoalList();
                if (subgoalList != null && subgoalList.length > 0) {
                    newPath = subgoalSearchAlg.computePath(new SearchState(currentRecord.getStartId()), new SearchState(subgoalList[0]), stats);
                    // SearchUtil.printPath(problem, newPath);
                    // Now find farthest spot on path that is HC-reachable
                    currentGoal = SearchUtil.computeBinaryReachable(newPath, optCheckSearchAlg, currentStart, stats);
                    //	System.out.println("Binary start optimization successful.  Starting point: "+problem.idToString(currentGoal.id)+" Index in path: "+newPath.indexOf(currentGoal));
                    startOptimizationDone = true;
                }
            }

            if (!startOptimizationDone) {    // If no start optimization succeeds, go to record start
                currentGoal = new SearchState(currentRecord.getStartId());
                subgoals.add(currentGoal);
            }
        } else {
            System.out.println("DATABASE RECORD NOT FOUND.");
        }

        endTime = System.nanoTime();
        // System.out.println("Total Time for processing first step: "+(endTime-startTime));
        stats.updateMaxTime(endTime - startTime);

        // IDEA: Once have one DB record follow it all the way to goal using one subgoal at a time.
        // Use only hill climbing first if possible.
        // If exhausted all subgoals, then go to global goal if it is HC reachable from last subgoal.  Otherwise, go to goal of DB record then to our desired goal.

        SearchUtil.computePathCost(path, stats, problem);        // Compute path costs up to this point

        while (true) {

            newPath = subgoalSearchAlg.computePath(currentStart, currentGoal, stats);

            if (newPath == null) {    // TODO: Not sure what to do in this case
                if (currentGoal == goal) {    // Special case to handle situation where did not guarantee HC-reachability in both directions
                    System.out.println("WARNING: Unable to reach goal " + problem.idToString(currentStart.id) + " from currentGoal: " + problem.idToString(currentGoal.id));
                    System.out.println("Finding reverse path.");
                    newPath = subgoalSearchAlg.computePath(currentGoal, currentStart, stats);
                    if (newPath == null) {
                        System.out.println("CRITICAL ERROR: Reverse path search failed.");
                        return null;
                    }
                    path = SearchUtil.mergeReversePaths(path, newPath);
                    SearchUtil.computePathCost(path, stats, problem);
                    System.out.println("Reverse path search successful");
                    return path;
                }
                System.out.println("Unable to find subgoal path between " + problem.idToString(currentStart.id) + " and " + problem.idToString(currentGoal.id));
                currentGoal = goal;
                //System.exit(1);
                return null;
            }

            path = SearchUtil.mergePaths(path, newPath);

            SearchState curr = newPath.get(newPath.size() - 1);
            if (curr.equals(goal)) break;

            if (!curr.equals(currentGoal)) {    // Must have been interrupted
                int times = cutoff;
                if (times > 100) times = 100;

                currentStart = newPath.get(newPath.size() - 1);
                System.out.println("Detect interruption.  Trying database lookup at: " + currentStart);
                System.out.println("Length of path at interrupt: " + path.size());
                records = database.findNearest(problem, currentStart, goal, subgoalSearchAlg, 1, stats, null);
                if (records.size() >= 1) {
                    currentRecord = records.get(0);
                    subgoalList = currentRecord.getSubgoalList();
                    used.add(currentRecord);
                    // System.out.println("Using subgoal record from database: "+currentRecord.toString());
                    currentIndex = -1;
                    // Always go for start
                    currentGoal = new SearchState(currentRecord.getStartId());
                } else currentGoal = goal;

                continue;
            }

            if (currentGoal == goal) break;

            currentStart = currentGoal;
            currentIndex++;
            if (subgoalList == null) {
                if (currentGoal.id == currentRecord.getGoalId()) currentGoal = goal;
                else currentGoal = new SearchState(currentRecord.getGoalId());
            } else if (currentIndex == subgoalList.length) {
                // Default is to go to goal of database record then we will hill climb from there.
                currentGoal = new SearchState(currentRecord.getGoalId());
                startTime2 = System.nanoTime();

                if (OPTIMIZE_END_DB_PATH) {    // System.out.println("End optimization check from: "+problem.idToString(currentStart.id)+" to "+problem.idToString(goal.id));
                    newPath = optCheckSearchAlg.computePath(currentStart, goal, stats);
                    if (newPath != null) {    // From last subgoal spot can reach global goal using hill climbing.  That completes the path.
                        // Trying to go to the global goal from the last subgoal may not work but will often be shorter than going to the database goal point then our desired goal.
                        path = SearchUtil.mergePaths(path, newPath);
                        SearchUtil.computePathCost(path, stats, problem);
                        // System.out.println("End optimization successful from: "+problem.idToString(currentStart.id));
                        endTime = System.nanoTime();
                        // System.out.println("Time to do end check: "+(endTime-startTime2));
                        stats.updateMaxTime(endTime - startTime2);
                        return path;
                    } else {
                        if (OPTIMIZE_CHECK) {    // Unable to hill climb to goal from last subgoal - try to hill climb somewhere between last subgoal and end to global goal
                            // Build path from a node between last subgoal (current location) and end of record state to goal
                            subgoalList = currentRecord.getSubgoalList();
                            if (subgoalList != null && subgoalList.length > 0) {
                                newPath = subgoalSearchAlg.computePath(currentStart, new SearchState(currentRecord.getGoalId()), stats);
                                // SearchUtil.printPath(problem, newPath);
                                // Now find farthest spot on path that can HC-reach the global goal
                                SearchState tmp = SearchUtil.computeBinaryReachableFrom(newPath, optCheckSearchAlg, goal, stats);
                                if (tmp == null) {
                                    System.out.println("WARNING: Unable to reach goal during end optimization check: " + problem.idToString(goal.id));
                                    System.out.println("Computing path to end of record.");
                                    currentGoal = new SearchState(currentRecord.getGoalId());
                                    newPath = subgoalSearchAlg.computePath(currentStart, currentGoal, stats);
                                    path = SearchUtil.mergePaths(path, newPath);
                                    System.out.println("Finding reverse path.");
                                    newPath = subgoalSearchAlg.computePath(goal, currentGoal, stats);
                                    if (newPath == null) {
                                        System.out.println("CRITICAL ERROR: Reverse path search failed.");
                                        return null;
                                    }
                                    path = SearchUtil.mergeReversePaths(path, newPath);
                                    SearchUtil.computePathCost(path, stats, problem);
                                    endTime = System.nanoTime();
                                    System.out.println("Time to do end check: " + (endTime - startTime2));
                                    stats.updateMaxTime(endTime - startTime2);
                                    System.out.println("Reverse path search successful");
                                    return path;
                                }
                                currentGoal = tmp;

								/*
								if (!subgoalSearchAlg.isPath(currentStart.id, currentGoal.id, stats) || !subgoalSearchAlg.isPath(currentGoal.id, goal.id, stats))
								{
									System.out.println("HERE");
								}
								*/
                                System.out.println("Binary end optimization successful.  Starting point: " + problem.idToString(currentGoal.id) + " Index in path: " + newPath.indexOf(currentGoal));
                                // Now build rest of path.  Note that may not be able to HC from a subgoal to any arbitrary state between subgoals, so compute the path between them then go from found state.
                                int i = newPath.indexOf(currentGoal) + 1;
                                int count = newPath.size() - i;
                                for (int k = 0; k < count; k++)
                                    newPath.remove(i);            // Remove rest of path not needed
                                // SearchUtil.printPath(problem, newPath);
                                path = SearchUtil.mergePaths(path, newPath);
                                newPath = subgoalSearchAlg.computePath(currentGoal, goal, stats);            // Get to global goal
                                path = SearchUtil.mergePaths(path, newPath);
                                SearchUtil.computePathCost(path, stats, problem);
                                endTime = System.nanoTime();
                                // System.out.println("Time to do end check: "+(endTime-startTime2));
                                stats.updateMaxTime(endTime - startTime2);
                                return path;
                            }
                        }

                    }
                }
            } else if (currentIndex < subgoalList.length) {
                currentGoal = new SearchState(subgoalList[currentIndex]);
                subgoals.add(currentGoal);
            } else currentGoal = goal;            // Go towards global goal
        }

        SearchUtil.computePathCost(path, stats, problem);
        return path;

    }

Replace Generic With Diamond ArrayList<SubgoalDBRecord> with ArrayList<> in method public computePath(start SearchState, goal SearchState, stats StatsRecord) : ArrayList<SearchState> from class search.SubgoalSearch


    public static int countRevisits(ArrayList<SearchState> path) {
        if (path == null)
            return 0;
        else {
            HashMap<Integer, Integer> states = new HashMap<>(path.size());
            int revisits = 0;
            for (SearchState searchState : path) {
                int id = searchState.id;
                if (states.containsKey(id))
                    revisits++;
                else
                    states.put(id, id);
            }
            return revisits;
        }
    }

    public static int countRevisits(ArrayList<SearchState> path) {
        if (path == null)
            return 0;
        else {
            HashMap<Integer, Integer> states = new HashMap<Integer, Integer>(path.size());
            int revisits = 0;
            for (SearchState searchState : path) {
                int id = searchState.id;
                if (states.containsKey(id))
                    revisits++;
                else
                    states.put(id, id);
            }
            return revisits;
        }
    }

Replace Generic With Diamond HashMap<Integer,Integer> with HashMap<> in method public countRevisits(path ArrayList<SearchState>) : int from class search.SearchUtil


    public void load(String fileName) {
        try (Scanner sc = new Scanner(new File(fileName))) {

            String st = sc.nextLine();    // Drop first line which is format
            if (!st.contains("type")) {    // Map is in binary format
                sc.close();
                this.loadMap(fileName);
                return;
            }

            st = sc.nextLine();            // Number of rows. e.g. height 139
            rows = Integer.parseInt(st.substring(7).trim());
            st = sc.nextLine();            // Number of cols. e.g. width 148
            cols = Integer.parseInt(st.substring(6).trim());
            st = sc.nextLine();            // Just says map
            squares = new int[rows][cols];
            mapInit();
            states = 0;

            for (int i = 0; i < rows; i++) {
                st = sc.nextLine();
                for (int j = 0; j < cols; j++) {
                    if (st.charAt(j) == '@' || st.charAt(j) == 'O' || st.charAt(j) == 'W' || st.charAt(j) == 'T')
                        squares[i][j] = WALL_CHAR;
                    else {
                        squares[i][j] = EMPTY_CHAR;
                        states++;
                    }
                }
            }
        } catch (FileNotFoundException e) {
            System.out.println("Did not find input file: " + e);
        }
    }

	public void load(String fileName)
	{
		Scanner sc = null;
		try
		{	sc = new Scanner(new File(fileName));

			String st = sc.nextLine();	// Drop first line which is format
			if (!st.contains("type"))
			{	// Map is in binary format
				sc.close();
				this.loadMap(fileName);
				return;
			}

			st = sc.nextLine();			// Number of rows. e.g. height 139
			rows = Integer.parseInt(st.substring(7).trim());
			st = sc.nextLine();			// Number of cols. e.g. width 148
			cols = Integer.parseInt(st.substring(6).trim());
			st = sc.nextLine();			// Just says map
			squares = new int[rows][cols];
			mapInit();
			states = 0;

			for (int i=0; i < rows; i++)
			{	st = sc.nextLine();
		        for (int j=0; j < cols; j++)
		        {
		        	if (st.charAt(j) == '@' || st.charAt(j) == 'O' || st.charAt(j) == 'W' || st.charAt(j) == 'T')
		        		squares[i][j] = WALL_CHAR;
		        	else
		        	{	squares[i][j] = EMPTY_CHAR;
		        		states++;
		        	}
		        }
			}
		}
		catch (FileNotFoundException e)
		{	System.out.println("Did not find input file: "+e);	}
		finally
		{	if (sc != null)
				sc.close();
		}
	}


	/**
	 * This version loads a number-based map.
	 * @param fileName
	 */
	public void loadMap(String fileName)
	{
		Scanner sc = null;
		try
		{	sc = new Scanner(new File(fileName));

			String st = sc.nextLine();			// Number of rows. e.g. height 139
			rows = Integer.parseInt(st.substring(7).trim());
			st = sc.nextLine();			// Number of cols. e.g. width 148
			cols = Integer.parseInt(st.substring(6).trim());
			squares = new int[rows][cols];
			mapInit();
			states = 0;

			for (int i=0; i < rows; i++)
			{
		        for (int j=0; j < cols; j++)
		        {
		        	squares[i][j] = sc.nextInt();
		        }
			}
		}
		catch (FileNotFoundException e)
		{	System.out.println("Did not find input file: "+e);	}
		catch (Exception e)
		{	System.out.println("IO Error: "+e);	}
		finally
		{	if (sc != null)
				sc.close();
		}
	}


	public void save(String fileName)
	{
		PrintWriter out = null;
		try
		{
			out = new PrintWriter(fileName);
			out.println("height "+rows);
			out.println("width "+cols);

			for (int i=0; i < rows; i++)
			{
				for (int j=0; j < cols; j++)
				{	out.print(this.squares[i][j]);
					out.print("\t");
				}
				out.println();
			}
		}
		catch (FileNotFoundException e)
		{	System.out.println("Error with output file: "+e);	}
		finally
		{	if (out != null)
				out.close();
		}
	}


    /**
     * Start the Logger, only one logger Thread can exist
     *
     * @param debugMode debugMode of {@link TYPE}
     * @param os        {@link OutputStream} or null if {@link System#out} should be used
     */
    public synchronized void start(TYPE debugMode, @Nullable OutputStream os) {
        if (!running) {
            this.debugMode = debugMode;
            if (os == null) {
                os = System.out;
            }
            OutputStream finalOs = os;
            Thread t = new Thread(() -> {
                while (running) {
                    try {
                        LogMessage log = logQueue.poll(100, TimeUnit.MILLISECONDS);
                        if (log != null) {
                            SimpleDateFormat formatter = new SimpleDateFormat("[H:mm:ss] ");
                            Date date = new Date();
                            StringBuilder output = new StringBuilder(formatter.format(date));
                            switch (log.type) {
                                case NONE -> throw new IllegalStateException("Message of Type " + TYPE.NONE + " should not exist!");
                                case INFO -> output.append("[INFO]: ");
                                case DEBUG -> output.append("[DEBUG]: ");
                                case WARNING -> output.append("[WARNING]: ");
                                case ERROR -> output.append("[ERROR]: ");
                            }
                            output.append(log.message);
                            output.append(System.lineSeparator());
                            finalOs.write(output.toString().getBytes(StandardCharsets.UTF_8));
                        }
                    } catch (InterruptedException | IOException e) {
                        e.printStackTrace();
                    }
                }
                // cleanup
                try {
                    finalOs.flush();
                    finalOs.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
            t.setName("Logger Thread");
            running = true;
            t.start();
        } else {
            throw new IllegalStateException("Logger already Running");
        }
    }

    /**
     * Start the Logger, only one logger Thread can exist
     *
     * @param debugMode debugMode of {@link TYPE}
     * @param os        {@link OutputStream} or null if {@link System#out} should be used
     */
    public synchronized void start(TYPE debugMode, @Nullable OutputStream os) {
        if (!running) {
            this.debugMode = debugMode;
            if (os == null) {
                os = System.out;
            }
            OutputStream finalOs = os;
            Thread t = new Thread() {
                @Override
                public void run() {
                    while (running) {
                        try {
                            LogMessage log = logQueue.poll(100, TimeUnit.MILLISECONDS);
                            if (log != null) {
                                SimpleDateFormat formatter = new SimpleDateFormat("[H:mm:ss] ");
                                Date date = new Date();
                                StringBuilder output = new StringBuilder(formatter.format(date));
                                switch (log.type) {
                                    case NONE -> throw new IllegalStateException("Message of Type " + TYPE.NONE + " should not exist!");
                                    case INFO -> output.append("[INFO]: ");
                                    case DEBUG -> output.append("[DEBUG]: ");
                                    case WARNING -> output.append("[WARNING]: ");
                                    case ERROR -> output.append("[ERROR]: ");
                                }
                                output.append(log.message);
                                output.append(System.lineSeparator());
                                finalOs.write(output.toString().getBytes(StandardCharsets.UTF_8));
                            }
                        } catch (InterruptedException | IOException e) {
                            e.printStackTrace();
                        }
                    }
                    // cleanup
                    try {
                        finalOs.flush();
                        finalOs.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };
            t.setName("Logger Thread");
            running = true;
            t.start();
        } else {
            throw new IllegalStateException("Logger already Running");
        }
    }

Replace Anonymous With Lambda logger.Logger.start.t.new Thread with () -> {while(running){tryLogMessage log=logQueue.poll(100,TimeUnit.MILLISECONDS); in method public start(debugMode TYPE, os OutputStream) : void from class logger.Logger


    private void createButtonListeners() {
        addPieceIconsToButtons();

        ActionListener buttonListener = e -> {
            JButton clickedButton = (JButton) e.getSource();

            String coordinate = clickedButton.getName();
            if (coordinate == null)
                return;

            Position position = CoordinateTransformer.stringToPosition(coordinate);
            Piece piece = board.getPiece(position.getFile(), position.getRow());

            if (piece != null)
                highlightPossibleMoves(piece.getPossibleMoves());
        };

        for (JButton button : panel.getBoardButtons())
            button.addActionListener(buttonListener);
    }

    private void createButtonListeners() {
        addImageButton();

        ActionListener buttonListener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JButton clickedButton = (JButton) e.getSource();

                String coordinate = clickedButton.getName();
                if (coordinate == null)
                    return;

                Position test = CoordinateTransformer.stringToPosition(coordinate);
                int test2 = coordinate.charAt(1) + '0';

                Piece piece = board.getPiece(test.getFile(), test.getRow());

                if (piece != null)
                    setColorPossibleMovePiece(piece.getPossibleMoves());
            }
        };

        for (JButton button : panel.getAllBoardButtons())
            button.addActionListener(buttonListener);
    }

Replace Anonymous With Lambda ui.GameRolls.createButtonListeners.buttonListener.new ActionListener with e -> {JButton clickedButton=(JButton)e.getSource(); in method private createButtonListeners() : void from class ui.ChessGameUIController


    public ArrayList<Integer> transformPosition2(String str){
        ArrayList<Integer> position = new ArrayList<>();
        int column;
        int rank;
        column = switch (str.charAt(0)) {
            case 'a' -> 0;
            case 'b' -> 1;
            case 'c' -> 2;
            case 'd' -> 3;
            case 'e' -> 4;
            case 'f' -> 5;
            case 'g' -> 6;
            case 'h' -> 7;
            default ->  9;
        };
        position.add(column);

        rank = switch (str.charAt(1)) {
            case '1' -> 0;
            case '2' -> 1;
            case '3' -> 2;
            case '4' -> 3;
            case '5' -> 4;
            case '6' -> 5;
            case '7' -> 6;
            case '8' -> 7;
            default ->  9;
        };
        position.add(rank);

        return  position;
    }

    public ArrayList<Integer> transformPosition2(String str){
        ArrayList<Integer> position = new ArrayList<Integer>();
        int column;
        int rank;
        column = switch (str.charAt(0)) {
            case 'a' -> 0;
            case 'b' -> 1;
            case 'c' -> 2;
            case 'd' -> 3;
            case 'e' -> 4;
            case 'f' -> 5;
            case 'g' -> 6;
            case 'h' -> 7;
            default ->  9;
        };
        position.add(column);

        rank = switch (str.charAt(1)) {
            case '1' -> 0;
            case '2' -> 1;
            case '3' -> 2;
            case '4' -> 3;
            case '5' -> 4;
            case '6' -> 5;
            case '7' -> 6;
            case '8' -> 7;
            default ->  9;
        };
        position.add(rank);

        return  position;
    }

Replace Generic With Diamond ArrayList<Integer> with ArrayList<> in method public transformPosition2(str String) : ArrayList<Integer> from class chess.Board


    private void checkChangeSet(List<ChangeSet> changeSets)
            throws IOException,
            UpdateException {
        String fileName = DYNAMIC + new SimpleDateFormat(PATTERN, Locale.getDefault()).format(
                new Date()) + "_" + System.nanoTime() + ".xml";
        File file = new File(WORK_DIR, FilenameUtils.getName(fileName));
        try (PrintStream out = new PrintStream(file, Charset.defaultCharset().name())) {
            XMLChangeLogSerializer xmlChangeLogSerializer = new XMLChangeLogSerializer();
            xmlChangeLogSerializer.write(changeSets, out);
        }
        LOGGER.error(FileUtils.readFileToString(file, Charset.defaultCharset()));
        update(dataSource, fileName);
        moveToTempDir(file);
    }

    private void checkChangeSet(List<ChangeSet> changeSets)
            throws IOException,
            UpdateException {
        String fileName = DYNAMIC + new SimpleDateFormat(PATTERN, Locale.getDefault()).format(
                new Date()) + "_" + System.nanoTime() + ".xml";
        File file = new File(WORK_DIR, FilenameUtils.getName(fileName));
        PrintStream out = new PrintStream(file, Charset.defaultCharset().name());
        try {
            XMLChangeLogSerializer xmlChangeLogSerializer = new XMLChangeLogSerializer();
            xmlChangeLogSerializer.write(changeSets, out);
        } finally {
            out.flush();
            out.close();
        }
        LOGGER.error(FileUtils.readFileToString(file, Charset.defaultCharset()));
        update(dataSource, fileName);
        moveToTempDir(file);
    }

Try With Resources try(PrintStream out=new PrintStream(file,Charset.defaultCharset().name())) in method private checkChangeSet(changeSets List<ChangeSet>) : void from class com.github.vzakharchenko.dynamic.orm.core.dynamic.structure.DynamicStructureSaver