00001
00002 import java.awt.*;
00003 import java.awt.image.*;
00004 import java.util.*;
00005 import java.util.ArrayList;
00006 import java.util.List;
00007 import java.util.concurrent.Semaphore;
00008
00010
00030 public final class TerrainMap
00031 {
00033 private static final double SQRT_2 = Math.sqrt(2.0);
00034
00036 public enum MovementType
00037 {
00038 Chess, Euclidean, Manhattan
00039 }
00040
00041 private final MovementType moveType;
00042
00044 private final byte[][] Board;
00045
00047 private final int[][] Uncovered;
00048
00050 private ArrayList<Point> path;
00051
00053 private final int Width;
00054
00056 private final int Height;
00057
00059 private final Point StartPoint;
00060
00062 private final Point EndPoint;
00063
00065 private int uncoveredCounter;
00066
00068 private final boolean chaotic;
00069
00071 private final Semaphore sem = new Semaphore(1, true);
00072
00074 private final Timer timer = new Timer();
00075
00077 private final TimerTask task = new TimerTask()
00078 {
00079 @Override
00080 public void run()
00081 {
00082 runChaos();
00083 }
00084 };
00085
00087
00102 public TerrainMap(final int width, final int height, final TerrainGenerator terraGen, final MovementType moveType, final boolean chaotic)
00103 {
00104 this.Width = width;
00105 this.Height = height;
00106 this.moveType = moveType;
00107
00108
00109 StartPoint = new Point((int) (0.5 * width), (int) (0.5 * height));
00110 EndPoint = new Point((int) (0.9 * width), (int) (0.9 * height));
00111
00112
00113 Board = terraGen.getTerrain();
00114
00115
00116 Uncovered = new int[width][height];
00117 for(int x = 0; x < width; x++)
00118 {
00119 Arrays.fill(Uncovered[x], 0);
00120 }
00121 uncoveredCounter = 0;
00122
00123 this.chaotic = chaotic;
00124 }
00125
00127
00130 public Point getStartPoint()
00131 {
00132 return new Point(StartPoint);
00133 }
00134
00136
00139 public Point getEndPoint()
00140 {
00141 return new Point(EndPoint);
00142 }
00143
00145
00153 public boolean validTile(final int x, final int y)
00154 {
00155 return x >= 0 && x < Width && y >= 0 && y < Height;
00156 }
00157
00159
00166 public boolean validTile(final Point pt)
00167 {
00168 return validTile(pt.x, pt.y);
00169 }
00170
00172
00181 public int getTile(final int x, final int y)
00182 {
00183 if(!validTile(x, y))
00184 {
00185 throw new IndexOutOfBoundsException("Tried to access (" + x + ", " + y + ") " +
00186 "in a board of dimension " + Width + " x " + Height);
00187 }
00188 down();
00189 if(Uncovered[x][y] == 0)
00190 {
00191 uncoveredCounter++;
00192 Uncovered[x][y] = uncoveredCounter;
00193 }
00194 up();
00195 return Board[x][y] & 0xFF;
00196 }
00197
00199
00207 public int getTile(final Point pt)
00208 {
00209 return getTile(pt.x, pt.y);
00210 }
00211
00213
00220 public boolean isAdjacent(final Point p1, final Point p2)
00221 {
00222 final int dx = Math.abs(p1.x - p2.x);
00223 final int dy = Math.abs(p1.y - p2.y);
00224 if((moveType == MovementType.Manhattan) && (dx * dy != 0))
00225 return false;
00226 else
00227 return dx <= 1 && dy <= 1 && (dx != 0 || dy != 0);
00228 }
00229
00231
00238 public boolean isDiagonal(final Point p1, final Point p2)
00239 {
00240 final int dx = Math.abs(p1.x - p2.x);
00241 final int dy = Math.abs(p1.y - p2.y);
00242 if(dx * dy != 0)
00243 return false;
00244 else
00245 return dx <= 1 && dy <= 1 && (dx != 0 || dy != 0);
00246 }
00247
00249
00255 public Point[] getNeighbors(final Point pt)
00256 {
00257 final ArrayList<Point> neighbors = new ArrayList<Point>();
00258 for(int dx = -1; dx <= 1; dx++)
00259 {
00260 for(int dy = -1; dy <= 1; dy++)
00261 {
00262 if(dx == 0 && dy == 0)
00263 continue;
00264 final Point temp = new Point(pt.x + dx, pt.y + dy);
00265 if(validTile(temp) && isAdjacent(pt, temp))
00266 neighbors.add(temp);
00267 }
00268 }
00269 return neighbors.toArray(new Point[0]);
00270 }
00271
00273
00282 public double getCost(final Point p1, final Point p2)
00283 {
00284 if(!isAdjacent(p1, p2))
00285 throw new RuntimeException("Cannot get the cost of non-adjacent points");
00286 final double mult;
00287 if(moveType == MovementType.Euclidean && isDiagonal(p1, p2))
00288 mult = SQRT_2;
00289 else
00290 mult = 1.0;
00291 return mult * Math.exp(Math.abs(getTile(p1) - getTile(p2)));
00292 }
00293
00295 public int getWidth()
00296 {
00297 return Width;
00298 }
00299
00301 public int getHeight()
00302 {
00303 return Height;
00304 }
00305
00307
00315 public double findPath(final AIModule module)
00316 {
00317 if(chaotic)
00318 timer.scheduleAtFixedRate(task, 1000, 1000);
00319 final List<Point> ai_path = module.createPath(this);
00320 timer.cancel();
00321 return verifyPath(ai_path);
00322 }
00323
00325
00333 private double verifyPath(final List<Point> path)
00334 {
00335
00336 if(this.path != null)
00337 throw new IllegalStateException("Attempted to register a path after a path has already been registered.");
00338
00339
00340 if(path == null || path.isEmpty())
00341 {
00342 throw new RuntimeException("Empty Path");
00343 }
00344
00345
00346 if(!path.get(0).equals(StartPoint) || !path.get(path.size() - 1).equals(EndPoint))
00347 {
00348 throw new RuntimeException("Invalid Path");
00349 }
00350
00351
00352 for(int index = 0; index < path.size() - 1; index++)
00353 {
00354 if(!isAdjacent(path.get(index), path.get(index + 1)))
00355 {
00356 throw new RuntimeException("Invalid Path");
00357 }
00358 }
00359
00360 double PathCost = 0;
00361
00362 for(int index = 0; index < path.size() - 1; index++)
00363 {
00364 PathCost += getCost(path.get(index), path.get(index + 1));
00365 }
00366
00367
00368 this.path = new ArrayList<Point>(path);
00369
00370 return PathCost;
00371 }
00372
00374 public int getNumVisited()
00375 {
00376 int numVisited = 0;
00377
00378 for(int i = 0; i < Width; i++)
00379 {
00380 for(int j = 0; j < Height; j++)
00381 {
00382 if(Uncovered[i][j] != 0)
00383 {
00384 numVisited++;
00385 }
00386 }
00387 }
00388 return numVisited;
00389 }
00390
00392
00399 public BufferedImage createImage()
00400 {
00401
00402 if(path == null)
00403 {
00404 throw new IllegalStateException("Attempted to create map image, but path isn't set.");
00405 }
00406
00407
00408 final BufferedImage im = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_RGB);
00409 final WritableRaster raster = im.getRaster();
00410 final int[] pixels = new int[Width * Height * 3];
00411 for(int x = 0; x < Width; x++)
00412 {
00413 for(int y = 0; y < Height; y++)
00414 {
00415 final int offset = (y * Width + x) * 3;
00416 final int val = Board[x][y] & 0xFF;
00417
00418 if(Uncovered[x][y] != 0)
00419 {
00420 pixels[offset] = 0xFF;
00421 }
00422 else
00423 {
00424 pixels[offset] = val;
00425 }
00426
00427 pixels[offset + 1] = pixels[offset + 2] = val;
00428 }
00429 }
00430
00431 raster.setPixels(0, 0, Width, Height, pixels);
00432
00433
00434 for(final Point pt : path)
00435 {
00436 im.setRGB(pt.x, pt.y, Color.BLUE.getRGB());
00437 }
00438
00439
00440 im.setRGB(StartPoint.x, StartPoint.y, Color.GREEN.getRGB());
00441 im.setRGB(EndPoint.x, EndPoint.y, Color.GREEN.getRGB());
00442
00443 return im;
00444 }
00445
00447
00454 public BufferedImage createContourImage()
00455 {
00456
00457 if(path == null)
00458 {
00459 throw new IllegalStateException("Attempted to create map image, but path isn't set.");
00460 }
00461
00462
00463 final BufferedImage im = new BufferedImage(Width, Height, BufferedImage.TYPE_INT_RGB);
00464 final WritableRaster raster = im.getRaster();
00465 final int[] pixels = new int[Width * Height * 3];
00466 for(int x = 0; x < Width; x++)
00467 {
00468 for(int y = 0; y < Height; y++)
00469 {
00470 final int offset = (y * Width + x) * 3;
00471 pixels[offset] = (int)(0.5 + 0xFF * Uncovered[x][y] * 1.0 / uncoveredCounter);
00472 pixels[offset + 1] = pixels[offset + 2] = 0;
00473 }
00474 }
00475 raster.setPixels(0, 0, Width, Height, pixels);
00476 return im;
00477 }
00478
00480 private BufferedImage toBufferedImage()
00481 {
00482 final BufferedImage im = new BufferedImage(Width, Height, BufferedImage.TYPE_BYTE_GRAY);
00483 final WritableRaster raster = im.getRaster();
00484 final int[] pixels = new int[Width * Height];
00485 for(int x = 0; x < Width; x++)
00486 {
00487 for(int y = 0; y < Height; y++)
00488 {
00489 pixels[y * Width + x] = Board[x][y];
00490 }
00491 }
00492 raster.setPixels(0, 0, Width, Height, pixels);
00493 return im;
00494 }
00495
00497 private void toBoard(final BufferedImage im)
00498 {
00499 if(im.getType() != BufferedImage.TYPE_BYTE_GRAY || im.getWidth() != Width || im.getHeight() != Height)
00500 throw new RuntimeException("Something very bad has happened");
00501 final WritableRaster raster = im.getRaster();
00502 final int[] pixels = raster.getPixels(0, 0, Width, Height, new int[Width * Height]);
00503 for(int x = 0; x < Width; x++)
00504 {
00505 for(int y = 0; y < Height; y++)
00506 {
00507 Board[x][y] = (byte)pixels[y * Width + x];
00508 }
00509 }
00510 }
00511
00513 private void runChaos()
00514 {
00515 final float f = -0.001f;
00516 final Kernel kernel = new Kernel(3, 3,
00517 new float[] {
00518 f, f, f,
00519 f, 1.008f, f,
00520 f, f, f});
00521 down();
00522 toBoard(new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null).filter(toBufferedImage(), null));
00523 up();
00524 }
00525
00527 private void down()
00528 {
00529 try
00530 {
00531 sem.acquire();
00532 }
00533 catch(InterruptedException ex)
00534 {
00535 throw new RuntimeException("down function was interupted");
00536 }
00537 }
00538
00540 private void up()
00541 {
00542 sem.release();
00543 }
00544 }