├── .gitignore ├── README.md └── empire ├── src └── empire │ ├── ColorInterpolator.java │ ├── Empire.java │ └── terrain │ └── TerrainInterpolator.java └── test └── empire ├── ColorInterpolatorTest.java └── terrain └── TerrainInterpolatorTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | *.idea 3 | empire/out 4 | empire/out/* 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Based on old VMS Empire game. 2 | 3 | -------------------------------------------------------------------------------- /empire/src/empire/ColorInterpolator.java: -------------------------------------------------------------------------------- 1 | package empire; 2 | 3 | import java.awt.*; 4 | 5 | public class ColorInterpolator { 6 | public static void interpolate(Color[] colors) { 7 | int end = 0; 8 | while (end < colors.length - 1) { 9 | int start = end; 10 | end = start + 1; 11 | for (; colors[end] == null; end++) 12 | ; 13 | double distance = end - start; 14 | ColorVector cs = new ColorVector(colors[start]); 15 | ColorVector cEnd = new ColorVector(colors[end]); 16 | ColorVector cStep = cEnd.subtract(cs).scale(distance); 17 | 18 | for (int i = start + 1; i < end; i++) { 19 | cs.addTo(cStep); 20 | colors[i] = cs.toColor(); 21 | } 22 | } 23 | } 24 | } 25 | 26 | class ColorVector { 27 | public double r; 28 | public double g; 29 | public double b; 30 | 31 | public ColorVector(double r, double g, double b) { 32 | this.r = r; 33 | this.g = g; 34 | this.b = b; 35 | } 36 | 37 | public ColorVector(Color c) { 38 | r = c.getRed(); 39 | g = c.getGreen(); 40 | b = c.getBlue(); 41 | } 42 | 43 | public Color toColor() { 44 | int r = (int)Math.round(this.r); 45 | int g = (int)Math.round(this.g); 46 | int b = (int)Math.round(this.b); 47 | return new Color(r, g, b); 48 | } 49 | 50 | void addTo(ColorVector v) { 51 | r += v.r; 52 | g += v.g; 53 | b += v.b; 54 | } 55 | 56 | public ColorVector subtract(ColorVector c) { 57 | return new ColorVector(r - c.r, g - c.g, b - c.b); 58 | } 59 | 60 | public ColorVector scale(double d) { 61 | return new ColorVector(r / d, g / d, b / d); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /empire/src/empire/Empire.java: -------------------------------------------------------------------------------- 1 | package empire; 2 | 3 | import empire.terrain.TerrainInterpolator; 4 | import processing.core.PApplet; 5 | 6 | import java.awt.*; 7 | import java.util.Random; 8 | 9 | public class Empire extends PApplet { 10 | private Point center = new Point(0, 0); 11 | private int cellSize = 1; 12 | private Random r = new Random(); 13 | private Color[] altitudeColor = new Color[256]; 14 | double[][] land = new double[1025][1025]; 15 | private TerrainInterpolator ti; 16 | 17 | public void settings() { 18 | size(displayWidth, displayHeight); 19 | altitudeColor[0] = new Color(0, 0, 80); 20 | altitudeColor[31] = new Color(32, 32, 255); 21 | altitudeColor[32] = new Color(200,200,90); 22 | altitudeColor[33] = new Color(0, 150, 0); 23 | altitudeColor[65] = new Color(0,100,0); 24 | altitudeColor[75] = new Color(120, 80, 0); 25 | altitudeColor[120] = new Color(50, 20, 4); 26 | altitudeColor[125] = new Color(50,50,50); 27 | altitudeColor[147] = new Color(150,150,150); 28 | altitudeColor[150] = new Color(255, 255, 255); 29 | altitudeColor[255] = new Color(255, 255, 255); 30 | ColorInterpolator.interpolate(altitudeColor); 31 | 32 | land[0][0] = 180; 33 | land[1024][0]=200; 34 | ti = new TerrainInterpolator(); 35 | generateTerrain(); 36 | } 37 | 38 | private void generateTerrain() { 39 | ti.interpolate(land,1025,100,-10); 40 | } 41 | 42 | public void draw() { 43 | background(220); 44 | for (int x = 0; x < 1025; x++) 45 | for (int y = 0; y < 1025; y++) 46 | drawMapCell(x, y); 47 | } 48 | 49 | public void mousePressed() { 50 | generateTerrain(); 51 | } 52 | 53 | private void drawMapCell(int x, int y) { 54 | rectMode(CORNER); 55 | noStroke(); 56 | fill(0); 57 | int cellWidth = displayWidth / cellSize; 58 | int cellHeight = displayHeight / cellSize; 59 | Point originCell = new Point(0,0); 60 | 61 | int cell = getCell(x, y); 62 | fill(cellColor(cell).getRGB()); 63 | 64 | rect((originCell.x + x) * cellSize, (originCell.y + y) * cellSize, cellSize, cellSize); 65 | } 66 | 67 | private int getCell(int x, int y) { 68 | return max((int)land[x][y], 0); 69 | } 70 | 71 | private Color cellColor(int cell) { 72 | return altitudeColor[cell]; 73 | } 74 | 75 | public static void main(String... args) { 76 | PApplet.main("empire.Empire"); 77 | } 78 | } 79 | 80 | class Point { 81 | public int x, y; 82 | 83 | public Point(int x, int y) { 84 | this.x = x; 85 | this.y = y; 86 | } 87 | } -------------------------------------------------------------------------------- /empire/src/empire/terrain/TerrainInterpolator.java: -------------------------------------------------------------------------------- 1 | package empire.terrain; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class TerrainInterpolator { 7 | private double[][] array; 8 | private int size; 9 | protected double randomAmplitude; 10 | private double offset; 11 | 12 | public void interpolate(double[][] array, int size, double randomAmplitude, double offset) { 13 | this.array = array; 14 | this.size = size; 15 | this.randomAmplitude = randomAmplitude; 16 | this.offset = offset; 17 | if (size == 1) 18 | return; 19 | if (!isPowerOfTwo(size - 1)) 20 | throw new BadSize(); 21 | 22 | diamondSquare(); 23 | } 24 | 25 | void interpolate(double[][] array, int size) { 26 | interpolate(array, size, 0, 0); 27 | } 28 | 29 | void diamondSquare() { 30 | for (int cellSize = size - 1; cellSize > 1; cellSize /= 2) { 31 | doSquares(cellSize); 32 | doDiamonds(cellSize); 33 | offset /= 2; 34 | randomAmplitude/=2; 35 | } 36 | } 37 | 38 | private void doDiamonds(int cellSize) { 39 | for (int x = 0; x < size - 1; x += cellSize) { 40 | for (int y = 0; y < size - 1; y += cellSize) { 41 | doDiamond(x, y, cellSize + 1); 42 | } 43 | } 44 | } 45 | 46 | private void doSquares(int cellSize) { 47 | for (int x = 0; x < size - 1; x += cellSize) { 48 | for (int y = 0; y < size - 1; y += cellSize) { 49 | doSquare(x, y, cellSize + 1); 50 | } 51 | } 52 | } 53 | 54 | void doSquare(int x, int y, int size) { 55 | int len = size - 1; 56 | int mid = len / 2; 57 | 58 | set(x + mid, y + mid, average(x, y, x + len, y, x, y + len, x + len, y + len)); 59 | } 60 | 61 | void doDiamond(int x, int y, int size) { 62 | int len = size - 1; 63 | int d = len / 2; 64 | setDiamondPoint(x + d, y, d); 65 | setDiamondPoint(x, y + d, d); 66 | setDiamondPoint(x + d, y + len, d); 67 | setDiamondPoint(x + len, y + d, d); 68 | } 69 | 70 | private void setDiamondPoint(int x, int y, int d) { 71 | List points = new ArrayList<>(); 72 | addIfValid(points, x - d, y); 73 | addIfValid(points, x + d, y); 74 | addIfValid(points, x, y - d); 75 | addIfValid(points, x, y + d); 76 | 77 | Integer[] pointsArray = points.toArray(new Integer[1]); 78 | set(x, y, average(pointsArray)); 79 | } 80 | 81 | private void addIfValid(List points, int x, int y) { 82 | if (x >= 0 && x < size && y >= 0 && y < size) { 83 | points.add(x); 84 | points.add(y); 85 | } 86 | } 87 | 88 | void set(int x, int y, double value) { 89 | array[x][y]=value; 90 | } 91 | 92 | double average(Integer... points) { 93 | if (points.length == 0) 94 | return 0; 95 | double sum = 0; 96 | for (int i = 0; i < points.length; i += 2) 97 | sum += get(points[i], points[i + 1]); 98 | return sum / (points.length / 2) + random() + offset; 99 | } 100 | 101 | double random() { 102 | return (Math.random()+Math.random()-1)*randomAmplitude; 103 | } 104 | 105 | double get(int x, int y) { 106 | return array[x][y]; 107 | } 108 | 109 | boolean isPowerOfTwo(int p) { 110 | if (p<=1) 111 | return false; 112 | int n; 113 | for (n=p; n%2 == 0; n/=2) 114 | ; 115 | return n==1; 116 | } 117 | 118 | public class BadSize extends RuntimeException { 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /empire/test/empire/ColorInterpolatorTest.java: -------------------------------------------------------------------------------- 1 | package empire; 2 | 3 | import org.junit.Before; 4 | import org.junit.Test; 5 | 6 | import java.awt.*; 7 | 8 | import static org.junit.Assert.*; 9 | 10 | public class ColorInterpolatorTest { 11 | private Color colors[]; 12 | @Before 13 | public void setUp() throws Exception { 14 | 15 | } 16 | 17 | @Test 18 | public void singleStepInterpolation () throws Exception { 19 | colors = new Color[3]; 20 | colors[0] = new Color(0,0,0); 21 | colors[2] = new Color(255, 255, 255); 22 | ColorInterpolator.interpolate(colors); 23 | assertEquals(new Color(128, 128, 128), colors[1]); 24 | } 25 | 26 | @Test 27 | public void multipleStepInterpolation() throws Exception { 28 | colors = new Color[6]; 29 | colors[0] = new Color(0,0,0); 30 | colors[2] = new Color(50,60,70); 31 | colors[5] = new Color(100,100,100); 32 | ColorInterpolator.interpolate(colors); 33 | assertEquals(new Color(25, 30, 35), colors[1]); 34 | assertEquals(new Color(67,73,80), colors[3]); 35 | assertEquals(new Color(83,87,90), colors[4]); 36 | } 37 | 38 | @Test 39 | public void noGap() throws Exception { 40 | colors=new Color[2]; 41 | colors[0] = new Color(0,0,0); 42 | colors[1] = new Color(2,2,2); 43 | ColorInterpolator.interpolate(colors); 44 | assertEquals(new Color(0, 0, 0), colors[0]); 45 | assertEquals(new Color(2, 2, 2), colors[1]); 46 | } 47 | } -------------------------------------------------------------------------------- /empire/test/empire/terrain/TerrainInterpolatorTest.java: -------------------------------------------------------------------------------- 1 | package empire.terrain; 2 | 3 | import de.bechte.junit.runners.context.HierarchicalContextRunner; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | 8 | import static org.hamcrest.core.Is.is; 9 | import static org.hamcrest.core.StringEndsWith.endsWith; 10 | import static org.hamcrest.core.StringStartsWith.startsWith; 11 | import static org.hamcrest.text.IsEmptyString.isEmptyString; 12 | import static org.junit.Assert.assertThat; 13 | 14 | @RunWith(HierarchicalContextRunner.class) 15 | public class TerrainInterpolatorTest { 16 | private String actions = ""; 17 | private double[][] dummy; 18 | private TerrainInterpolator interpolator; 19 | 20 | public class SimpleValidations { 21 | @Before 22 | public void setUp() throws Exception { 23 | dummy = new double[1][1]; 24 | interpolator = new TerrainInterpolatorSpy(); 25 | } 26 | 27 | @Test 28 | public void terminalCondition_sizeOne() throws Exception { 29 | interpolator.interpolate(dummy, 1); 30 | assertThat(actions, isEmptyString()); 31 | } 32 | 33 | @Test(expected = TerrainInterpolator.BadSize.class) 34 | public void sizeMustBePowerOfTwoPlus1() throws Exception { 35 | interpolator.interpolate(dummy, 2); 36 | } 37 | 38 | @Test 39 | public void Check_isPowerOfTwo() throws Exception { 40 | assertThat(interpolator.isPowerOfTwo(2), is(true)); 41 | assertThat(interpolator.isPowerOfTwo(4), is(true)); 42 | assertThat(interpolator.isPowerOfTwo(8), is(true)); 43 | 44 | assertThat(interpolator.isPowerOfTwo(1), is(false)); 45 | assertThat(interpolator.isPowerOfTwo(7), is(false)); 46 | assertThat(interpolator.isPowerOfTwo(18), is(false)); 47 | } 48 | 49 | public class SquareDiamondCoordinateCalculations { 50 | @Test 51 | public void simpleThreeByThree_SquarePass() throws Exception { 52 | interpolator.interpolate(dummy, 3); 53 | assertThat(actions, startsWith("Square(0,0,3): A([0,0],[2,0],[0,2],[2,2])->[1,1].")); 54 | } 55 | 56 | @Test 57 | public void simpleThreeByThree_DiamondPass() throws Exception { 58 | interpolator.interpolate(dummy, 3); 59 | assertThat(actions, endsWith("Diamond(0,0,3): " + 60 | "A([0,0],[2,0],[1,1])->[1,0]. " + 61 | "A([1,1],[0,0],[0,2])->[0,1]. " + 62 | "A([0,2],[2,2],[1,1])->[1,2]. " + 63 | "A([1,1],[2,0],[2,2])->[2,1]. ")); 64 | } 65 | 66 | @Test 67 | public void DiamondSquare_FirstPass() throws Exception { 68 | interpolator.interpolate(dummy, 5); 69 | assertThat(actions, startsWith( 70 | "Square(0,0,5): A([0,0],[4,0],[0,4],[4,4])->[2,2]. " + 71 | "Diamond(0,0,5): " + 72 | "A([0,0],[4,0],[2,2])->[2,0]. " + 73 | "A([2,2],[0,0],[0,4])->[0,2]. " + 74 | "A([0,4],[4,4],[2,2])->[2,4]. " + 75 | "A([2,2],[4,0],[4,4])->[4,2]. ")); 76 | } 77 | 78 | } 79 | 80 | public class SquareDiamondRepetition { 81 | @Before 82 | public void setup() { 83 | dummy = new double[1][1]; 84 | interpolator = new TerrainInterpolatorDiamondSquareSpy(); 85 | } 86 | 87 | @Test 88 | public void FiveByFive() throws Exception { 89 | interpolator.interpolate(dummy, 5); 90 | assertThat(actions, is( 91 | "" + 92 | "Square(0,0,5) Diamond(0,0,5) " + 93 | "Square(0,0,3) Square(0,2,3) Square(2,0,3) Square(2,2,3) " + 94 | "Diamond(0,0,3) Diamond(0,2,3) Diamond(2,0,3) Diamond(2,2,3) " 95 | )); 96 | } 97 | } 98 | 99 | public class Averages { 100 | @Before 101 | public void setup() { 102 | dummy = new double[3][3]; 103 | interpolator = new TerrainInterpolator(); 104 | } 105 | 106 | @Test 107 | public void zero() throws Exception { 108 | interpolator.interpolate(dummy, 3); 109 | assertThat(dummy, is(new double[][]{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}})); 110 | } 111 | 112 | @Test 113 | public void allOnes() throws Exception { 114 | dummy[0][0] = dummy[2][0] = dummy[0][2] = dummy[2][2] = 1; 115 | interpolator.interpolate(dummy, 3); 116 | assertThat(dummy, is(new double[][]{{1, 1, 1}, {1, 1, 1}, {1, 1, 1}})); 117 | } 118 | 119 | @Test 120 | public void ramp() throws Exception { 121 | dummy[0][0] = 0; 122 | dummy[2][0] = 12; 123 | dummy[0][2] = 12; 124 | dummy[2][2] = 24; 125 | interpolator.interpolate(dummy,3); 126 | assertThat(dummy, is(new double[][]{{0, 8, 12}, {8, 12, 16}, {12, 16, 24}})); 127 | } 128 | } 129 | 130 | public class RandomsAndOffsets { 131 | @Before 132 | public void setup() { 133 | dummy = new double[5][5]; 134 | interpolator = new TerrainInterpolatorWithFixedRandom(); 135 | } 136 | 137 | @Test 138 | public void volcano() throws Exception { 139 | interpolator.interpolate(dummy, 5, 2,4); 140 | assertThat(dummy, is(new double[][]{ 141 | {0,8.5,8,8.5,0}, 142 | {8.5,8.5,10.75,8.5,8.5}, 143 | {8,10.75,6,10.75,8}, 144 | {8.5,8.5,10.75,8.5,8.5}, 145 | {0,8.5,8,8.5,0} 146 | })); 147 | } 148 | } 149 | } 150 | 151 | private class TerrainInterpolatorSpy extends TerrainInterpolator { 152 | void doSquare(int x, int y, int size) { 153 | actions += String.format("Square(%d,%d,%d): ", x, y, size); 154 | super.doSquare(x, y, size); 155 | } 156 | 157 | void doDiamond(int x, int y, int size) { 158 | actions += String.format("Diamond(%d,%d,%d): ", x, y, size); 159 | super.doDiamond(x, y, size); 160 | } 161 | 162 | void set(int x, int y, double value) { 163 | actions += String.format("->[%d,%d]. ", x, y); 164 | } 165 | 166 | double get(int x, int y) { 167 | return -1; 168 | } 169 | 170 | double average(Integer... points) { 171 | actions += "A("; 172 | for (int i = 0; i < points.length; i += 2) 173 | actions += String.format("[%d,%d],", points[i], points[i + 1]); 174 | actions = actions.substring(0, actions.length() - 1) + ")"; 175 | return 0; 176 | } 177 | } 178 | 179 | private class TerrainInterpolatorDiamondSquareSpy extends TerrainInterpolator { 180 | void doSquare(int x, int y, int size) { 181 | actions += String.format("Square(%d,%d,%d) ", x, y, size); 182 | } 183 | 184 | void doDiamond(int x, int y, int size) { 185 | actions += String.format("Diamond(%d,%d,%d) ", x, y, size); 186 | 187 | } 188 | } 189 | 190 | private class TerrainInterpolatorWithFixedRandom extends TerrainInterpolator { 191 | double random() { 192 | return randomAmplitude; 193 | } 194 | } 195 | } --------------------------------------------------------------------------------