mirror of
https://github.com/tu-darmstadt-informatik/Grundlagen-der-Informatik-1.git
synced 2026-01-20 20:01:26 +00:00
401 lines
8.4 KiB
Java
401 lines
8.4 KiB
Java
package TrainSimulation;
|
|
|
|
import exceptions.InvalidAreaException;
|
|
import exceptions.InvalidCoordinatesException;
|
|
import exceptions.NoObstacleException;
|
|
import exceptions.NoPlainsException;
|
|
import exceptions.NotACityException;
|
|
import exceptions.NotEnoughCitiesException;
|
|
|
|
public class TrainSimulation {
|
|
|
|
//Constants for the Map
|
|
public final char map_city = '#';
|
|
public final char map_plain = '0';
|
|
public final char map_swamp = '~';
|
|
public final char map_mountain = '^';
|
|
public final char map_train = '=';
|
|
|
|
/**
|
|
* Variable to store Map-Char-Representation
|
|
*/
|
|
private char[][] area;
|
|
|
|
/**
|
|
* Constructor sets Map-Char-Representation and throws Exceptions
|
|
* if its not valid.
|
|
*
|
|
* @param area Map-Char-Representation
|
|
* @throws InvalidAreaException
|
|
*/
|
|
public TrainSimulation(char[][] area) throws InvalidAreaException
|
|
{
|
|
setArea(area); //use setter-method to check given area
|
|
}
|
|
|
|
/**
|
|
* Flood-Algorithm to replace all Positions which are reachable
|
|
* from pos (x/y) with map_train.
|
|
*
|
|
* A Position is reachable when (x/y) == map_plain || (x/y) == map_city.
|
|
*
|
|
* Cities are not replaced.
|
|
*
|
|
* Modifies this.area
|
|
*
|
|
* @param x X-Coordinate
|
|
* @param y >-Coordinate
|
|
*/
|
|
private void floodArrayWithTrain(int x, int y)
|
|
{
|
|
//check if position is valid
|
|
if(areCoordinatesValid(x, y))
|
|
{
|
|
//skip/anchor -> area not reachable
|
|
if(area[x][y] != map_plain && area[x][y] != map_city)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//replace only map_plain not map_city
|
|
if(area[x][y] == map_plain)
|
|
{
|
|
area[x][y] = map_train;
|
|
}
|
|
|
|
//flood-algorithm-recursive call
|
|
floodArrayWithTrain(x,y+1);
|
|
floodArrayWithTrain(x,y-1);
|
|
floodArrayWithTrain(x+1,y);
|
|
floodArrayWithTrain(x-1,y);
|
|
}
|
|
}
|
|
|
|
private boolean areCoordinatesValid(IntChar[][] array, int x, int y)
|
|
{
|
|
if( x < 0 ||
|
|
y < 0 ||
|
|
array.length <= x ||
|
|
array[x].length <= y)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private boolean areCoordinatesValid(char[][] array, int x, int y)
|
|
{
|
|
if( x < 0 ||
|
|
y < 0 ||
|
|
array.length <= x ||
|
|
array[x].length <= y)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private boolean areCoordinatesValid(int x, int y)
|
|
{
|
|
return areCoordinatesValid(area, x, y);
|
|
}
|
|
|
|
/**
|
|
* Replaces all map_plain in this.area
|
|
* if field is reachable
|
|
*
|
|
* Coordinate have to point to a city.
|
|
*
|
|
* @param x X-Coordinate
|
|
* @param y Y-Coordinate
|
|
* @throws InvalidCoordinatesException
|
|
*/
|
|
public void reachable(int x, int y) throws InvalidCoordinatesException
|
|
{
|
|
//check if coordinates are valid
|
|
if(!areCoordinatesValid(x, y))
|
|
{
|
|
throw new InvalidCoordinatesException("Error: Coordinates are not valid");
|
|
}
|
|
|
|
//check if position is a city
|
|
if(area[x][y] != map_city)
|
|
{
|
|
throw new NotACityException();
|
|
}
|
|
|
|
//flood-algorithm
|
|
floodArrayWithTrain(x,y);
|
|
}
|
|
|
|
/**
|
|
* Floods given are with Costs-parameters; only replaces
|
|
* reachable fields.
|
|
*
|
|
* @param array Array with chars/costs-values
|
|
* @param x X-Coordinate
|
|
* @param y Y-Coordinate
|
|
* @param value Current costs for the field, if its reachable
|
|
* @return new Array
|
|
*/
|
|
private IntChar[][] floodArrayWithCosts(IntChar[][] array, int x, int y, int value)
|
|
{
|
|
//check Coordinates are valid
|
|
if(areCoordinatesValid(array,x, y)) //recursion-anchor
|
|
{
|
|
//is Coordinate an integer and value of the field is > current costs
|
|
// -> replace with current costs
|
|
if(array[x][y].isInt() && array[x][y].getInt() > value)
|
|
{
|
|
array[x][y].setInt(value);
|
|
} else
|
|
{
|
|
//is Coordinate a char and value of the field is map_plain or map_city
|
|
// -> set current costs @ current field
|
|
if( array[x][y].isChar() &&
|
|
(array[x][y].getChar() == map_plain ||
|
|
array[x][y].getChar() == map_city)
|
|
)
|
|
{
|
|
array[x][y].setInt(value);
|
|
} else
|
|
{
|
|
return array; //recursion-anchor
|
|
}
|
|
}
|
|
|
|
//Flood-Algorithm
|
|
array = floodArrayWithCosts(array,x,y+1,value+1);
|
|
array = floodArrayWithCosts(array,x,y-1,value+1);
|
|
array = floodArrayWithCosts(array,x+1,y,value+1);
|
|
array = floodArrayWithCosts(array,x-1,y,value+1);
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
/**
|
|
* Returns a integer-field(2-dim) with "costs" of the
|
|
* all reachable fields from pos (x/y)
|
|
*
|
|
* @param x X-Coordinate
|
|
* @param y Y-Coordinate
|
|
* @return integer-field(2-dim)
|
|
* @throws InvalidCoordinatesException
|
|
*/
|
|
public int[][] analyzeCosts(int x, int y) throws InvalidCoordinatesException
|
|
{
|
|
//Check if array is ok
|
|
if(!isAreaSizeOk())
|
|
{
|
|
//should not happen, but to be sure
|
|
throw new InvalidCoordinatesException("Error: Area-Array was not ok");
|
|
}
|
|
|
|
//Check Coordinates
|
|
if(!areCoordinatesValid(x, y))
|
|
{
|
|
throw new InvalidCoordinatesException("Error: Coordinates are not valid");
|
|
}
|
|
|
|
//check if position is a city
|
|
if(area[x][y] != map_city)
|
|
{
|
|
throw new NotACityException();
|
|
}
|
|
|
|
//New array to operate on
|
|
IntChar[][] tarea = new IntChar[area.length][area[0].length];
|
|
for(int i=0; i<area.length;i++)
|
|
{
|
|
for(int j=0; j<area[i].length;j++)
|
|
{
|
|
tarea[i][j] = new IntChar(); //create element
|
|
tarea[i][j].setChar(area[i][j]); //set to value from this.area
|
|
}
|
|
}
|
|
|
|
//flood Area with Costs-Values
|
|
tarea = floodArrayWithCosts(tarea,x,y,0);
|
|
|
|
//size is ok! -> created object locally
|
|
int[][] result = new int[tarea.length][tarea[0].length];
|
|
|
|
//Convert IntChar[][] -> int[][]
|
|
for(int i=0;i<tarea.length;i++)
|
|
{
|
|
for(int j=0;j<tarea[i].length;j++)
|
|
{
|
|
if(tarea[i][j].isInt()) //Calculated Costs in this field?
|
|
{
|
|
//set calculated Costs to result-array
|
|
result[i][j] = tarea[i][j].getInt();
|
|
} else
|
|
{
|
|
//if value is still a char -> set to -1
|
|
result[i][j] = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
//return int[][]
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @return String Returns a String-Representation of the current this.area property.
|
|
*/
|
|
public String toString()
|
|
{
|
|
//result variable
|
|
String result = "";
|
|
|
|
//check if area has a length
|
|
if(area.length > 0)
|
|
{
|
|
for(int i=0; i<area[0].length; i++) //count lines
|
|
{
|
|
for(int j=0;j<area.length;j++) //count within lines(columns)
|
|
{
|
|
//add char to result
|
|
result += area[j][i];
|
|
}
|
|
|
|
//end of line -> add return
|
|
result += "\n";
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Returns true if element is at least count-times present in this.area. else false
|
|
*
|
|
* @param element Element to look for
|
|
* @param count How many times should the Element appear before returning true
|
|
* @return All required Elements found ? true : false
|
|
*/
|
|
private boolean hasElement(char element, int count)
|
|
{
|
|
for(int i=0; i<area.length;i++)
|
|
{
|
|
for(int j=0; j<area[i].length;j++)
|
|
{
|
|
if(area[i][j] == element) //pos = element?
|
|
{
|
|
count--; //count down
|
|
|
|
if(count <= 0) //no more elements required
|
|
{
|
|
return true; //all found -> true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false; //not all found -> false
|
|
}
|
|
|
|
/**
|
|
* @return returns true if this.area has two or more cities else false
|
|
*/
|
|
private boolean hasTwoCities()
|
|
{
|
|
return hasElement(map_city,2);
|
|
}
|
|
|
|
/**
|
|
* @return returns true if this.area has one or more plains else false
|
|
*/
|
|
private boolean hasPlainsElement()
|
|
{
|
|
return hasElement(map_plain,1);
|
|
}
|
|
|
|
/**
|
|
* @return returns true if this.area has one or more terrainObstacle else false
|
|
*
|
|
* TerrainObstacle = map_mountain, map_swamp
|
|
*/
|
|
private boolean hasTerrainObstacle()
|
|
{
|
|
if(hasElement(map_swamp,1))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return hasElement(map_mountain,1);
|
|
}
|
|
|
|
/**
|
|
* Checks size of the area, returns true if its ok, else false
|
|
*/
|
|
private boolean isAreaSizeOk()
|
|
{
|
|
if(area.length == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int length = area[0].length;
|
|
|
|
for(int i=1; i<area.length;i++)
|
|
{
|
|
if(area[i].length != length)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns true if this.area is valid
|
|
*
|
|
* @return
|
|
* @throws InvalidAreaException
|
|
*/
|
|
private boolean isAreaValid() throws InvalidAreaException
|
|
{
|
|
if(!isAreaSizeOk())
|
|
{
|
|
throw new InvalidAreaException("Error: Area-Size(lines/cloumns) was not ok");
|
|
}
|
|
|
|
//Two-Cities
|
|
if(!hasTwoCities())
|
|
{
|
|
throw new NotEnoughCitiesException();
|
|
}
|
|
|
|
//One-Plain
|
|
if(!hasPlainsElement())
|
|
{
|
|
throw new NoPlainsException();
|
|
}
|
|
|
|
//One-TerrainObstacle
|
|
if(!hasTerrainObstacle())
|
|
{
|
|
throw new NoObstacleException();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public char[][] getArea() {
|
|
return area;
|
|
}
|
|
public void setArea(char[][] area) throws InvalidAreaException {
|
|
|
|
this.area = area;
|
|
|
|
isAreaValid();
|
|
}
|
|
|
|
}
|