package koth.user.jlb;
import koth.game.Board;
import koth.game.Generator;
import koth.game.Move;
import koth.util.Algorithms;
import koth.util.Vector;
import java.util.*;
/**
* Generate a pseudo-random maze-like map.
*/
public class Maze implements Generator {
private final Random random = new Random();
/**
* Modified version of Prim's algorithm to generate pseudo-maze.
* @see <a href=http://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_Prim.27s_algorithm>Wikipedia</a>
*/
public static Set<Vector> generate(int size, float redundantProbability, Random random) {
// Add origin as a maze tile
Set<Vector> tiles = new HashSet<Vector>();
tiles.add(new Vector());
Set<Vector> walls = new HashSet<Vector>();
walls.add(new Vector(1, 0));
walls.add(new Vector(-1, 0));
walls.add(new Vector(0, 1));
walls.add(new Vector(0, -1));
// Repeat specified number of times
for (int n = 1; n < size;) {
// Choose randomly a wall
Vector wall = Algorithms.random(walls, random);
walls.remove(wall);
// Check if both endpoints already exist
Move dir = wall.getX() % 2 == 0 ? Move.North : Move.East;
Vector a = wall.add(dir), b = wall.sub(dir);
boolean ac = tiles.contains(a), bc = tiles.contains(b);
if (ac && bc && random.nextFloat() >= redundantProbability)
continue;
// Add tiles to maze
tiles.add(wall);
++n;
if (!ac) {
tiles.add(a);
++n;
}
if (!bc) {
tiles.add(b);
++n;
}
// Add neighbors as walls
for (Move m : Move.getNonzeros()) {
Vector am = a.add(m);
if (!tiles.contains(am))
walls.add(am);
Vector bm = b.add(m);
if (!tiles.contains(bm))
walls.add(bm);
}
}
return tiles;
}
/**
* Create a board with specified parameters.
*/
public static Board create(int teams, int spawnRadius, int size, float redundantProbability, Random random) {
if (random == null)
throw new NullPointerException();
if (teams < 0 || spawnRadius < 0 || size <= 0)
throw new IllegalArgumentException();
// Create tiles
Set<Vector> tiles = generate(size, redundantProbability, random);
Map<Vector, Map<Vector, Integer>> dists = Algorithms.distances(tiles);
int max = 0;
for (Map<Vector, Integer> m : dists.values())
for (int i : m.values())
max = Math.max(i, max);
// Try to select equidistant spawn origins
List<Vector> origins = null;
long score = Long.MAX_VALUE;
for (int n = 0; n < 50; ++n) {
List<Vector> test = new ArrayList<Vector>(teams);
long cost = 0;
for (int i = 0; i < teams; ++i) {
Vector p = Algorithms.random(tiles, random);
test.add(p);
for (int j = 0; j < i; ++j) {
long d = max - dists.get(p).get(test.get(j));
cost += d * d;
}
}
if (origins == null || cost < score) {
origins = test;
score = cost;
}
}
// Add surroundings to spawns
List<Set<Vector>> spawns = new ArrayList<Set<Vector>>(teams);
for (int i = 0; i < teams; ++i) {
Set<Vector> ss = new HashSet<Vector>();
Map<Vector, Integer> ds = dists.get(origins.get(i));
for (Vector t : tiles)
if (ds.get(t) <= spawnRadius)
ss.add(t);
spawns.add(ss);
}
return new Board(tiles, spawns);
}
@Override
public Board create(int teams) {
return create(teams, 4, 50 * teams, 0.3f, random);
}
}