Terrain smoothing algorithm not working… it brings values towards positive and negative infinity with multiple runs

Keywords: java random terrain

Question: 

I'm making a world generator in java. The algorithm works by first setting various positions with high points (ranges) and low points (trenches), then it averages it all out by going through every tile, getting the height of all surrounding tiles, then averaging out, then setting all surrounding tiles to the new averaged value. It then goes to the next tile and does the same thing, going across both the X and Y axes. The hope is that it will create a world that is dynamically generated by the ranges and trenches positions. And by looking at a simple picture, it does accomplish this. (Image A).

However, the problems begin with the algorithm is ran more than once, which is necessary to smooth out the map. Values begin to skyrocket, and I can't figure out why this happens. Running it a second time shows this effect (Image B) - the average value function should push values towards 0, not infinity.

While the terrain does look a bit wonky, when ran through a simple roughening algorithm it looks fine. (Currently disabled) I can do the roughening later, I need to figure out why the smoothing algorithm doesn't work.

I know about tools such as Perlin Noise and Simplex Noise, and it's becoming more and more clear that I may need to start using them, but I want to figure out what my issue is.

So, I've tracked the error to the fact that the average value cells overlap. averaging tile 0,0 includes all surrounding tiles, but of course when moved to the next tile, it will take the new value from 0,0 and include it in the function. But this is what I want. I want the average value to diffuse over the entire map. I've ruled out overflow and underflow errors, and the average value function itself works fine.

public static void smoothHeight(int p, int num) { 
        for (int z = 0; z < 1; z++) { //Run through once. Edit to find optimal?
            for (int i = 0; i < toSmooth.size(); i++) { //toSmooth will contain HALF of all values on the map. It will have values on column 0, 2, 4, etc.
                Integer[] pos = toSmooth.get(i);
                int x = pos[0];
                int y = pos[1];
                Tile thisTile = FinalMap[x][y];
                double avgHeightForAll = thisTile.getHeight(); //avgHeightForAll is used to eventually smooth. THIS IS THE VALUE THAT SHOULD GET AVERAGED OUT
                double times = 0;
                for (int x1 = x - 1; x1 <= x + 1; x1 += 1) {
                    for (int y1 = y - 1; y1 <= y + 1; y1 += 1) { //Go through tiles around it, and add to avg
                        Integer[] posToSmooth = smoothAlgFind(new Integer[] {x1, y1}, 1); //smoothAlgFind ensures that tiles across the map are included. For example, if smoothing at position 0, it will include position 499 in the smoothing, not -1
                        Tile tileToSmooth = FinalMap[posToSmooth[0]][posToSmooth[1]];
                        if (tileToSmooth.isCoastal == true || num == 0) {
                            double smoothTileh = tileToSmooth.getHeight();
                            avgHeightForAll += smoothTileh;
                            times++;    
                        }
                    }
                }
                DecimalFormat df = new DecimalFormat("0.00");
                avgHeightForAll /= (times); //DONT MESS WITH 1.17, REMOVING IT BREAKS EVERYTHING. NO I DONT KNOW WHY. times = 9
                //thisTile.setHeight(avgHeightForAll, 0);
                avgHeightForAll = Double.parseDouble(df.format(avgHeightForAll));
                if (Math.abs(avgHeightForAll) > 40000) {
                    System.out.println((avgHeightForAll * times) + " / " + times + " = " + (avgHeightForAll));
                }
                for (int x1 = x - 1; x1 <= x + 1; x1 += 1) {
                    for (int y1 = y - 1; y1 <= y + 1; y1 += 1) {
                        Integer[] posToSmooth = smoothAlgFind(new Integer[] {x1, y1}, 1);
                        Tile tile = FinalMap[posToSmooth[0]][posToSmooth[1]];
                        if (tile.isCoastal == true  || num == 0) {//don't worry about isCoastal, has been disabed for now
                            tile.setHeight(avgHeightForAll, 0); 
                        }
                    }
                }
            }


            for (int i = toSmooth.size() - 1; i >= 0; i--) { //now we go the other way to prevent scerw-y looking generation skewed to one side
                Integer[] pos = toSmooth.get(i);
                int x = pos[0];
                int y = pos[1];
                Tile thisTile = FinalMap[x][y];
                double avgHeightForAll = thisTile.getHeight(); //avgHeightForAll is used to eventually smooth
                double times = 0;
                for (int x1 = x - 1 - p; x1 <= x + 1 + p; x1 += 1) {
                    for (int y1 = y - 1 - p; y1 <= y + 1 + p; y1 += 1) { //Go through tiles around it, and add to avg
                        Integer[] posToSmooth = smoothAlgFind(new Integer[] {x1, y1}, 1);
                        Tile tileToSmooth = FinalMap[posToSmooth[0]][posToSmooth[1]];
                        if (tileToSmooth.isCoastal == true  || num == 0) { //don't worry about isCoastal, has been disabed for now
                            double smoothTileh = tileToSmooth.getHeight();
                            avgHeightForAll += smoothTileh;
                            times++;    
                        }
                    }
                }

                DecimalFormat df = new DecimalFormat("0.00");
                avgHeightForAll = Double.parseDouble(df.format(avgHeightForAll));
                avgHeightForAll /= (times); //DONT MESS WITH 1.17, REMOVING IT BREAKS EVERYTHING. NO I DONT KNOW WHY.
                //thisTile.setHeight(avgHeightForAll, 0);
                avgHeightForAll = Double.parseDouble(df.format(avgHeightForAll));
                //System.out.println("avgHeightForAll" + avgHeightForAll);
                if (Math.abs(avgHeightForAll) > 40000) { //if something goes wrong this triggers!
                    System.out.println((avgHeightForAll * times) + " / " + times + " = " + (avgHeightForAll));
                }
                for (int x1 = x - 1; x1 <= x + 1; x1 += 1) {
                    for (int y1 = y - 1; y1 <= y + 1; y1 += 1) {
                        Integer[] posToSmooth = smoothAlgFind(new Integer[] {x1, y1}, 1);
                        Tile tile = FinalMap[posToSmooth[0]][posToSmooth[1]];
                        if (tile.isCoastal == true  || num == 0) {
                            tile.setHeight(avgHeightForAll, 0); 
                        }
                    }
                }
            }
        }
        System.out.println("Smoothed");

    }

Answers: