/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package pr.battlebots.gen;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import pr.battlebots.World;
import pr.battlebots.blocks.BlockType;
import pr.lib.Debug;
import pr.lib.Dir;
import pr.lib.Turn;
import pr.lib.U;
import pr.lib.Vec;
/**
*
* @author Paul
*/
public class MazeGen implements IGen {
boolean[][] map;
Random rng;
World world;
Vec[][] getNeigborPaths(Vec p, Dir d) {
Vec[][] paths = new Vec[4][2];
for (int i = 0; i < 4; i++) {
Dir pd = d.turn(Turn.values()[i]);
paths[i][0] = p.InDirection(pd);
paths[i][1] = paths[i][0].InDirection(pd);
}
return paths;
}
Vec[] getNeighbors(Vec p) {
Vec[] n = new Vec[4];
for (int i = 0; i < 4; i++) {
n[i] = p.InDirection(Dir.dirs[i]);
}
return n;
}
private void randomWalk(Vec p, Dir d, int steps, int baseScore, int noRepeatScore, int noBackTrackScore, int noTurnScore) {
set(p);
for (int i = 0; i < steps; i++) {
int[] pSpotsScores = new int[4];
for (int j = 0; j < 4; j++) {
Dir pd = d.turn(Turn.values()[j]);
Vec pHallSpot = p.InDirection(pd);
Vec pSpot = pHallSpot.InDirection(pd);
if (pSpot.distanceTo(world.midpoint) >= world.width / 2) {
//Debug.log(pSpot+" " + j+ " is out of bounds! ");
pSpotsScores[j] = 0;
continue;
}
boolean alreadBeenAtPSpot = get(pSpot);
boolean backTrack = get(pHallSpot);
int score = baseScore;
if (d == pd) {
score += noTurnScore;
}
if (!alreadBeenAtPSpot) {
score += noRepeatScore;
}
if (!backTrack) {
score += noBackTrackScore;
}
pSpotsScores[j] = score;
}
d = d.turn(Turn.values()[weightedChoise(pSpotsScores)]);
for (int j = 0; j < 2; j++) {
set(p = p.InDirection(d));
}
}
}
private void make(long seed) {
rng.setSeed(seed);
map = new boolean[world.height][world.width];
Vec start = world.midpoint;
int steps = world.width * world.height / (4 * 6);
int baseScore = 10;
int noRepeatScore = 10000;
int noBackTrackScore = -5;
int noTurnScore = 20;
int radius = world.width / 2 - 2;
randomWalk(start, Dir.East, steps, baseScore, noRepeatScore, noBackTrackScore, noTurnScore);
for (Dir d : Dir.dirs) {
Vec startPoint = world.midpoint.InDirection(d, d == Dir.North ? radius + 2 : radius);
randomWalk(startPoint, Dir.East, steps, baseScore, noRepeatScore, noBackTrackScore, noTurnScore);
}
}
int score() {
int visitedCells = 0;
for (int r = 2; r < world.height - 2; r += 2) {
for (int c = 2; c < world.width - 2; c += 2) {
if (map[r][c]) {
visitedCells++;
}
}
}
return visitedCells;
}
public int length = -1;
public int seed = -1;
@Override
public void gen(World world) {
rng = new Random();
this.world = world;
//long seed = rng.nextLong();
//rng.setSeed(seed);
make(seed);
length = astar(map, world.midpoint, world.getGoal());
world.setMessage(length + "," + seed + "," + score());
Iterator<Vec> vi = world.iterator();
while (vi.hasNext()) {
Vec p = vi.next();
if (!get(p)) {
world.makeBlock(blockType, p);
}
}
}
int weightedChoise(int[] weights) {
float[] ps = U.accumulate(U.pnorm(weights));
//Debug.log(U.join(ps,","));
float r = 1 - rng.nextFloat();
for (int i = 0; i < weights.length; i++) {
if (ps[i] > r && weights[i] != 0) {
return i;
}
}
return 0;
}
void set(Vec p, boolean v) {
map[p.y][p.x] = v;
}
void set(Vec p) {
set(p, true);
}
boolean get(Vec p) {
return map[p.y][p.x];
}
int astar(boolean[][] map, Vec pos, Vec goal) {
int[][] dists = new int[world.height][world.width];
int d = 0;
HashSet<Vec> open = new HashSet<Vec>();
open.add(pos);
while (open.size() > 0) {
Vec[] working = new Vec[open.size()];
//closed.addAll(open);
open.toArray(working);
open.clear();
++d;
if(d>200)
return -1;
for (Vec p : working) {
dists[p.y][p.x] = d;
if (goal.equals(p)) {
return d;
}
for (Vec oo : p.getNeighbors()) {
if (map[p.y][p.x]) {
open.add(oo);
}
}
}
}
return -1;
}
BlockType blockType = BlockType.BEDROCK;
}