2020-02-22 14:43:24 +01:00
|
|
|
package inf112.fiasko.roborally.objects;
|
2020-02-22 14:40:00 +01:00
|
|
|
|
2020-03-16 20:06:35 +01:00
|
|
|
import inf112.fiasko.roborally.element_properties.Direction;
|
2020-03-23 13:46:55 +01:00
|
|
|
import inf112.fiasko.roborally.element_properties.ParticleType;
|
2020-03-16 20:06:35 +01:00
|
|
|
import inf112.fiasko.roborally.element_properties.Position;
|
|
|
|
import inf112.fiasko.roborally.element_properties.RobotID;
|
|
|
|
import inf112.fiasko.roborally.element_properties.TileType;
|
|
|
|
import inf112.fiasko.roborally.element_properties.WallType;
|
|
|
|
|
2020-02-22 14:40:00 +01:00
|
|
|
import java.util.ArrayList;
|
2020-02-22 23:27:59 +01:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
2020-02-22 14:40:00 +01:00
|
|
|
|
2020-02-22 14:53:10 +01:00
|
|
|
/**
|
|
|
|
* This class represents a board
|
|
|
|
*/
|
2020-02-22 14:40:00 +01:00
|
|
|
public class Board {
|
2020-02-22 23:27:59 +01:00
|
|
|
private int boardHeight;
|
|
|
|
private int boardWidth;
|
|
|
|
private IGrid<Wall> walls;
|
|
|
|
private IGrid<Tile> tiles;
|
2020-03-23 13:46:55 +01:00
|
|
|
private IGrid<Particle> particles;
|
2020-02-22 23:27:59 +01:00
|
|
|
private Map<RobotID, Robot> robots;
|
2020-02-26 20:20:46 +01:00
|
|
|
private List<Robot> deadRobots;
|
2020-02-22 14:40:00 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes the board
|
2020-02-22 23:27:59 +01:00
|
|
|
* @param tiles A grid containing all tiles
|
|
|
|
* @param walls A grid containing all walls
|
|
|
|
* @param robots A list of all robots in the game
|
|
|
|
*/
|
|
|
|
public Board(IGrid<Tile> tiles, IGrid<Wall> walls, List<Robot> robots) {
|
|
|
|
if (walls.getWidth() != tiles.getWidth() || walls.getHeight() != tiles.getHeight()) {
|
|
|
|
throw new IllegalArgumentException("The grids in the input don't have the same dimensions.");
|
|
|
|
}
|
|
|
|
this.robots = new HashMap<>();
|
|
|
|
for (Robot robot : robots) {
|
|
|
|
if (this.robots.get(robot.getRobotId()) != null) {
|
|
|
|
throw new IllegalArgumentException("There can't be two robots with the same robot id.");
|
|
|
|
}
|
|
|
|
this.robots.put(robot.getRobotId(), robot);
|
|
|
|
}
|
|
|
|
this.boardWidth = tiles.getWidth();
|
|
|
|
this.boardHeight = tiles.getHeight();
|
|
|
|
this.walls = walls;
|
|
|
|
this.tiles = tiles;
|
2020-03-23 13:46:55 +01:00
|
|
|
this.particles = new Grid<>(tiles.getWidth(), tiles.getHeight());
|
2020-02-26 20:20:46 +01:00
|
|
|
this.deadRobots = new ArrayList<>();
|
2020-02-22 23:27:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the height of the board
|
|
|
|
* @return The height of the board
|
2020-02-22 14:40:00 +01:00
|
|
|
*/
|
2020-02-22 23:27:59 +01:00
|
|
|
public int getBoardHeight() {
|
|
|
|
return boardHeight;
|
2020-02-22 14:40:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-02-22 23:27:59 +01:00
|
|
|
* Gets the width of the board
|
|
|
|
* @return The width of the board
|
|
|
|
*/
|
|
|
|
public int getBoardWidth() {
|
|
|
|
return boardWidth;
|
|
|
|
}
|
|
|
|
|
2020-02-23 20:26:11 +01:00
|
|
|
/**
|
|
|
|
* Gets all alive robots from the board
|
|
|
|
* @return A list of alive robots
|
|
|
|
*/
|
|
|
|
public List<Robot> getAliveRobots() {
|
2020-03-09 12:32:11 +01:00
|
|
|
List<Robot> robotsCopy = new ArrayList<>(robots.values());
|
|
|
|
robotsCopy.replaceAll(Robot::copy);
|
|
|
|
return robotsCopy;
|
2020-02-23 20:26:11 +01:00
|
|
|
}
|
|
|
|
|
2020-02-24 18:07:26 +01:00
|
|
|
/**
|
|
|
|
* Gets all the tiles from the board
|
|
|
|
* @return A list of all tiles on the board
|
|
|
|
*/
|
|
|
|
public List<Tile> getTiles() {
|
|
|
|
return getAllElementsFromGrid(tiles);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets all the walls from the board
|
|
|
|
* @return A list of all the walls on the board
|
|
|
|
*/
|
|
|
|
public List<Wall> getWalls() {
|
|
|
|
return getAllElementsFromGrid(walls);
|
|
|
|
}
|
|
|
|
|
2020-03-23 13:46:55 +01:00
|
|
|
/**
|
|
|
|
* Gets all the particles from the board
|
|
|
|
* @return A list of all the particles on the board
|
|
|
|
*/
|
|
|
|
public List<Particle> getParticles() {
|
|
|
|
return getAllElementsFromGrid(particles);
|
|
|
|
}
|
|
|
|
|
2020-02-23 20:10:28 +01:00
|
|
|
/**
|
|
|
|
* Rotates a robot to the right
|
|
|
|
* @param robotID The id of the robot to rotate
|
|
|
|
*/
|
|
|
|
public void rotateRobotLeft(RobotID robotID) {
|
|
|
|
Robot robot = robots.get(robotID);
|
|
|
|
Direction newDirection = Direction.getLeftRotatedDirection(robot.getFacingDirection());
|
|
|
|
robot.setFacingDirection(newDirection);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Rotates a robot to the left
|
|
|
|
* @param robotID The id of the robot to rotate
|
|
|
|
*/
|
|
|
|
public void rotateRobotRight(RobotID robotID) {
|
|
|
|
Robot robot = robots.get(robotID);
|
|
|
|
Direction newDirection = Direction.getRightRotatedDirection(robot.getFacingDirection());
|
|
|
|
robot.setFacingDirection(newDirection);
|
|
|
|
}
|
|
|
|
|
2020-02-22 23:27:59 +01:00
|
|
|
/**
|
2020-02-25 17:45:39 +01:00
|
|
|
* Moves a robot one unit forward according to the direction it's currently facing
|
|
|
|
* @param robotID The robot to move
|
|
|
|
*/
|
|
|
|
public void moveRobotForward(RobotID robotID) {
|
|
|
|
moveRobot(robotID, robots.get(robotID).getFacingDirection());
|
|
|
|
}
|
|
|
|
|
2020-03-10 17:29:36 +01:00
|
|
|
/**
|
|
|
|
* Moves a robot one unit backwards according to the direction it's currently facing
|
|
|
|
* @param robotID The robot to move
|
|
|
|
*/
|
|
|
|
public void reverseRobot(RobotID robotID) {
|
|
|
|
moveRobot(robotID, Direction.getReverseDirection(robots.get(robotID).getFacingDirection()));
|
|
|
|
}
|
|
|
|
|
2020-02-25 17:45:39 +01:00
|
|
|
/**
|
2020-02-22 23:27:59 +01:00
|
|
|
* Moves a robot one unit in a specified direction
|
|
|
|
* @param robotID ID of the robot to move
|
|
|
|
* @param direction The direction to move the robot
|
|
|
|
* @return True if the robot moved away from its old position
|
|
|
|
*/
|
|
|
|
public boolean moveRobot(RobotID robotID, Direction direction) {
|
|
|
|
Robot robot = robots.get(robotID);
|
|
|
|
Position robotPosition = robot.getPosition();
|
|
|
|
Position newPosition = getNewPosition(robotPosition, direction);
|
|
|
|
//There is a wall blocking the robot. It can't proceed.
|
2020-03-17 14:37:49 +01:00
|
|
|
if (moveIsStoppedByWall(robotPosition, newPosition, direction)) {
|
2020-02-22 23:27:59 +01:00
|
|
|
return false;
|
|
|
|
}
|
2020-03-10 18:18:32 +01:00
|
|
|
//Robot tried to go outside of the map. Kill it.
|
|
|
|
if (killRobotIfGoesOutsideMap(robot, newPosition)) {
|
|
|
|
return true;
|
|
|
|
}
|
2020-02-22 23:27:59 +01:00
|
|
|
//If another robot is blocking this robot's path, try to shove it.
|
|
|
|
if (hasRobotOnPosition(newPosition)) {
|
|
|
|
RobotID otherRobotID = getRobotOnPosition(newPosition);
|
|
|
|
if (otherRobotID != null && !moveRobot(otherRobotID, direction)) {
|
2020-02-23 20:30:57 +01:00
|
|
|
//The other robot can't be shoved. Give up.
|
2020-02-22 23:27:59 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-02-23 00:03:20 +01:00
|
|
|
robot.setPosition(newPosition);
|
2020-02-23 20:30:57 +01:00
|
|
|
//Some tiles may kill the robot if stepped on.
|
|
|
|
killRobotIfStepsOnDangerousTile(robot, newPosition);
|
2020-02-23 00:03:20 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-03-25 13:36:35 +01:00
|
|
|
/**
|
|
|
|
* Checks whether a given tile is a conveyor belt
|
|
|
|
* @param tile The tile to check
|
|
|
|
* @return True if the tile is a conveyor belt
|
|
|
|
*/
|
|
|
|
public boolean isConveyorBelt(Tile tile) {
|
|
|
|
if (tile == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
switch (tile.getTileType()) {
|
|
|
|
case CONVEYOR_BELT_SLOW:
|
|
|
|
case CONVEYOR_BELT_FAST:
|
|
|
|
case CONVEYOR_BELT_FAST_LEFT:
|
|
|
|
case CONVEYOR_BELT_FAST_RIGHT:
|
|
|
|
case CONVEYOR_BELT_FAST_SIDE_ENTRANCE_LEFT:
|
|
|
|
case CONVEYOR_BELT_FAST_SIDE_ENTRANCE_RIGHT:
|
|
|
|
case CONVEYOR_BELT_FAST_SIDE_ENTRANCES:
|
|
|
|
case CONVEYOR_BELT_SLOW_LEFT:
|
|
|
|
case CONVEYOR_BELT_SLOW_RIGHT:
|
|
|
|
case CONVEYOR_BELT_SLOW_SIDE_ENTRANCE_LEFT:
|
|
|
|
case CONVEYOR_BELT_SLOW_SIDE_ENTRANCE_RIGHT:
|
|
|
|
case CONVEYOR_BELT_SLOW_SIDE_ENTRANCES:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Teleports a robot to some position without verification
|
|
|
|
*
|
|
|
|
* Be quite careful about using this method. No validation will me done. The robot will magically disappear from
|
|
|
|
* one position and appear on another, hence the name. This method should only be used when the new position has
|
|
|
|
* been confirmed available.
|
|
|
|
*
|
|
|
|
* @param robotID The id of the robot to teleport
|
|
|
|
* @param newPosition The position the robot should teleport to
|
|
|
|
*/
|
|
|
|
public void teleportRobot(RobotID robotID, Position newPosition) {
|
|
|
|
robots.get(robotID).setPosition(newPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether a given conveyor belt is able to move in its direction
|
|
|
|
* @param conveyorBelt The conveyor belt to move
|
|
|
|
* @return True if nothing is blocking its movement
|
|
|
|
*/
|
|
|
|
public boolean conveyorBeltCanMove(BoardElementContainer<Tile> conveyorBelt) {
|
|
|
|
if (!isConveyorBelt(conveyorBelt.getElement())) {
|
|
|
|
throw new IllegalArgumentException("Input to function is of invalid tile type.");
|
|
|
|
}
|
|
|
|
Position conveyorBeltPosition = conveyorBelt.getPosition();
|
|
|
|
Direction conveyorBeltDirection = conveyorBelt.getElement().getDirection();
|
|
|
|
//Ignore conveyor belts without a robot
|
|
|
|
if (!hasRobotOnPosition(conveyorBeltPosition)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Position positionInFront = getNewPosition(conveyorBeltPosition, conveyorBeltDirection);
|
|
|
|
Tile tileInFront = getTileOnPosition(positionInFront);
|
|
|
|
//If a conveyor belt will move the robot outside the map, the move is valid
|
|
|
|
if (!isValidPosition(positionInFront)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
//The tile in front of the robot is not a conveyor belt and has something on it stopping the conveyor belt
|
|
|
|
if (!isConveyorBelt(tileInFront) &&
|
|
|
|
hasFrontConflict(conveyorBeltPosition, positionInFront, conveyorBeltDirection)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
//There is another robot trying to enter the same crossing
|
|
|
|
if (hasCrossingConflict(positionInFront, conveyorBeltDirection)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
//The way forward seems clear
|
|
|
|
if (!hasRobotOnPosition(positionInFront)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return conveyorBeltCanMove(new BoardElementContainer<>(tileInFront, positionInFront));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether a conveyor belt has anything in front of it preventing it from moving forward
|
|
|
|
* @param conveyorBeltPosition The position of the conveyor belt
|
|
|
|
* @param positionInFront The position in front of the conveyor belt
|
|
|
|
* @param conveyorBeltDirection The direction of the conveyor belt
|
|
|
|
* @return True if the conveyor belt cannot move forward
|
|
|
|
*/
|
|
|
|
private boolean hasFrontConflict(Position conveyorBeltPosition, Position positionInFront,
|
|
|
|
Direction conveyorBeltDirection) {
|
|
|
|
//The robot cannot be moved because a wall is blocking it
|
|
|
|
if (moveIsStoppedByWall(conveyorBeltPosition, positionInFront, conveyorBeltDirection)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
//The robot cannot move off the conveyor belt because another robot is blocking it
|
|
|
|
if (hasRobotOnPosition(positionInFront)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Position positionTwoForward = getNewPosition(positionInFront, conveyorBeltDirection);
|
|
|
|
Tile tileTwoForward = getTileOnPosition(positionTwoForward);
|
|
|
|
//If a robot standing on the opposite side of a tile and trying to get to the tile in the middle, none of
|
|
|
|
//the robots should move
|
|
|
|
return (isValidPosition(positionInFront) && isConveyorBelt(tileTwoForward) &&
|
|
|
|
tileTwoForward.getDirection() == Direction.getReverseDirection(conveyorBeltDirection)
|
|
|
|
&& hasRobotOnPosition(positionTwoForward));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether a conveyor belt has a conflict in a crossing
|
|
|
|
* @param crossingPosition The position of the crossing
|
|
|
|
* @param conveyorBeltDirection The direction of the conveyor belt
|
|
|
|
* @return True if there is a conflict. False otherwise
|
|
|
|
*/
|
|
|
|
private boolean hasCrossingConflict(Position crossingPosition, Direction conveyorBeltDirection) {
|
|
|
|
Position frontLeftPosition = getNewPosition(crossingPosition,
|
|
|
|
Direction.getLeftRotatedDirection(conveyorBeltDirection));
|
|
|
|
Tile frontLeftTile = getTileOnPosition(frontLeftPosition);
|
|
|
|
Position frontRightPosition = getNewPosition(crossingPosition,
|
|
|
|
Direction.getRightRotatedDirection(conveyorBeltDirection));
|
|
|
|
Tile frontRightTile = getTileOnPosition(frontRightPosition);
|
|
|
|
Position twoForwardPosition = getNewPosition(crossingPosition, conveyorBeltDirection);
|
|
|
|
Tile twoForwardTile = getTileOnPosition(twoForwardPosition);
|
|
|
|
//If another robot is standing on a conveyor belt pointing to the conveyor belt in front, a conflict happens
|
|
|
|
return (isValidPosition(frontLeftPosition) && isConveyorBelt(frontLeftTile) && frontLeftTile.getDirection() ==
|
|
|
|
Direction.getRightRotatedDirection(conveyorBeltDirection) && hasRobotOnPosition(frontLeftPosition)) ||
|
|
|
|
(isValidPosition(frontRightPosition) && isConveyorBelt(frontRightTile)
|
|
|
|
&& frontRightTile.getDirection() == Direction.getLeftRotatedDirection(conveyorBeltDirection)
|
|
|
|
&& hasRobotOnPosition(frontRightPosition)) ||
|
|
|
|
(isValidPosition(twoForwardPosition) && isConveyorBelt(twoForwardTile)
|
|
|
|
&& twoForwardTile.getDirection() == Direction.getReverseDirection(conveyorBeltDirection)
|
|
|
|
&& hasRobotOnPosition(twoForwardPosition));
|
|
|
|
}
|
|
|
|
|
2020-02-27 18:36:10 +01:00
|
|
|
/**
|
|
|
|
* Moves all dead robots to their backups and makes them part of the board again, and if a robot has no lives
|
|
|
|
* it will be removed from the game.
|
|
|
|
*/
|
|
|
|
public void respawnRobots() {
|
|
|
|
for (Robot robot : deadRobots) {
|
|
|
|
if (robot.getAmountOfLives() > 0) {
|
|
|
|
robot.setPosition(robot.getBackupPosition());
|
|
|
|
robot.setFacingDirection(Direction.NORTH);
|
|
|
|
robots.put(robot.getRobotId(), robot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
deadRobots = new ArrayList<>();
|
|
|
|
}
|
|
|
|
|
2020-03-16 20:06:35 +01:00
|
|
|
/**
|
|
|
|
* Returns a robot id for a robot on a specific position if such a robot exists
|
|
|
|
* @param position The position to check
|
|
|
|
* @return The robot id of the robot on the position or null if there is no robot there
|
|
|
|
*/
|
|
|
|
public RobotID getRobotOnPosition(Position position) {
|
|
|
|
for (RobotID robotID : robots.keySet()) {
|
|
|
|
Robot robot = robots.get(robotID);
|
|
|
|
if (position.equals(robot.getPosition())) {
|
|
|
|
return robotID;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2020-02-27 18:36:10 +01:00
|
|
|
/**
|
|
|
|
* Checks if a specific robot is currently alive on the board
|
|
|
|
* @param robot the ID of the robot you want to check
|
|
|
|
* @return True/False based on if the robot was found.
|
|
|
|
*/
|
|
|
|
public boolean isRobotAlive(RobotID robot) {
|
|
|
|
return robots.containsKey(robot);
|
|
|
|
}
|
|
|
|
|
2020-03-16 15:52:18 +01:00
|
|
|
/**
|
|
|
|
* Updates the flag of the robot if it stands on the correct flag.
|
|
|
|
* @param robotID The RobotID of a robot
|
|
|
|
* @param flagID TileType of the flag we check
|
|
|
|
*/
|
|
|
|
public void updateFlagOnRobot(RobotID robotID, TileType flagID) {
|
|
|
|
Robot robot = robots.get(robotID);
|
|
|
|
int flagNr = flagID.getTileTypeID() % 16;
|
|
|
|
if (flagNr - 1 == robot.getLastFlagVisited()) {
|
|
|
|
robot.setLastFlagVisitedAndUpdateBackupPosition(flagNr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-23 00:03:20 +01:00
|
|
|
/**
|
2020-03-17 14:24:46 +01:00
|
|
|
* Gets the position 1 unit in a specific direction from another position
|
|
|
|
* @param oldPosition The old/current position of the element
|
|
|
|
* @param direction The direction to move the element
|
|
|
|
* @return The new position of the element
|
|
|
|
*/
|
|
|
|
public Position getNewPosition(Position oldPosition, Direction direction) {
|
|
|
|
switch (direction) {
|
|
|
|
case NORTH:
|
|
|
|
return new Position(oldPosition.getXCoordinate(), oldPosition.getYCoordinate() - 1);
|
|
|
|
case SOUTH:
|
|
|
|
return new Position(oldPosition.getXCoordinate(), oldPosition.getYCoordinate() + 1);
|
|
|
|
case EAST:
|
|
|
|
return new Position(oldPosition.getXCoordinate() + 1, oldPosition.getYCoordinate());
|
|
|
|
case WEST:
|
|
|
|
return new Position(oldPosition.getXCoordinate() - 1, oldPosition.getYCoordinate());
|
|
|
|
default:
|
|
|
|
throw new IllegalArgumentException("It's not possible to move in that direction.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-20 17:46:49 +01:00
|
|
|
/**
|
|
|
|
* Fires all lasers on the board and kills any robot that has taken to much damage after all lasers have fired.
|
|
|
|
*/
|
|
|
|
public void fireAllLasers() {
|
|
|
|
List<BoardElementContainer<Wall>> listOfWallLasers = getPositionsOfWallOnBoard(WallType.WALL_LASER_SINGLE,
|
|
|
|
WallType.WALL_LASER_DOUBLE);
|
|
|
|
for (Robot robot:robots.values()) {
|
|
|
|
fireRobotLaser(robot.getPosition(),robot.getFacingDirection());
|
|
|
|
}
|
|
|
|
for (BoardElementContainer<Wall> laser : listOfWallLasers) {
|
|
|
|
fireWallLaser(laser);
|
|
|
|
}
|
2020-03-23 13:46:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Does necessary cleanup after lasers have been fired
|
|
|
|
*/
|
|
|
|
public void doLaserCleanup() {
|
|
|
|
this.particles = new Grid<>(tiles.getWidth(), tiles.getHeight());
|
2020-03-20 17:46:49 +01:00
|
|
|
killAllHeavilyDamagedRobots();
|
|
|
|
}
|
|
|
|
|
2020-03-17 14:24:46 +01:00
|
|
|
/**
|
|
|
|
* Gets the tile on a specific position
|
|
|
|
* @param position The position to get a tile from
|
|
|
|
* @return The tile on the given position
|
|
|
|
*/
|
|
|
|
public Tile getTileOnPosition(Position position) {
|
|
|
|
if (!isValidPosition(position)) {
|
|
|
|
throw new IllegalArgumentException("Position is not on the board!");
|
|
|
|
}
|
|
|
|
return tiles.getElement(position.getXCoordinate(), position.getYCoordinate());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets a list of BoardElementContainers, containing all tiles and positions of given tile types
|
|
|
|
* @param tiles The tiles you want all positions for
|
|
|
|
* @return A list of BoardElementContainers
|
|
|
|
*/
|
|
|
|
public List<BoardElementContainer<Tile>> getPositionsOfTileOnBoard(TileType ... tiles) {
|
|
|
|
List<BoardElementContainer<Tile>> combinedList = new ArrayList<>();
|
|
|
|
for (TileType tile : tiles) {
|
|
|
|
combinedList.addAll(makeTileList(tile, this.tiles));
|
|
|
|
}
|
|
|
|
return combinedList;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets a list of BoardElementContainers, containing all tiles and positions of given wall types
|
|
|
|
* @param walls The walls you want all positions for
|
|
|
|
* @return A list of BoardElementContainers
|
|
|
|
*/
|
|
|
|
public List<BoardElementContainer<Wall>> getPositionsOfWallOnBoard(WallType... walls) {
|
|
|
|
List<BoardElementContainer<Wall>> combinedList = new ArrayList<>();
|
|
|
|
for (WallType wall : walls) {
|
|
|
|
combinedList.addAll(makeTileList(wall, this.walls));
|
|
|
|
}
|
|
|
|
return combinedList;
|
|
|
|
}
|
|
|
|
|
2020-03-20 17:46:49 +01:00
|
|
|
/**
|
|
|
|
* Checks whether there exists a robot on a specific position
|
|
|
|
* @param position The position to check
|
|
|
|
* @return True if there is a robot on the specified position
|
|
|
|
*/
|
|
|
|
public boolean hasRobotOnPosition(Position position) {
|
|
|
|
return getRobotOnPosition(position) != null;
|
|
|
|
}
|
|
|
|
|
2020-02-23 00:03:20 +01:00
|
|
|
/**
|
2020-03-17 14:37:49 +01:00
|
|
|
* Checks if a potential move would be blocked by a wall
|
|
|
|
* @param robotPosition The current position of whatever is trying to move
|
|
|
|
* @param newPosition The position something is trying to move to
|
|
|
|
* @param direction The direction something is going
|
2020-02-23 00:03:20 +01:00
|
|
|
* @return True if a wall would stop its path
|
|
|
|
*/
|
2020-03-25 14:43:54 +01:00
|
|
|
private boolean moveIsStoppedByWall(Position robotPosition, Position newPosition, Direction direction) {
|
2020-03-10 18:35:15 +01:00
|
|
|
return hasWallFacing(robotPosition, direction) || (isValidPosition(newPosition) &&
|
|
|
|
hasWallFacing(newPosition, Direction.getReverseDirection(direction)));
|
|
|
|
}
|
2020-03-10 18:18:32 +01:00
|
|
|
|
2020-03-10 18:35:15 +01:00
|
|
|
/**
|
|
|
|
* Checks whether a given position is valid
|
|
|
|
* @param position The position to test
|
|
|
|
* @return True if the position is valid. False otherwise
|
|
|
|
*/
|
|
|
|
private boolean isValidPosition(Position position) {
|
|
|
|
return position.getXCoordinate() >= 0
|
|
|
|
&& position.getXCoordinate() < boardWidth
|
|
|
|
&& position.getYCoordinate() >= 0
|
|
|
|
&& position.getYCoordinate() < boardHeight;
|
2020-02-23 00:03:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks if the robot is about to step outside of the board, and kills it if it does
|
|
|
|
* @param robot The robot attempting to move
|
|
|
|
* @param newPosition The position the robot is attempting to move to
|
|
|
|
* @return True if the robot was killed for leaving the board
|
|
|
|
*/
|
|
|
|
private boolean killRobotIfGoesOutsideMap(Robot robot, Position newPosition) {
|
2020-03-10 18:35:15 +01:00
|
|
|
if (!isValidPosition(newPosition)) {
|
2020-02-23 00:03:20 +01:00
|
|
|
killRobot(robot);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks the tile the robot is about to step on and kills it if the tile is dangerous
|
|
|
|
* @param robot The robot attempting to move
|
|
|
|
* @param newPosition The position the robot is attempting to move to
|
|
|
|
*/
|
2020-02-23 20:30:57 +01:00
|
|
|
private void killRobotIfStepsOnDangerousTile(Robot robot, Position newPosition) {
|
2020-02-22 23:27:59 +01:00
|
|
|
Tile tileRobotStepsOn = tiles.getElement(newPosition.getXCoordinate(), newPosition.getYCoordinate());
|
|
|
|
if (tileRobotStepsOn == null) {
|
|
|
|
throw new IllegalArgumentException("The game board is missing a tile. This should not happen.");
|
|
|
|
}
|
|
|
|
TileType tileTypeRobotStepsOn = tileRobotStepsOn.getTileType();
|
2020-03-19 13:47:47 +01:00
|
|
|
List<TileType> dangerousTiles = new ArrayList<>();
|
|
|
|
dangerousTiles.add(TileType.HOLE);
|
|
|
|
dangerousTiles.add(TileType.PIT_CORNER);
|
|
|
|
dangerousTiles.add(TileType.PIT_EMPTY);
|
|
|
|
dangerousTiles.add(TileType.PIT_FULL);
|
|
|
|
dangerousTiles.add(TileType.PIT_NORMAL);
|
|
|
|
dangerousTiles.add(TileType.PIT_U);
|
|
|
|
if (dangerousTiles.contains(tileTypeRobotStepsOn)) {
|
2020-02-22 23:27:59 +01:00
|
|
|
killRobot(robot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Kills the robot
|
|
|
|
*
|
|
|
|
* If the robot steps outside of the board, steps on a hole or takes too much damage, this method should be used to
|
|
|
|
* properly dispose of the robot until the next round.
|
|
|
|
*
|
|
|
|
* @param robot The robot to kill
|
|
|
|
*/
|
|
|
|
private void killRobot(Robot robot) {
|
2020-02-25 15:48:34 +01:00
|
|
|
robot.setAmountOfLives(robot.getAmountOfLives() - 1);
|
2020-02-27 10:38:26 +01:00
|
|
|
robots.remove(robot.getRobotId());
|
2020-02-26 20:20:46 +01:00
|
|
|
deadRobots.add(robot);
|
2020-02-22 23:27:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether a position has a wall facing a specific direction
|
|
|
|
* @param position The position to check
|
|
|
|
* @param direction The direction of the wall to check for
|
|
|
|
* @return True if there is a wall on the position facing the input direction
|
|
|
|
*/
|
|
|
|
private boolean hasWallFacing(Position position, Direction direction) {
|
|
|
|
Wall wall = walls.getElement(position.getXCoordinate(), position.getYCoordinate());
|
|
|
|
if (wall == null) {
|
|
|
|
return false;
|
|
|
|
}
|
2020-02-23 20:10:28 +01:00
|
|
|
int wallDirectionId = wall.getDirection().getDirectionID();
|
|
|
|
if (wallDirectionId % 2 == 0) {
|
|
|
|
return (wallDirectionId % 8) + 1 == direction.getDirectionID()
|
|
|
|
|| (((wallDirectionId - 2) + 8) % 8) + 1 == direction.getDirectionID();
|
|
|
|
} else {
|
|
|
|
return wall.getDirection() == direction;
|
2020-02-22 23:27:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-24 18:07:26 +01:00
|
|
|
/**
|
|
|
|
* Gets all elements on a grid
|
|
|
|
* @param grid The grid to get elements from
|
|
|
|
* @param <K> The type of the elements int the grid
|
|
|
|
* @return A list containing all the elements in the grid
|
|
|
|
*/
|
|
|
|
private <K> List<K> getAllElementsFromGrid(IGrid<K> grid) {
|
|
|
|
List<K> elements = new ArrayList<>();
|
2020-02-24 22:27:19 +01:00
|
|
|
for (int y = grid.getHeight() - 1; y >= 0; y--) {
|
|
|
|
for (int x = 0; x < grid.getWidth(); x++) {
|
2020-02-24 18:07:26 +01:00
|
|
|
elements.add(grid.getElement(x, y));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return elements;
|
|
|
|
}
|
2020-03-10 16:07:14 +01:00
|
|
|
|
2020-03-12 11:14:44 +01:00
|
|
|
/**
|
2020-03-20 17:46:49 +01:00
|
|
|
* Finds all tiles/walls with a certain type
|
|
|
|
* @param type The type of tile/wall to look for
|
|
|
|
* @param grid The grid to look through
|
|
|
|
* @param <K> Type of the type to look for
|
|
|
|
* @param <T> Type of the grid
|
2020-03-12 11:14:44 +01:00
|
|
|
* @return List of BoardElementContainers
|
|
|
|
*/
|
2020-03-10 16:07:14 +01:00
|
|
|
private <K,T> List<BoardElementContainer<T>> makeTileList(K type, IGrid<T> grid) {
|
|
|
|
List<BoardElementContainer<T>> objList = new ArrayList<>();
|
|
|
|
|
|
|
|
for (int y = grid.getHeight() - 1; y >= 0; y--) {
|
|
|
|
for (int x = 0; x < grid.getWidth(); x++) {
|
|
|
|
T gridElement = grid.getElement(x, y);
|
2020-03-16 16:46:22 +01:00
|
|
|
if (gridElement != null) {
|
|
|
|
if (gridElement.getClass().isAssignableFrom(Tile.class)) {
|
|
|
|
Tile tile = (Tile) gridElement;
|
|
|
|
if (tile.getTileType() == type) {
|
2020-03-20 17:46:49 +01:00
|
|
|
objList.add(new BoardElementContainer<>(gridElement, new Position(x, y)));
|
2020-03-16 16:46:22 +01:00
|
|
|
}
|
|
|
|
} else if (gridElement.getClass().isAssignableFrom(Wall.class)) {
|
|
|
|
Wall wall = (Wall) gridElement;
|
|
|
|
if (wall.getWallType() == type) {
|
2020-03-20 17:46:49 +01:00
|
|
|
objList.add(new BoardElementContainer<>(gridElement, new Position(x, y)));
|
2020-03-16 16:46:22 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException("Grid has unknown type.");
|
2020-03-10 16:07:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return objList;
|
|
|
|
}
|
2020-03-17 17:23:57 +01:00
|
|
|
|
|
|
|
/**
|
2020-03-20 17:46:49 +01:00
|
|
|
* Kills all robots that have taken too much damage
|
2020-03-17 17:23:57 +01:00
|
|
|
*/
|
2020-03-20 17:46:49 +01:00
|
|
|
private void killAllHeavilyDamagedRobots() {
|
2020-03-17 17:23:57 +01:00
|
|
|
for (Robot robot:robots.values()) {
|
2020-03-20 17:46:49 +01:00
|
|
|
if (robot.getDamageTaken() >= 10) {
|
2020-03-17 17:23:57 +01:00
|
|
|
killRobot(robot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fires one wall laser
|
2020-03-20 17:46:49 +01:00
|
|
|
* @param wallLaser The wall laser being fired
|
2020-03-17 17:23:57 +01:00
|
|
|
*/
|
2020-03-20 17:46:49 +01:00
|
|
|
private void fireWallLaser(BoardElementContainer<Wall> wallLaser) {
|
2020-03-23 13:46:55 +01:00
|
|
|
Direction laserDirection = Direction.getReverseDirection(wallLaser.getElement().getDirection());
|
|
|
|
List<Position> laserTargets = new ArrayList<>();
|
|
|
|
getLaserTarget(laserDirection, wallLaser.getPosition(), laserTargets);
|
|
|
|
Position hitPosition = laserTargets.get(laserTargets.size()-1);
|
|
|
|
WallType laserType = wallLaser.getElement().getWallType();
|
|
|
|
updateLaserDisplay(laserTargets, laserDirection, laserType);
|
2020-03-20 17:46:49 +01:00
|
|
|
if (getRobotOnPosition(hitPosition) != null) {
|
2020-03-23 13:46:55 +01:00
|
|
|
applyLaserDamage(laserType, robots.get(getRobotOnPosition(hitPosition)));
|
2020-03-17 17:23:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-20 17:46:49 +01:00
|
|
|
* Fires one robot laser
|
|
|
|
* @param robotPosition The position of the robot firing the laser
|
|
|
|
* @param robotDirection The direction the robot is facing
|
2020-03-17 17:23:57 +01:00
|
|
|
*/
|
2020-03-23 13:46:55 +01:00
|
|
|
private void fireRobotLaser(Position robotPosition, Direction robotDirection) {
|
2020-03-17 17:23:57 +01:00
|
|
|
Position positionInFront = getNewPosition(robotPosition,robotDirection);
|
2020-03-20 17:46:49 +01:00
|
|
|
if (!isValidPosition(positionInFront) || moveIsStoppedByWall(robotPosition, positionInFront, robotDirection)) {
|
2020-03-17 17:23:57 +01:00
|
|
|
return;
|
|
|
|
}
|
2020-03-23 13:46:55 +01:00
|
|
|
List<Position> laserTargets = new ArrayList<>();
|
|
|
|
getLaserTarget(robotDirection, positionInFront, laserTargets);
|
|
|
|
Position hitPosition = laserTargets.get(laserTargets.size()-1);
|
|
|
|
WallType laserType = WallType.WALL_LASER_SINGLE;
|
|
|
|
updateLaserDisplay(laserTargets, robotDirection, laserType);
|
2020-03-20 17:46:49 +01:00
|
|
|
if (getRobotOnPosition(hitPosition) != null) {
|
2020-03-23 13:46:55 +01:00
|
|
|
applyLaserDamage(laserType, robots.get(getRobotOnPosition(hitPosition)));
|
2020-03-17 17:23:57 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Applies the damage form the laser to the robot the laser hit
|
2020-03-20 17:46:49 +01:00
|
|
|
* @param laserType The type of laser that hit the robot
|
|
|
|
* @param robot The robot getting hit by the robot
|
2020-03-17 17:23:57 +01:00
|
|
|
*/
|
2020-03-20 17:46:49 +01:00
|
|
|
private void applyLaserDamage(WallType laserType, Robot robot) {
|
|
|
|
robot.setDamageTaken(robot.getDamageTaken() + laserType.getWallTypeID() - 2);
|
2020-03-17 17:23:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-03-23 13:46:55 +01:00
|
|
|
* Gets all the positions the laser fires at
|
2020-03-20 17:46:49 +01:00
|
|
|
* @param direction The direction of the laser
|
|
|
|
* @param startPosition The start position of the laser
|
2020-03-23 13:46:55 +01:00
|
|
|
* @param targets The list to update with target positions
|
2020-03-17 17:23:57 +01:00
|
|
|
*/
|
2020-03-23 13:46:55 +01:00
|
|
|
private void getLaserTarget(Direction direction, Position startPosition, List<Position> targets) {
|
2020-03-20 17:46:49 +01:00
|
|
|
Position newPosition = getNewPosition(startPosition, direction);
|
2020-03-23 13:46:55 +01:00
|
|
|
targets.add(startPosition);
|
2020-03-20 17:46:49 +01:00
|
|
|
if (!isValidPosition(newPosition) || moveIsStoppedByWall(startPosition, newPosition, direction) ||
|
|
|
|
getRobotOnPosition(startPosition) != null) {
|
2020-03-23 13:46:55 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (getRobotOnPosition(newPosition) != null) {
|
|
|
|
targets.add(newPosition);
|
|
|
|
} else {
|
|
|
|
getLaserTarget(direction, newPosition, targets);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds any lasers in the targets list to the grid displaying lasers
|
|
|
|
* @param laserTargets The tiles the laser will hit
|
|
|
|
* @param laserDirection The direction of the laser
|
|
|
|
* @param laserType The type of the laser
|
|
|
|
*/
|
|
|
|
private void updateLaserDisplay(List<Position> laserTargets, Direction laserDirection, WallType laserType) {
|
|
|
|
for (Position laserTarget : laserTargets) {
|
|
|
|
updateLaserBeamOnParticleGrid(laserTarget, laserDirection, laserType);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates a laser beam on the particle grid
|
|
|
|
* @param addPosition The position of the beam
|
|
|
|
* @param laserDirection The direction of the beam
|
|
|
|
* @param laserType The type of the laser shooting
|
|
|
|
*/
|
|
|
|
private void updateLaserBeamOnParticleGrid(Position addPosition, Direction laserDirection, WallType laserType) {
|
|
|
|
int positionX = addPosition.getXCoordinate();
|
|
|
|
int positionY = addPosition.getYCoordinate();
|
|
|
|
int numberOfLasers;
|
|
|
|
switch (laserType) {
|
|
|
|
case WALL_LASER_SINGLE:
|
|
|
|
numberOfLasers = 1;
|
|
|
|
break;
|
|
|
|
case WALL_LASER_DOUBLE:
|
|
|
|
numberOfLasers = 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new IllegalArgumentException("Laser type submitted is not a laser.");
|
|
|
|
}
|
|
|
|
ParticleType type;
|
|
|
|
Particle particleAtPosition = particles.getElement(positionX, positionY);
|
|
|
|
if (particleAtPosition != null && Direction.arePerpendicular(particleAtPosition.getDirection(),
|
|
|
|
laserDirection)) {
|
|
|
|
type = numberOfLasers == 1 ? ParticleType.LASER_BEAM_SINGLE_CROSS :
|
|
|
|
ParticleType.LASER_BEAM_DOUBLE_CROSS;
|
2020-03-20 17:46:49 +01:00
|
|
|
} else {
|
2020-03-23 13:46:55 +01:00
|
|
|
type = numberOfLasers == 1 ? ParticleType.LASER_BEAM_SINGLE : ParticleType.LASER_BEAM_DOUBLE;
|
2020-03-17 17:23:57 +01:00
|
|
|
}
|
2020-03-23 13:46:55 +01:00
|
|
|
particles.setElement(positionX, positionY, new Particle(type, laserDirection));
|
2020-03-17 17:23:57 +01:00
|
|
|
}
|
|
|
|
|
2020-03-10 16:07:14 +01:00
|
|
|
}
|