diff --git a/src/main/java/inf112/fiasko/roborally/objects/Board.java b/src/main/java/inf112/fiasko/roborally/objects/Board.java index f30c93f..e55eabb 100644 --- a/src/main/java/inf112/fiasko/roborally/objects/Board.java +++ b/src/main/java/inf112/fiasko/roborally/objects/Board.java @@ -167,6 +167,137 @@ public class Board { return true; } + /** + * 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 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)); + } + /** * 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. diff --git a/src/main/java/inf112/fiasko/roborally/objects/RoboRallyGame.java b/src/main/java/inf112/fiasko/roborally/objects/RoboRallyGame.java index 797b1ab..66efd4b 100644 --- a/src/main/java/inf112/fiasko/roborally/objects/RoboRallyGame.java +++ b/src/main/java/inf112/fiasko/roborally/objects/RoboRallyGame.java @@ -11,9 +11,10 @@ import inf112.fiasko.roborally.utility.DeckLoaderUtil; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; /** * This class represent a game which is drawable using libgdx @@ -24,8 +25,6 @@ public class RoboRallyGame implements IDrawableGame { private List> conveyorBelts; private List> fastConveyorBelts; private List playerList; - private List> blacklistedTiles = new ArrayList<>(); - private List> whitelistedTiles = new ArrayList<>(); /** * Instantiates a new robo rally game @@ -138,7 +137,7 @@ public class RoboRallyGame implements IDrawableGame { player.setInProgram(testProgram); } - gameBoard = BoardLoaderUtil.loadBoard("boards/Dizzy_Dash.txt", robots); + gameBoard = BoardLoaderUtil.loadBoard("boards/Checkmate.txt", robots); cogwheels = gameBoard.getPositionsOfTileOnBoard(TileType.COGWHEEL_RIGHT, TileType.COGWHEEL_LEFT); fastConveyorBelts = gameBoard.getPositionsOfTileOnBoard(TileType.CONVEYOR_BELT_FAST, @@ -267,22 +266,6 @@ public class RoboRallyGame implements IDrawableGame { } } - /** - * Checks whether a given list has at least one element as defined by the predicate - * @param list The list to check - * @param predicate The predicate to test - * @param The type of the list - * @return True if the list has at least one element passing the test of the predicate - */ - private static boolean testPredicate(List list, Predicate predicate) { - for (T object : list) { - if (predicate.test(object)) { - return true; - } - } - return false; - } - /** * Moves robots standing on conveyor belts in the direction of the conveyor belt * @@ -298,190 +281,41 @@ public class RoboRallyGame implements IDrawableGame { } /** - * Moves all conveyor belts in the input list - * @param conveyorBelts A list of conveyor belts to move + * Moves a list of conveyor belts + * @param conveyorBelts A list of board element containers containing conveyor belts */ private void moveConveyorBelts(List> conveyorBelts) { - List> conveyorBeltsWithRobotsThatShouldMove = - conveyorBeltsThatCanMoveWithoutConflict(conveyorBelts); - for (BoardElementContainer conveyorBelt : conveyorBeltsWithRobotsThatShouldMove) { - - Direction currentDirection = conveyorBelt.getElement().getDirection(); - RobotID robot = gameBoard.getRobotOnPosition(conveyorBelt.getPosition()); - Position newPosition = gameBoard.getNewPosition(conveyorBelt.getPosition(), currentDirection); - Tile nextTile = gameBoard.getTileOnPosition(newPosition); - - doConveyorBeltMovement(robot, currentDirection, nextTile); + Map newPositions = new HashMap<>(); + Map moveNormally = new HashMap<>(); + for (Robot robot : gameBoard.getAliveRobots()) { + newPositions.put(robot.getRobotId(), robot.getPosition()); } - } - - /** - * Finds conveyor belts that can move without conflict - * @param conveyorBelts that should be checked - * @return List of conveyor belts that can move robots without conflict - */ - private List> conveyorBeltsThatCanMoveWithoutConflict( - List> conveyorBelts) { - - List> conveyorBeltsWithRobotsOn = new ArrayList<>(); - whitelistedTiles.clear(); - blacklistedTiles.clear(); - + //Updates hash maps containing robot move information for (BoardElementContainer conveyorBelt : conveyorBelts) { - if (gameBoard.hasRobotOnPosition(conveyorBelt.getPosition())) { - conveyorBeltsWithRobotsOn.add(conveyorBelt); - } - } - - List> listOfRow = new ArrayList<>(); - for (BoardElementContainer conveyorBeltWithRobot : conveyorBeltsWithRobotsOn) { - if (blacklistedTiles.contains(conveyorBeltWithRobot) || - whitelistedTiles.contains((conveyorBeltWithRobot))) { - continue; - } - - BoardElementContainer lastInRow = findLastRobotInRow (conveyorBeltWithRobot, conveyorBeltsWithRobotsOn); - - List> results = findFirstRobotInRow(lastInRow, conveyorBeltsWithRobotsOn, - listOfRow); - - for (BoardElementContainer result : results) { - if (!whitelistedTiles.contains(result)) { - whitelistedTiles.add(0, result); + Position conveyorBeltPosition = conveyorBelt.getPosition(); + Direction conveyorBeltDirection = conveyorBelt.getElement().getDirection(); + if (gameBoard.conveyorBeltCanMove(conveyorBelt) && + gameBoard.hasRobotOnPosition(conveyorBeltPosition)) { + RobotID robotAtConveyorBelt = gameBoard.getRobotOnPosition(conveyorBeltPosition); + Position newPosition = gameBoard.getNewPosition(conveyorBeltPosition, conveyorBeltDirection); + if (gameBoard.isConveyorBelt(gameBoard.getTileOnPosition(newPosition))) { + newPositions.put(robotAtConveyorBelt, newPosition); + moveNormally.put(robotAtConveyorBelt, false); + } else { + newPositions.put(robotAtConveyorBelt, conveyorBeltPosition); + moveNormally.put(robotAtConveyorBelt, true); } } } - return whitelistedTiles; - } - - /** - * Recursive function to find all robots in a row that should move, and blacklists those that should not - * @param currentConveyorBelt The current conveyor belt - * @param conveyorBeltsWithRobotsOn List of conveyor belts that have robots on them - * @param listOfRow List of conveyor belts in a row with robots on them - * @return listOfRow - */ - private List> findFirstRobotInRow(BoardElementContainer currentConveyorBelt, - List> conveyorBeltsWithRobotsOn, - List> listOfRow) { - Position nextPosition = gameBoard.getNewPosition(currentConveyorBelt.getPosition(), - currentConveyorBelt.getElement().getDirection()); - Direction nextDirection = gameBoard.getTileOnPosition(nextPosition).getDirection(); - Tile nextTile = gameBoard.getTileOnPosition(nextPosition); - BoardElementContainer nextElementContainer = new BoardElementContainer<>(nextTile, nextPosition); - List> pointingNeighbours = listOfConveyorBeltsWithRobotPointingAtTile(true, - nextElementContainer, conveyorBeltsWithRobotsOn); - - listOfRow.add(currentConveyorBelt); - - if (blacklistedTiles.contains(nextElementContainer)) { - blacklistedTiles.addAll(listOfRow); - listOfRow.clear(); - } else if (currentConveyorBelt.getElement().getDirection() == Direction.getReverseDirection(nextDirection) && - conveyorBeltsWithRobotsOn.contains(nextElementContainer)) { - blacklistedTiles.addAll(listOfRow); - blacklistedTiles.add(nextElementContainer); - listOfRow.clear(); - } else if ((!conveyorBelts.contains(nextElementContainer)) && gameBoard.hasRobotOnPosition(nextPosition)) { - blacklistedTiles.addAll(listOfRow); - listOfRow.clear(); - } else if (gameBoard.moveIsStoppedByWall(currentConveyorBelt.getPosition(), nextPosition, - currentConveyorBelt.getElement().getDirection())) { - blacklistedTiles.addAll(listOfRow); - listOfRow.clear(); - } else if (pointingNeighbours.size() > 0) { - blacklistedTiles.addAll(pointingNeighbours); - blacklistedTiles.addAll(listOfRow); - listOfRow.clear(); - } else if ((conveyorBeltsWithRobotsOn.contains(nextElementContainer))) { - listOfRow = findFirstRobotInRow(nextElementContainer, conveyorBeltsWithRobotsOn, listOfRow); - } - return listOfRow; - } - - /** - * Recursive function that finds the last robot in a row with conveyor belts without conflicts - * @param currentConveyorBelt The current conveyor belt - * @param conveyorBeltsWithRobotsOn List with conveyor belts that have robots on them - * @return The last conveyor belt with a robot without conflict in a row - */ - private BoardElementContainer findLastRobotInRow(BoardElementContainer currentConveyorBelt, - List> conveyorBeltsWithRobotsOn) { - List> listOfConveyorBeltsWithRobotPointingAtTile = - listOfConveyorBeltsWithRobotPointingAtTile(false, currentConveyorBelt, - conveyorBeltsWithRobotsOn); - int sizeOfPointingList = listOfConveyorBeltsWithRobotPointingAtTile.size(); - if (sizeOfPointingList == 0) { - return currentConveyorBelt; - } else if (sizeOfPointingList == 1) { - return findLastRobotInRow(listOfConveyorBeltsWithRobotPointingAtTile.get(0), conveyorBeltsWithRobotsOn); - } else { - blacklistedTiles.addAll(listOfConveyorBeltsWithRobotPointingAtTile); - return currentConveyorBelt; - } - } - - /** - * Finds all neighbouring conveyor belt tiles with robots that are pointing on the current tile - * @param forward True if looking forward, false otherwise - * @param currentTile The current tile - * @param conveyorBeltsWithRobots List with conveyor belts that have robots on them - * @return A list of the neighbouring conveyor belt tiles with robots that are pointing on the current tile - */ - private List> listOfConveyorBeltsWithRobotPointingAtTile(Boolean forward, - BoardElementContainer currentTile, - List> conveyorBeltsWithRobots) { - List> possibleConflictConveyorBelts = new ArrayList<>(); - Tile conveyorBeltTile = currentTile.getElement(); - Position currentPosition = currentTile.getPosition(); - Direction currentDirection; - - if (forward) { - currentDirection = conveyorBeltTile.getDirection(); - } else currentDirection = Direction.getReverseDirection(conveyorBeltTile.getDirection()); - - Position nextPositionStraight = gameBoard.getNewPosition(currentPosition, currentDirection); - Tile nextTileStraight = gameBoard.getTileOnPosition(nextPositionStraight); - Position nextPositionLeft = gameBoard.getNewPosition(currentPosition, - Direction.getLeftRotatedDirection(currentDirection)); - Tile nextTileLeft = gameBoard.getTileOnPosition(nextPositionLeft); - Position nextPositionRight = gameBoard.getNewPosition(currentPosition, - Direction.getRightRotatedDirection(currentDirection)); - Tile nextTileRight = gameBoard.getTileOnPosition(nextPositionRight); - - BoardElementContainer rightOfCurrent = new BoardElementContainer<>(nextTileRight, nextPositionRight); - BoardElementContainer leftOfCurrent = new BoardElementContainer<>(nextTileLeft, nextPositionLeft); - BoardElementContainer inFrontOfCurrent = new BoardElementContainer<>(nextTileStraight, nextPositionStraight); - - if (currentDirection == Direction.getReverseDirection( - nextTileStraight.getDirection()) && conveyorBeltsWithRobots.contains(inFrontOfCurrent)) { - possibleConflictConveyorBelts.add(inFrontOfCurrent); - } - if (currentDirection == Direction.getLeftRotatedDirection( - nextTileLeft.getDirection()) && conveyorBeltsWithRobots.contains(leftOfCurrent)) { - possibleConflictConveyorBelts.add(leftOfCurrent); - } - if (currentDirection == Direction.getRightRotatedDirection( - nextTileRight.getDirection()) && conveyorBeltsWithRobots.contains(rightOfCurrent)) { - possibleConflictConveyorBelts.add(rightOfCurrent); - } - return possibleConflictConveyorBelts; - } - - /** - * Moves a robot standing on a conveyor belt - * @param robot The id of the robot to move - * @param currentDirection The direction of the conveyor belt the robot is standing on - * @param nextTile The tile the robot is moving to - */ - private void doConveyorBeltMovement(RobotID robot, Direction currentDirection, Tile nextTile) { - Direction nextDirection = nextTile.getDirection(); - gameBoard.moveRobot(robot, currentDirection); - if (testPredicate(conveyorBelts, (container) -> container.getElement() == nextTile)) { - if (Direction.getRightRotatedDirection(nextDirection) == currentDirection) { - gameBoard.rotateRobotLeft(robot); - } else if (Direction.getLeftRotatedDirection(nextDirection) == currentDirection) { - gameBoard.rotateRobotRight(robot); + //Updates position for all robots affected by conveyor belts + for (RobotID robotID : RobotID.values()) { + if (newPositions.get(robotID) == null || moveNormally.get(robotID) == null) { + continue; + } + if (moveNormally.get(robotID)) { + gameBoard.moveRobot(robotID, gameBoard.getTileOnPosition(newPositions.get(robotID)).getDirection()); + } else { + gameBoard.teleportRobot(robotID, newPositions.get(robotID)); } } } @@ -522,7 +356,7 @@ public class RoboRallyGame implements IDrawableGame { for (Player player : playerList) { List playerProgram = player.getProgram(); if (!playerProgram.isEmpty()) { - ProgrammingCard programmingCard = playerProgram.get(phase); + ProgrammingCard programmingCard = playerProgram.get(phase-1); originalPriority.add(programmingCard.getPriority()); robotsToDoAction.add(player.getRobotID()); programToBeRun.add(programmingCard);