package ru.shalnov.pacman.server;
import ru.shalnov.pacman.protocol.Courses;
import ru.shalnov.pacman.protocol.Map;
import java.util.ArrayList;
import java.util.Random;
/**
* Created with IntelliJ IDEA.
* User: Kirill Shalnov
* Date: 27.03.13
* Time: 1:20
*/
public class MapGenerator extends Object {
public static final int MIN_HEIGHT = 10;
public static final int MIN_WIDTH = 20;
private int mHeight;
private int mWidth;
//filling density map
private double mDensity;
//minimal step for turning trek to other side
private int mStepLength;
private Map mMap;
public MapGenerator() {
//last arguments are dummies used to fault fixer
initialize(MIN_WIDTH, MIN_HEIGHT, 1.0, MIN_HEIGHT);
}
public MapGenerator(int width, int height, double density, int stepLength) {
initialize(width, height, density, stepLength);
}
public int getStepLength() {
return mStepLength;
}
public void setStepLength(int stepLength) {
if (stepLength < getHeight() / 2 && stepLength < getWidth() / 2) {
mStepLength = stepLength;
} else {
mStepLength = Math.min(getHeight() / 2, getWidth() / 2);
}
}
public double getDensity() {
return mDensity;
}
public void setDensity(double density) {
//if density value will be 0.4 or more, we can't generate map
if (density <= 0.45) {
mDensity = density;
} else {
mDensity = 0.4;
}
}
public int getWidth() {
return mWidth;
}
public void setWidth(int width) {
if (width > MIN_WIDTH) {
mWidth = width;
} else {
mWidth = MIN_WIDTH;
}
}
public int getHeight() {
return mHeight;
}
public void setHeight(int height) {
if (height > MIN_HEIGHT) {
mHeight = height;
} else {
mHeight = MIN_HEIGHT;
}
}
public void initialize(int width, int height, double density, int stepLength) {
//regularity is important
setHeight(height);
setWidth(width);
setDensity(density);
setStepLength(stepLength);
}
public Map generateMap() {
mMap = new Map(mWidth, mHeight);
int numberPassables = (int) (mHeight * mWidth * mDensity);
//before route generating
//drawing border
for (int i = 0; i < mHeight; i++) {
mMap.setPassable(0, i, true);
mMap.setPassable(mWidth - 1, i, true);
}
for (int i = 0; i < mWidth; i++) {
mMap.setPassable(i, 0, true);
mMap.setPassable(i, mHeight - 1, true);
}
//start point
int x = mWidth / 2;
int y = mHeight / 2;
//last start point
int lastX = x;
int lastY = y;
mMap.setPassable(x, y, true);
//counter of passable points
int passableCounter = mHeight + mWidth - 4;
int course = Courses.STAY;
int step = 0; //step less than mStepLength
//begin drawing trek on the map
for (int iteration = 2 * mHeight * mWidth; iteration > 0; iteration--) {
ArrayList<Integer> courses = getPossibleCourses(x, y);
if (step == mStepLength || !courses.contains(course) || course == Courses.STAY) {
course = changeCourse(courses);
step = 0;
}
switch (course) {
case Courses.RIGHT:
mMap.setPassable(++x, y, true);
step++;
passableCounter++;
break;
case Courses.LEFT:
mMap.setPassable(--x, y, true);
step++;
passableCounter++;
break;
case Courses.UP:
mMap.setPassable(x, ++y, true);
step++;
passableCounter++;
break;
case Courses.DOWN:
mMap.setPassable(x, --y, true);
step++;
passableCounter++;
break;
case Courses.STAY:
//if density is to low then proceed to generating from point on the border
if (passableCounter < numberPassables) {
if (lastX == -1 && lastY == -1) {
x = Math.abs(new Random().nextInt()) % mWidth;
y = Math.abs(new Random().nextInt()) % mHeight;
lastX = x;
lastY = y;
ArrayList<Integer> tmpCourses = getPossibleCourses(x, y);
// size == 1 when it contain only "STAY"
if (tmpCourses.size() != 1 && checkStartPoint(x, y)) {
mMap.setPassable(x, y, true);
}
} else {
x = lastX;
y = lastY;
lastX = -1;
lastY = -1;
}
} else {
iteration = 0;
}
break;
}
}
fillFood();
return mMap;
}
public Map fillFood() {
for (int i = 0; i < mMap.getHeight(); i++) {
for (int j = 0; j < mMap.getWidth(); j++) {
mMap.setFood(j, i, mMap.isPassable(j, i));
}
}
return mMap;
}
//check point to set passable
private boolean checkStartPoint(int x, int y) {
try {
//ooo
//xoo
//xxo
if (mMap.isPassable(x - 1, y)
&& mMap.isPassable(x - 1, y - 1)
&& mMap.isPassable(x, y - 1)) {
return false;
}
//xxo
//xoo
//ooo
if (mMap.isPassable(x - 1, y)
&& mMap.isPassable(x - 1, y + 1)
&& mMap.isPassable(x, y + 1)) {
return false;
}
//oxx
//oox
//ooo
if (mMap.isPassable(x, y + 1)
&& mMap.isPassable(x + 1, y + 1)
&& mMap.isPassable(x + 1, y)) {
return false;
}
//ooo
//oox
//oxx
if (mMap.isPassable(x, y - 1)
&& mMap.isPassable(x + 1, y - 1)
&& mMap.isPassable(x + 1, y)) {
return false;
}
} catch (ArrayIndexOutOfBoundsException e) {
return false;
}
return true;
}
private int changeCourse(ArrayList<Integer> courses) {
int numberAvailableCourses = courses.size() - 1;
//if all courses without STAY were removing. It apart case to exclude from random
if (numberAvailableCourses == 0) {
return Courses.STAY;
}
Random rand = new Random();
int courseIndex = Math.abs(rand.nextInt()) % numberAvailableCourses;
int course = courses.get(courseIndex).intValue();
return course;
}
private ArrayList<Integer> getPossibleCourses(int x, int y) {
//create an array with courses to remove part of them from the choice
ArrayList<Integer> courses = new ArrayList<Integer>();
Integer right = new Integer(Courses.RIGHT);
Integer up = new Integer(Courses.UP);
Integer left = new Integer(Courses.LEFT);
Integer down = new Integer(Courses.DOWN);
Integer stay = new Integer(Courses.STAY);
courses.add(right);
courses.add(up);
courses.add(left);
courses.add(down);
courses.add(stay);
//Global condition: passable points can`t be side by side
if (x - 1 < 0 || y + 1 >= mHeight || mMap.isPassable(x - 1, y + 1)) {
//xoo
//ooo
//ooo
courses.remove(left);
courses.remove(up);
}
if (x + 1 >= mWidth || y + 1 >= mHeight || mMap.isPassable(x + 1, y + 1)) {
//oox
//ooo
//ooo
courses.remove(right);
courses.remove(up);
}
if (x + 1 >= mWidth || y - 1 < 0 || mMap.isPassable(x + 1, y - 1)) {
//ooo
//ooo
//oox
courses.remove(right);
courses.remove(down);
}
if (x - 1 < 0 || y - 1 < 0 || mMap.isPassable(x - 1, y - 1)) {
//ooo
//ooo
//xoo
courses.remove(left);
courses.remove(down);
}
if (y + 1 >= mHeight || mMap.isPassable(x, y + 1)) {
//oxo
//ooo
//ooo
courses.remove(up);
}
if (y - 1 < 0 || mMap.isPassable(x, y - 1)) {
//ooo
//ooo
//oxo
courses.remove(down);
}
if (x - 1 < 0 || mMap.isPassable(x - 1, y)) {
//ooo
//xoo
//ooo
courses.remove(left);
}
if (x + 1 >= mWidth || mMap.isPassable(x + 1, y)) {
//ooo
//oox
//ooo
courses.remove(right);
}
return courses;
}
}