Let's be lazy and adapt my answer from this question !
The algorithm computes a "snake path" starting from upper left corner that fills the whole rectangle. The snake can go only up, down, left, right.
The snake path is followed and is filled with the first color, then the second color, etc... taking in account the color percentages
This algorithm produces a lots of straight lines; to improve it, I detect them and replace them with "waves" that keep the same amount of pixels.
Parameters : 380 260 233 420 1300 3511 4772 5089 9507 22107 25117 26744
Parameters : 380 260 8 5 6 7 8 4 5 6 7 9 4 6 9 5 8 7 5
Dark Age of Camelot (213 307 1 1 1)
The code:
package map;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
public class GenMap2 {
private enum State { NO, YES, SHIFT };
public final static int TOP = 1, BOTTOM = 2, LEFT = 4, RIGHT = 8;
enum Action { ADD_LINE_TOP, ADD_LINE_LEFT, DOUBLE_SIZE, CREATE};
public static void main(String[] args) throws IOException {
int w = Integer.parseInt(args[0]), h = Integer.parseInt(args[1]);
List<Integer> areas = new ArrayList<Integer>();
int total = 0;
for (int i = 2; i < args.length; i++) {
int area = Integer.parseInt(args[i]);
areas.add(area);
total += area;
}
Collections.sort(areas);
Collections.reverse(areas);
int [][] tab = build(w, h);
BufferedImage dest = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
int [] black = {0, 0, 0};
for (int j = 0; j < dest.getHeight(); j++) {
for (int i = 0; i < dest.getWidth(); i++) {
dest.getRaster().setPixel(i, j, black);
}
}
int x = 0, y = -1;
int go = BOTTOM, previous = BOTTOM;
List<Color> colors = new ArrayList<Color>();
Random rand = new Random(0); // prog must be deterministic
while (colors.size() < areas.size()) {
Color c = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
boolean ok = true;
for (Color existing : colors) {
if (existing.equals(c)) {
ok = false;
break;
}
}
if (ok) {
colors.add(c);
}
}
int [][] map = new int[w][h];
int cpt = 0;
while (true) {
if (go == BOTTOM) y++;
if (go == TOP) y--;
if (go == LEFT) x--;
if (go == RIGHT) x++;
int tmp = (int)(((long)cpt) * total / (w * h));
int i = 0;
for (i = 0; i < areas.size(); i++) {
int area = areas.get(i);
if (tmp < area) {
break;
}
tmp -= area;
}
map[x][y] = i;
previous = go;
go = -1;
if ((tab[x][y] & TOP) != 0 && previous != BOTTOM) go = TOP;
if ((tab[x][y] & BOTTOM) != 0 && previous != TOP) go = BOTTOM;
if ((tab[x][y] & LEFT) != 0 && previous != RIGHT) go = LEFT;
if ((tab[x][y] & RIGHT) != 0 && previous != LEFT) go = RIGHT;
if (go == -1) break;
cpt++;
}
String [] src0 = srcPattern(16);
String [] repl0 = destPattern(16);
while (findPattern(map, src0, Arrays.asList(repl0, flip(repl0)))){}
while (findPattern(map, rotate(src0), Arrays.asList(rotate(repl0), rotate(flip(repl0))))){}
String [] src1 = srcPattern(8);
String [] repl1 = destPattern(8);
while (findPattern(map, src1, Arrays.asList(repl1, flip(repl1)))){}
while (findPattern(map, rotate(src1), Arrays.asList(rotate(repl1), rotate(flip(repl1))))){}
String [] src2 = srcPattern(4);
String [] repl2 = destPattern(4);
while (findPattern(map, src2, Arrays.asList(repl2, flip(repl2)))){}
while (findPattern(map, rotate(src2), Arrays.asList(rotate(repl2), rotate(flip(repl2))))){}
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
Color c = colors.get(map[x][y]);
dest.getRaster().setPixel(x, y, new int[] {c.getRed(), c.getGreen(), c.getBlue()});
}
}
ImageIO.write(dest, "png", new FileOutputStream("map.png"));
}
private static Random randPat = new Random(0);
private static String [] srcPattern(int size) {
String [] ret = new String[size*2];
for (int i = 0; i < size*2; i++) {
ret[i] = "";
for (int j = 0; j < size*4; j++) {
ret[i] += i < size ? "1" : "2";
}
}
return ret;
}
private static String [] destPattern(int size) {
String [] ret = new String[size*2];
for (int i = 0; i < size*2; i++) {
ret[i] = "";
for (int j = 0; j < size*2; j++) {
//int target = (int)((1 + Math.sin(j * Math.PI * .5/ size) * .4) * size);
int target = (int)((1 + (Math.cos(j * Math.PI/ size) - 1) * .2) * size);
ret[i] += (i < target) ? '1' : '2';
}
}
for (int i = 0; i < size*2; i++) {
for (int j = 0; j < size*2; j++) {
ret[i] += ret[size*2 - 1 - i].charAt(size*2 - 1 - j) == '1' ? '2' : '1';
}
}
return ret;
}
private static String [] flip(String [] pat) {
String [] ret = new String[pat.length];
for (int i = 0; i < ret.length; i++) {
ret[i] = new StringBuilder(pat[i]).reverse().toString();
}
return ret;
}
private static String [] rotate(String [] pat) {
String [] ret = new String[pat[0].length()];
for (int i = 0; i < ret.length; i++) {
ret[i] = "";
for (int j = 0; j < pat.length; j++) {
ret[i] += pat[j].charAt(i);
}
}
return ret;
}
private static boolean findPattern(int [][] map, String [] src, List<String []> dest) {
for (int y = 0; y < map[0].length - src.length; y++) {
for (int x = 0; x < map.length - src[0].length(); x++) {
int c1 = -1, c2 = -1;
boolean wrong = false;
for (int y1 = 0; y1 < src.length; y1++) {
for (int x1 = 0; x1 < src[0].length(); x1++) {
if (src[y1].charAt(x1) == '1') {
if (c1 == -1) {
c1 = map[x+x1][y+y1];
} else {
if (c1 != map[x+x1][y+y1]) {
wrong = true;
}
}
}
if (src[y1].charAt(x1) == '2') {
if (c2 == -1) {
c2 = map[x+x1][y+y1];
} else {
if (c2 != map[x+x1][y+y1]) {
wrong = true;
}
}
}
if (c1 != -1 && c1 == c2) wrong = true;
if (wrong) break;
}
if (wrong) break;
}
if (!wrong) {
System.out.println("Found match at " + x + " " + y);
String [] repl = dest.get(randPat.nextInt(dest.size()));
for (int y1 = 0; y1 < src.length; y1++) {
for (int x1 = 0; x1 < src[0].length(); x1++) {
map[x+x1][y+y1] = repl[y1].charAt(x1) == '1' ? c1 : c2;
}
}
return true;
}
}
}
return false;
}
public static int [][] build(int width, int height) {
List<Action> actions = new ArrayList<Action>();
while (height>1 && width>1) {
if (height % 2 == 1) {
height--;
actions.add(Action.ADD_LINE_TOP);
}
if (width % 2 == 1) {
width--;
actions.add(Action.ADD_LINE_LEFT);
}
if (height%2 == 0 && width%2 == 0) {
actions.add(Action.DOUBLE_SIZE);
height /= 2;
width /= 2;
}
}
actions.add(Action.CREATE);
Collections.reverse(actions);
int [][] tab = null;
for (Action action : actions) {
if (action == Action.CREATE) {
tab = new int[width][height];
if (height >= width) {
for (int i = 0; i < height-1; i++) {
tab[0][i] = TOP|BOTTOM;
}
tab[0][height-1] = TOP;
} else {
tab[0][0] = TOP|RIGHT;
for (int i = 1; i < width-1; i++) {
tab[i][0] = RIGHT|LEFT;
}
tab[width-1][0] = LEFT;
}
}
if (action == Action.DOUBLE_SIZE) {
tab = doubleTab(tab);
}
if (action == Action.ADD_LINE_TOP) {
int [][] tab2 = new int[tab.length][tab[0].length+1];
for (int i = 0; i < tab.length; i++) {
for (int j = 0; j < tab[0].length; j++) {
tab2[i][j+1] = tab[i][j];
}
}
tab2[0][0] = BOTTOM|RIGHT;
for (int i = 1; i < tab.length-1; i++) {
tab2[i][0] = RIGHT|LEFT;
}
tab2[tab.length-1][0] = TOP|LEFT;
mirror(tab2);
tab = tab2;
}
if (action == Action.ADD_LINE_LEFT) {
int [][] tab2 = new int[tab.length+1][tab[0].length];
for (int i = 0; i < tab.length; i++) {
for (int j = 0; j < tab[0].length; j++) {
tab2[i+1][j] = tab[i][j];
}
}
tab2[0][0] = BOTTOM|RIGHT;
tab2[1][0] |= LEFT;
tab2[1][0] -= TOP;
for (int i = 1; i < tab[0].length-1; i++) {
tab2[0][i] = TOP|BOTTOM;
}
tab2[0][tab[0].length-1] = TOP|BOTTOM;
flip(tab2);
tab = tab2;
}
}
return tab;
}
private static void mirror(int [][] tab) {
for (int i = 0; i < tab.length/2; i++) {
for (int j = 0; j < tab[0].length; j++) {
int tmp = tab[tab.length - 1 - i][j];
tab[tab.length - 1 - i][j] = tab[i][j];
tab[i][j] = tmp;
}
}
for (int i = 0; i < tab.length; i++) {
for (int j = 0; j < tab[0].length; j++) {
if ((tab[i][j] & LEFT)!=0 && (tab[i][j] & RIGHT)==0) {
tab[i][j] -= LEFT; tab[i][j] |= RIGHT;
} else if ((tab[i][j] & RIGHT)!=0 && (tab[i][j] & LEFT)==0) {
tab[i][j] -= RIGHT; tab[i][j] |= LEFT;
}
}
}
}
private static void flip(int [][] tab) {
for (int i = 0; i < tab.length; i++) {
for (int j = 0; j < tab[0].length/2; j++) {
int tmp = tab[i][tab[0].length - 1 - j];
tab[i][tab[0].length - 1 - j] = tab[i][j];
tab[i][j] = tmp;
}
}
for (int i = 0; i < tab.length; i++) {
for (int j = 0; j < tab[0].length; j++) {
if ((tab[i][j] & TOP)!=0 && (tab[i][j] & BOTTOM)==0) {
tab[i][j] -= TOP; tab[i][j] |= BOTTOM;
} else if ((tab[i][j] & BOTTOM)!=0 && (tab[i][j] & TOP)==0) {
tab[i][j] -= BOTTOM; tab[i][j] |= TOP;
}
}
}
}
public static int [][] doubleTab(int [][] tab) {
boolean [][] shiftTop = new boolean[tab.length][],
shiftLeft = new boolean[tab.length][],
shiftBottom = new boolean[tab.length][],
shiftRight = new boolean[tab.length][];
for (int i = 0; i < tab.length; i++) {
shiftTop[i] = new boolean[tab[i].length];
shiftLeft[i] = new boolean[tab[i].length];
shiftBottom[i] = new boolean[tab[i].length];
shiftRight[i] = new boolean[tab[i].length];
}
int x = 0, y = -1;
for (int i = 0; i < tab.length; i++) {
if ((tab[i][0] & TOP) != 0) {
x = i;
}
}
int go = BOTTOM, previous = BOTTOM;
boolean init = false;
while (true) {
if (go == BOTTOM) y++;
if (go == TOP) y--;
if (go == LEFT) x--;
if (go == RIGHT) x++;
previous = go;
go = -1;
if ((tab[x][y] & TOP) != 0 && previous != BOTTOM) go = TOP;
if ((tab[x][y] & BOTTOM) != 0 && previous != TOP) go = BOTTOM;
if ((tab[x][y] & LEFT) != 0 && previous != RIGHT) go = LEFT;
if ((tab[x][y] & RIGHT) != 0 && previous != LEFT) go = RIGHT;
if (previous == BOTTOM) {
shiftTop[x][y] = y==0 ? init : shiftBottom[x][y-1];
}
if (previous == TOP) {
shiftBottom[x][y] = shiftTop[x][y+1];
}
if (previous == RIGHT) {
shiftLeft[x][y] = shiftRight[x-1][y];
}
if (previous == LEFT) {
shiftRight[x][y] = shiftLeft[x+1][y];
}
if (go == -1) break;
if (previous == BOTTOM && go == LEFT) {
shiftLeft[x][y] = !shiftTop[x][y];
}
if (previous == BOTTOM && go == RIGHT) {
shiftRight[x][y] = shiftTop[x][y];
}
if (previous == BOTTOM && go == BOTTOM) {
shiftBottom[x][y] = shiftTop[x][y];
}
if (previous == TOP && go == LEFT) {
shiftLeft[x][y] = shiftBottom[x][y];
}
if (previous == TOP && go == RIGHT) {
shiftRight[x][y] = !shiftBottom[x][y];
}
if (previous == TOP && go == TOP) {
shiftTop[x][y] = shiftBottom[x][y];
}
if (previous == RIGHT && go == TOP) {
shiftTop[x][y] = !shiftLeft[x][y];
}
if (previous == RIGHT && go == BOTTOM) {
shiftBottom[x][y] = shiftLeft[x][y];
}
if (previous == RIGHT && go == RIGHT) {
shiftRight[x][y] = shiftLeft[x][y];
}
if (previous == LEFT && go == TOP) {
shiftTop[x][y] = shiftRight[x][y];
}
if (previous == LEFT && go == BOTTOM) {
shiftBottom[x][y] = !shiftRight[x][y];
}
if (previous == LEFT && go == LEFT) {
shiftLeft[x][y] = shiftRight[x][y];
}
}
int [][] tab2 = new int[tab.length * 2][];
for (int i = 0; i < tab2.length; i++) {
tab2[i] = new int[tab[0].length * 2];
}
for (int i = 0; i < tab.length; i++) {
for (int j = 0; j < tab[0].length; j++) {
State left = State.NO, right = State.NO, top = State.NO, bottom = State.NO;
if ((tab[i][j] & LEFT) != 0) {
left = shiftLeft[i][j] ? State.SHIFT : State.YES;
}
if ((tab[i][j] & TOP) != 0) {
top = shiftTop[i][j] ? State.SHIFT : State.YES;
}
if ((tab[i][j] & RIGHT) != 0) {
right = shiftRight[i][j] ? State.SHIFT : State.YES;
}
if ((tab[i][j] & BOTTOM) != 0) {
bottom = shiftBottom[i][j] ? State.SHIFT : State.YES;
}
int [] comp = compute(left, top, right, bottom);
tab2[i*2][j*2] = comp[0];
tab2[i*2+1][j*2] = comp[1];
tab2[i*2][j*2+1] = comp[2];
tab2[i*2+1][j*2+1] = comp[3];
}
}
return tab2;
}
private static int [] compute(State left, State top, State right, State bottom) {
// |
// --+
//
if (left == State.YES && top == State.SHIFT) {
return new int[] {LEFT|BOTTOM, TOP|BOTTOM, TOP|RIGHT, TOP|LEFT};// "v^>^";
}
if (left == State.SHIFT && top == State.YES) {
return new int[] {TOP|RIGHT, LEFT|BOTTOM, LEFT|RIGHT, LEFT|TOP}; //"^<>^";
}
//
// --+
// |
if (left == State.YES && bottom == State.YES) {
return new int[] {LEFT|RIGHT, LEFT|BOTTOM, RIGHT|BOTTOM, LEFT|TOP}; //">vv<";
}
if (left == State.SHIFT && bottom == State.SHIFT) {
return new int[] {RIGHT|BOTTOM, LEFT|BOTTOM, LEFT|TOP, TOP|BOTTOM}; //">v^v";
}
// |
// +--
//
if (right == State.SHIFT && top == State.SHIFT) {
return new int [] {RIGHT|BOTTOM,LEFT|TOP,TOP|RIGHT, LEFT|RIGHT}; //" v<>>";
}
if (right == State.YES && top == State.YES) {
return new int [] {TOP|BOTTOM,RIGHT|BOTTOM,TOP|RIGHT,TOP|LEFT}; //"v>>^";
}
//
// +--
// |
if (right == State.YES && bottom == State.SHIFT) {
return new int [] {RIGHT|BOTTOM, LEFT|RIGHT, TOP|RIGHT, LEFT|BOTTOM}; //"v<>v";
}
if (right == State.SHIFT && bottom == State.YES) {
return new int [] {RIGHT|BOTTOM, LEFT|BOTTOM, TOP|BOTTOM, RIGHT|TOP}; //"v<v^";
}
//
// --+--
//
if (right == State.YES && left == State.YES) {
return new int [] {LEFT|BOTTOM, RIGHT|BOTTOM, TOP|RIGHT, LEFT|TOP};
}
if (right == State.SHIFT && left == State.SHIFT) {
return new int [] {RIGHT|BOTTOM, LEFT|BOTTOM, LEFT|TOP, RIGHT|TOP};
}
// |
// +
// |
if (top == State.YES && bottom == State.YES) {
return new int [] {TOP|RIGHT, LEFT|BOTTOM, BOTTOM|RIGHT, LEFT|TOP};
}
if (top == State.SHIFT && bottom == State.SHIFT) {
return new int [] {RIGHT|BOTTOM, LEFT|TOP, RIGHT|TOP, LEFT|BOTTOM};
}
//
// +--
//
if (right == State.YES && bottom == State.NO && left == State.NO && top == State.NO) {
return new int [] {BOTTOM, RIGHT|BOTTOM, TOP|RIGHT, LEFT|TOP};
}
if (right == State.SHIFT && bottom == State.NO && left == State.NO && top == State.NO) {
return new int [] {RIGHT|BOTTOM, LEFT|BOTTOM, TOP, RIGHT|TOP};
}
// |
// +
//
if (top == State.YES && bottom == State.NO && left == State.NO && right == State.NO) {
return new int [] {TOP|RIGHT, LEFT|BOTTOM, RIGHT, LEFT|TOP};
}
if (top == State.SHIFT && bottom == State.NO && left == State.NO && right == State.NO) {
return new int [] {BOTTOM|RIGHT, LEFT|TOP, TOP|RIGHT, LEFT};
}
//
// +
// |
if (bottom == State.YES && top == State.NO && left == State.NO && right == State.NO) {
return new int [] {RIGHT, LEFT|BOTTOM, BOTTOM|RIGHT, LEFT|TOP};
}
if (bottom == State.SHIFT && top == State.NO && left == State.NO && right == State.NO) {
return new int [] {BOTTOM|RIGHT, LEFT, TOP|RIGHT, LEFT|BOTTOM};
}
//
// --+
//
if (left == State.YES && bottom == State.NO && right == State.NO && top == State.NO) {
return new int [] {LEFT|BOTTOM, BOTTOM, TOP|RIGHT, LEFT|TOP};
}
if (left == State.SHIFT && bottom == State.NO && right == State.NO && top == State.NO) {
return new int [] {BOTTOM|RIGHT, LEFT|BOTTOM, LEFT|TOP, TOP};
}
return null;
}
}
That's nice ! could you explain a little bit how your algorithm works ? – Arnaud – 2014-10-30T14:32:04.013
1Great! I think only the colors could be more "Earthy" (the third one is more like the Dark Purple Age of Camelot :P). I think you forgot the image for your last example. – Calvin's Hobbies – 2014-10-30T21:09:34.383
@Calvin'sHobbies gotta love random colour selection... it seems that my computer has a penchant for purple :P. Oops i did forget the third example... will generate and update. – Moogie – 2014-10-30T22:13:18.050