├── .travis.yml ├── SmartRoboVacuum.gif ├── commons ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── team4 │ │ │ │ └── commons │ │ │ │ ├── WorkingMode.java │ │ │ │ ├── Direction.java │ │ │ │ ├── State.java │ │ │ │ ├── RobotException.java │ │ │ │ ├── FloorType.java │ │ │ │ ├── LocationFactory.java │ │ │ │ ├── Location.java │ │ │ │ ├── TextFileLogger.java │ │ │ │ ├── ConfigManager.java │ │ │ │ ├── LogManager.java │ │ │ │ └── Utilities.java │ │ └── resources │ │ │ └── configuration.xml │ └── test │ │ └── java │ │ └── com │ │ └── team4 │ │ └── commons │ │ └── LocationFactoryTest.java └── pom.xml ├── sensor ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── team4 │ │ │ │ └── sensor │ │ │ │ ├── DirtDao.java │ │ │ │ ├── DirtGenerator.java │ │ │ │ ├── FloorBuilder.java │ │ │ │ ├── Sensor.java │ │ │ │ ├── FloorDao.java │ │ │ │ ├── DirtGeneratorRandom.java │ │ │ │ ├── DirtGeneratorFactory.java │ │ │ │ ├── FloorBuilderFactory.java │ │ │ │ ├── TileFactory.java │ │ │ │ ├── DirtGeneratorGaussian.java │ │ │ │ ├── Floor.java │ │ │ │ ├── DirtGeneratorSimple.java │ │ │ │ ├── Tile.java │ │ │ │ ├── FloorPlan.java │ │ │ │ ├── SensorSimulator.java │ │ │ │ ├── InMemoryFloorBuilder.java │ │ │ │ └── JsonFloorBuilder.java │ │ └── resources │ │ │ ├── FP0.json │ │ │ ├── FP1.json │ │ │ └── FP2.json │ └── test │ │ └── java │ │ └── com │ │ └── team4 │ │ └── sensor │ │ ├── TileFactoryTest.java │ │ ├── GetLocationInfoNeighborsWithinRadiusTest.java │ │ └── GetLocationInfoCorrectDirectionsOfPassageTest.java └── pom.xml ├── robot ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── team4 │ │ │ ├── robot │ │ │ ├── Robot.java │ │ │ ├── VacuumCleaner.java │ │ │ ├── Navigator.java │ │ │ ├── PowerManager.java │ │ │ ├── NavigatorNull.java │ │ │ ├── Heuristics.java │ │ │ ├── NavigatorFactory.java │ │ │ ├── NavigatorBeta.java │ │ │ ├── NavigatorOmega.java │ │ │ ├── DirtManager.java │ │ │ ├── Node.java │ │ │ ├── AStar.java │ │ │ ├── NavigatorAlpha.java │ │ │ ├── PowerUnit.java │ │ │ └── RobotCleanSweep.java │ │ │ └── CleanSweepApp.java │ └── test │ │ └── java │ │ └── com │ │ └── team4 │ │ └── robot │ │ ├── NoTilesLeftUnvisitedTest.java │ │ ├── MaxDirtLevelTest.java │ │ └── PowerManagerTest.java └── pom.xml ├── .gitignore ├── pom.xml └── Readme.md /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | language: java 3 | jdk: oraclejdk8 4 | env: 5 | - LOGS_HOME="./" -------------------------------------------------------------------------------- /SmartRoboVacuum.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v-za/clean-sweep-robot/HEAD/SmartRoboVacuum.gif -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/WorkingMode.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | public enum WorkingMode { TESTING, DEPLOYED }; -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/DirtDao.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | public class DirtDao { 4 | public boolean isClean; 5 | } 6 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/Robot.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | public interface Robot { 4 | void turnOn(); 5 | void turnOff(); 6 | } 7 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/DirtGenerator.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | public interface DirtGenerator { 4 | int generateDirt(); 5 | } 6 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/FloorBuilder.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | public interface FloorBuilder { 4 | void buildFloor(); 5 | } 6 | -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/Direction.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | public enum Direction { 4 | NORTH, 5 | SOUTH, 6 | EAST, 7 | WEST 8 | } 9 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/VacuumCleaner.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | interface VacuumCleaner { 4 | void clean(double cost); 5 | int getDirtLevel(); 6 | } 7 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/Navigator.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | import com.team4.commons.Direction; 3 | 4 | interface Navigator { 5 | Direction traverseFloor(Direction [] directions); 6 | } 7 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/PowerManager.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | interface PowerManager { 4 | void recharge(); 5 | void updateBatteryLevel(double units); 6 | double getBatteryLevel(); 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse 2 | .classpath 3 | .project 4 | .settings/ 5 | .metadata/ 6 | 7 | # Intellij 8 | .idea/ 9 | *.iml 10 | *.iws 11 | 12 | # Mac 13 | .DS_Store 14 | 15 | # Maven 16 | log/ 17 | target/ 18 | /bin/ 19 | 20 | log* 21 | -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/State.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | public enum State { 4 | OFF, 5 | STANDBY, 6 | WORKING, 7 | LOW_BATTERY, 8 | CHARGING, 9 | FULL_TANK, 10 | MOVING_BACK 11 | } 12 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/NavigatorNull.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.Direction; 4 | 5 | public class NavigatorNull implements Navigator { 6 | @Override 7 | public Direction traverseFloor(Direction[] directions) { 8 | return null; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/Sensor.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.Location; 4 | 5 | public interface Sensor { 6 | FloorDao getLocationInfo(Location location); 7 | void setTileDone(Location location); 8 | void removeDirtFromLocation(Location location); 9 | } 10 | -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/RobotException.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | public class RobotException extends RuntimeException { 4 | 5 | public RobotException(String message) { 6 | super(message); 7 | } 8 | 9 | @Override 10 | public String getMessage() { 11 | return super.getMessage(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/FloorDao.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.Direction; 4 | 5 | import com.team4.commons.FloorType; 6 | import com.team4.commons.Location; 7 | 8 | public class FloorDao { 9 | public FloorType floorType; 10 | public Direction [] openPassages; 11 | public Location [] chargingStations; 12 | public boolean isClean; 13 | } 14 | -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/FloorType.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | public enum FloorType { 4 | BARE, 5 | LOW_PILE, 6 | HIGH_PILE; 7 | 8 | public int getCost(){ 9 | switch(this) { 10 | case BARE: return 1; 11 | case LOW_PILE: return 2; 12 | case HIGH_PILE: return 3; 13 | default: throw new RobotException("Invalid floor type state"); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/CleanSweepApp.java: -------------------------------------------------------------------------------- 1 | package com.team4; 2 | 3 | import com.team4.commons.Utilities; 4 | import com.team4.robot.RobotCleanSweep; 5 | import com.team4.robot.Robot; 6 | 7 | public class CleanSweepApp { 8 | public static void main( String[] args ) { 9 | Utilities.printLogo(); 10 | Utilities.printConfiguration(); 11 | Robot cleanSweepRobot = RobotCleanSweep.getInstance(); 12 | cleanSweepRobot.turnOn(); 13 | cleanSweepRobot.turnOff(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/Heuristics.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.Location; 4 | 5 | class Heuristics { 6 | 7 | private Location goal; 8 | 9 | public Heuristics(Location goal) { 10 | this.goal = goal; 11 | } 12 | 13 | Location getGoal() { 14 | return this.goal; 15 | } 16 | 17 | int manhattan(Node node) { 18 | int val = Math.abs((node.getLocation().getX() - getGoal().getX())) + Math.abs(node.getLocation().getY() - getGoal().getY()); 19 | return val; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/DirtGeneratorRandom.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import java.util.Random; 4 | 5 | public class DirtGeneratorRandom implements DirtGenerator { 6 | 7 | DirtGeneratorRandom() { 8 | } 9 | 10 | /** 11 | * Generates dirt amounts randomly 12 | * @return 13 | */ 14 | @Override 15 | public int generateDirt() { 16 | int min = 0; 17 | int max = 3; 18 | Random random = new Random(); 19 | return random.ints(min, max + 1).findFirst().getAsInt(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /robot/src/test/java/com/team4/robot/NoTilesLeftUnvisitedTest.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.State; 4 | import com.team4.sensor.SensorSimulator; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.assertTrue; 8 | 9 | public class NoTilesLeftUnvisitedTest { 10 | 11 | @Test 12 | public void robot_visits_every_tile_on_the_floor() { 13 | RobotCleanSweep.getInstance().setState(State.STANDBY); 14 | RobotCleanSweep.getInstance().dryRun(); 15 | assertTrue(SensorSimulator.getInstance().getDonePercentage() == 100.0); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/NavigatorFactory.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.ConfigManager; 4 | 5 | public class NavigatorFactory { 6 | 7 | private NavigatorFactory() { 8 | } 9 | 10 | static Navigator createNavigator() { 11 | switch(ConfigManager.getConfiguration("navigator")) { 12 | case "null": 13 | return new NavigatorNull(); 14 | case "alpha": 15 | return new NavigatorAlpha(); 16 | case "beta": 17 | case "omega": 18 | default: 19 | return new NavigatorOmega(); 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /sensor/src/main/resources/FP0.json: -------------------------------------------------------------------------------- 1 | { 2 | "southEastCornerCoordinates": {"x": 10, "y": 10}, 3 | "obstacles" : [ 4 | { "from" : { "x" : 0, "y" : 0 }, "to" : { "x" : 10, "y" : 0 }, "obstacleType" : "WALL" }, 5 | { "from" : { "x" : 0, "y" : 0 }, "to" : { "x" : 0, "y" : 10 }, "obstacleType" : "WALL" }, 6 | { "from" : { "x" : 0, "y" : 10 }, "to" : { "x" : 10, "y" : 10 }, "obstacleType" : "WALL" }, 7 | { "from" : { "x" : 10, "y" : 0 }, "to" : { "x" : 10, "y" : 10 }, "obstacleType" : "WALL" } 8 | ], 9 | "passages":[], 10 | "chargingStations": [{ "x" : 0, "y" : 9 }], 11 | "areas":[ 12 | { "topLeft" : { "x" : 0, "y" : 2 }, "bottomRight" : { "x" : 2, "y" : 8 }, "floorType" : "LOW_PILE" } 13 | ] 14 | } -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/DirtGeneratorFactory.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.ConfigManager; 4 | import com.team4.commons.RobotException; 5 | 6 | public class DirtGeneratorFactory { 7 | 8 | private DirtGeneratorFactory() { 9 | } 10 | 11 | static DirtGenerator createDirtGenerator() { 12 | String dirtGeneratorType = ConfigManager.getConfiguration("dirtGeneratorType"); 13 | switch (dirtGeneratorType) { 14 | case "random": return new DirtGeneratorRandom(); 15 | case "simple": return new DirtGeneratorSimple(); 16 | case "gaussian": return new DirtGeneratorGaussian(); 17 | default: throw new RobotException("Invalid dirt generator type: " + dirtGeneratorType); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/FloorBuilderFactory.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.ConfigManager; 4 | import com.team4.commons.Location; 5 | import com.team4.commons.RobotException; 6 | 7 | import java.util.HashMap; 8 | 9 | public class FloorBuilderFactory { 10 | 11 | private FloorBuilderFactory() { 12 | } 13 | 14 | static FloorBuilder createFloorBuilder(HashMap tiles) { 15 | String floorBuilderType = ConfigManager.getConfiguration("floorBuilderType"); 16 | switch(floorBuilderType) { 17 | case "inMemory": 18 | return new InMemoryFloorBuilder(tiles); 19 | case "json": 20 | return new JsonFloorBuilder(tiles); 21 | default: 22 | throw new RobotException(floorBuilderType + " is not implemented yet."); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/TileFactory.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.Location; 4 | 5 | import java.util.HashMap; 6 | 7 | /** 8 | * This makes sure there is only one Tile object per unique location. 9 | * NOTE: This is not a perfect flyweight pattern implementation. 10 | */ 11 | class TileFactory { 12 | 13 | private static HashMap existingTiles = new HashMap<>(); 14 | 15 | private TileFactory() { 16 | } 17 | 18 | static Tile createTile(Location location) { 19 | if(!getExistingTiles().containsKey(location)) { 20 | Tile newTile = new Tile(location); 21 | getExistingTiles().put(location, newTile); 22 | } 23 | return getExistingTiles().get(location); 24 | } 25 | 26 | private static HashMap getExistingTiles() { 27 | return existingTiles; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/DirtGeneratorGaussian.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import java.util.Random; 4 | 5 | public class DirtGeneratorGaussian implements DirtGenerator { 6 | 7 | DirtGeneratorGaussian() { 8 | } 9 | 10 | /** 11 | * Generates dirt amounts randomly using the Gaussian (normal) distribution 12 | * Mean = 1.0 13 | * SD = 0.75 14 | * The Javadoc says Mean ± 1 SD (1.0 ± 0.75) contain 68.2% of all values. 15 | * => 68% of the time the dirt unit is going to be 1. 16 | * @return 17 | */ 18 | @Override 19 | public int generateDirt() { 20 | Random random = new Random(); 21 | int randomGaussianInt; 22 | do { 23 | double value = random.nextGaussian() * 0.75 + 1; 24 | randomGaussianInt = (int) Math.round(value); 25 | } while (randomGaussianInt <= 0); 26 | return randomGaussianInt; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/LocationFactory.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * This makes sure there is only one location object per pair of (x, y) coordinates. 7 | */ 8 | public class LocationFactory { 9 | 10 | private static HashMap existingLocations = new HashMap<>(); 11 | 12 | private LocationFactory() { 13 | } 14 | 15 | private static HashMap getExistingLocations() { 16 | return existingLocations; 17 | } 18 | 19 | 20 | public static Location createLocation(int x, int y) { 21 | 22 | if(x < 0 || y < 0) { 23 | throw new RobotException("Negative coordinates not allowed in location creation."); 24 | } 25 | String key = Utilities.tupleToString(x, y); 26 | if(!getExistingLocations().containsKey(key)) { 27 | Location newLocation = new Location(x, y); 28 | getExistingLocations().put(key, newLocation); 29 | } 30 | return getExistingLocations().get(key); 31 | } 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/Floor.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.Location; 4 | import java.util.HashMap; 5 | 6 | class Floor { 7 | 8 | private HashMap tiles = new HashMap<>(); 9 | 10 | static int WIDTH; 11 | static int LENGTH; 12 | 13 | private static Floor theFloor = null; 14 | private Floor() { 15 | // pass a reference of the tiles hash map to the floor builder. 16 | // the floor builder uses that reference and builds tiles into it. 17 | FloorBuilderFactory.createFloorBuilder(getTiles()).buildFloor(); 18 | } 19 | 20 | static Floor getInstance() { 21 | if(theFloor == null) { 22 | synchronized (Floor.class) { 23 | if(theFloor == null) { 24 | theFloor = new Floor(); 25 | } 26 | } 27 | } 28 | return theFloor; 29 | } 30 | 31 | HashMap getTiles() { 32 | return tiles; 33 | } 34 | 35 | Tile getTile(Location location) { 36 | return getTiles().get(location); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/DirtGeneratorSimple.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.Utilities; 4 | 5 | public class DirtGeneratorSimple implements DirtGenerator { 6 | 7 | private int callCount; 8 | 9 | DirtGeneratorSimple() { 10 | setCallCount(0); 11 | } 12 | 13 | /** 14 | * Generates a fixed amount of dirt for tiles at all times. 15 | * @return 16 | */ 17 | @Override 18 | public int generateDirt() { 19 | setCallCount(1 + getCallCount()); 20 | int generatedDirt = 1; 21 | if(Utilities.isPrime(getCallCount())) { 22 | if(getCallCount() % 10 == 9) { 23 | //generatedDirt = 0; 24 | } else if(getCallCount() % 10 == 7) { 25 | generatedDirt = 3; 26 | } else { 27 | generatedDirt = 2; 28 | } 29 | } 30 | return generatedDirt; 31 | } 32 | 33 | private int getCallCount() { 34 | return callCount; 35 | } 36 | 37 | private void setCallCount(int callCount) { 38 | this.callCount = callCount; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /robot/src/test/java/com/team4/robot/MaxDirtLevelTest.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.ConfigManager; 4 | import com.team4.sensor.SensorSimulator; 5 | import org.junit.Before; 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | 9 | import static com.team4.commons.State.WORKING; 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class MaxDirtLevelTest { 13 | 14 | private DirtManager dirtManager; 15 | 16 | @Before 17 | public void init() { 18 | this.dirtManager = new DirtManager(); 19 | } 20 | 21 | @Test 22 | public void dirt_manager_makes_sure_tank_is_emptied_and_robot_gets_back_to_WORKING_state_after_tank_fills_up() { 23 | RobotCleanSweep robot = RobotCleanSweep.getInstance(); 24 | robot.setState(WORKING); 25 | int dirtCapacity = Integer.parseInt(ConfigManager.getConfiguration("dirtCapacity")); 26 | for(int i = 0; i < dirtCapacity; i++) { 27 | if(!SensorSimulator.getInstance().getLocationInfo(robot.getLocation()).isClean) { 28 | this.dirtManager.clean(3.0); 29 | } 30 | } 31 | assertEquals(WORKING, robot.getState()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /commons/src/main/resources/configuration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /commons/src/test/java/com/team4/commons/LocationFactoryTest.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.junit.runners.Parameterized; 6 | import org.junit.runners.Parameterized.Parameters; 7 | import static org.junit.Assert.assertEquals; 8 | 9 | import java.util.ArrayList; 10 | 11 | import java.util.Collection; 12 | import java.util.stream.Collectors; 13 | import java.util.stream.Stream; 14 | 15 | @RunWith(Parameterized.class) 16 | public class LocationFactoryTest { 17 | 18 | private Location first; 19 | private Location second; 20 | 21 | public LocationFactoryTest(Location first, Location second) { 22 | this.first = first; 23 | this.second = second; 24 | } 25 | 26 | @Parameters(name = "{index}") 27 | public static Collection data() { 28 | return (ArrayList) Stream.of(new Object[][] { 29 | { LocationFactory.createLocation(0, 0), LocationFactory.createLocation(0, 0) }, 30 | { LocationFactory.createLocation(1, 9), LocationFactory.createLocation(1, 9) } 31 | }).collect(Collectors.toList()); 32 | } 33 | 34 | @Test 35 | public void location_factory_creates_only_one_location_object_per_unique_coordinates() { 36 | assertEquals(first, second); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/Location.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | import java.util.Objects; 4 | 5 | public class Location { 6 | 7 | private int x; 8 | private int y; 9 | 10 | private Location() { 11 | } 12 | 13 | Location(int x, int y) { 14 | setX(x); 15 | setY(y); 16 | } 17 | 18 | public int getX() { 19 | return x; 20 | } 21 | 22 | public void setX(int x) { 23 | if( x < 0 ) { 24 | throw new RobotException("Negative coordinates are not allowed in locations."); 25 | } 26 | this.x = x; 27 | } 28 | 29 | public int getY() { 30 | return y; 31 | } 32 | 33 | public void setY(int y) { 34 | if( y < 0) { 35 | throw new RobotException("Negative coordinates are not allowed in locations."); 36 | } 37 | this.y = y; 38 | } 39 | 40 | @Override 41 | public String toString() { 42 | return "(" + x + ", " + y + ")"; 43 | } 44 | 45 | @Override 46 | public boolean equals(Object o) { 47 | if (this == o) return true; 48 | if (o == null || getClass() != o.getClass()) return false; 49 | Location location = (Location) o; 50 | return x == location.x && y == location.y; 51 | } 52 | 53 | @Override 54 | public int hashCode() { 55 | return Objects.hash(x, y); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/TextFileLogger.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | import java.io.IOException; 4 | import java.io.File; 5 | import java.nio.file.Files; 6 | import java.nio.file.Paths; 7 | import java.nio.file.StandardOpenOption; 8 | 9 | public class TextFileLogger { 10 | 11 | private String logFile; 12 | 13 | public TextFileLogger(String logFile) { 14 | try { 15 | String logsHome = ConfigManager.getConfiguration("logsHome"); 16 | File logsDirectory = new File(System.getenv(logsHome)); 17 | setLogFile(logsDirectory.getAbsolutePath() + File.separator + logFile); 18 | File file = new File(getLogFile()); 19 | if(!file.exists()) { 20 | file.createNewFile(); 21 | } 22 | } catch(IOException ioe) { 23 | throw new RobotException("I/O error while creating log file."); 24 | } catch(NullPointerException npe) { 25 | throw new RobotException("ERROR: the log file pathname is null."); 26 | } 27 | } 28 | 29 | public void log(String logMessage) { 30 | try { 31 | Files.write(Paths.get(getLogFile()), (logMessage + "\n").getBytes(), StandardOpenOption.APPEND); 32 | } catch(IOException ioe) { 33 | throw new RobotException("ERROR: while writing to log file " + getLogFile()); 34 | } 35 | } 36 | 37 | String getLogFile() { 38 | return logFile; 39 | } 40 | 41 | void setLogFile(String logFile) { 42 | if(logFile == null) { 43 | throw new RobotException("Null file name not allowed."); 44 | } 45 | this.logFile = logFile; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sensor/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.team4 8 | sensor 9 | 1.0-SNAPSHOT 10 | sensor 11 | 12 | com.team4 13 | cleansweep 14 | 1.0-SNAPSHOT 15 | 16 | 17 | 18 | com.team4 19 | commons 20 | 1.0-SNAPSHOT 21 | 22 | 23 | com.google.code.gson 24 | gson 25 | 2.8.5 26 | 27 | 28 | 29 | 30 | 31 | maven-assembly-plugin 32 | 3.1.1 33 | 34 | 35 | jar-with-dependencies 36 | 37 | 38 | 39 | 40 | make-assembly 41 | package 42 | 43 | single 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /sensor/src/test/java/com/team4/sensor/TileFactoryTest.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.Location; 4 | import com.team4.commons.LocationFactory; 5 | import com.team4.commons.RobotException; 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.junit.runners.Parameterized; 10 | import org.junit.runners.Parameterized.Parameters; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Collection; 14 | import java.util.stream.Collectors; 15 | import java.util.stream.Stream; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | 19 | @RunWith(Parameterized.class) 20 | public class TileFactoryTest { 21 | 22 | private Tile first; 23 | private Tile second; 24 | 25 | public TileFactoryTest(Tile first, Tile second) { 26 | this.first = first; 27 | this.second = second; 28 | } 29 | 30 | @Parameters(name = "{index}") 31 | public static Collection data() { 32 | return (ArrayList) Stream.of(new Object[][] { 33 | { TileFactory.createTile(LocationFactory.createLocation(0, 0)), TileFactory.createTile(LocationFactory.createLocation(0, 0)) }, 34 | { TileFactory.createTile(LocationFactory.createLocation(10, 0)), TileFactory.createTile(LocationFactory.createLocation(10, 0)) }, 35 | { TileFactory.createTile(LocationFactory.createLocation(0, 10)), TileFactory.createTile(LocationFactory.createLocation(0, 10)) }, 36 | { TileFactory.createTile(LocationFactory.createLocation(12, 21)), TileFactory.createTile(LocationFactory.createLocation(12, 21)) }, 37 | }).collect(Collectors.toList()); 38 | } 39 | 40 | @Test 41 | public void tile_factory_creates_only_one_tile_object_per_unique_location() { 42 | assertEquals(first, second); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /robot/src/test/java/com/team4/robot/PowerManagerTest.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.ConfigManager; 4 | import com.team4.commons.RobotException; 5 | import com.team4.commons.State; 6 | import org.junit.After; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | public class PowerManagerTest { 13 | @Before 14 | public void init() { 15 | RobotCleanSweep.getInstance().setState(State.WORKING); 16 | RobotCleanSweep.getInstance().getPowerManager().recharge(); 17 | } 18 | @Test 19 | public void power_manager_charges_robot_to_100_percent() { 20 | int maxBatteryLevel = Integer.parseInt(ConfigManager.getConfiguration("maxBatteryLevel")); 21 | RobotCleanSweep.getInstance().getPowerManager().recharge(); 22 | assertEquals(maxBatteryLevel, RobotCleanSweep.getInstance().getPowerManager().getBatteryLevel(), .5); 23 | } 24 | @Test(expected = RobotException.class) 25 | public void power_manager_throws_exception_on_invalid_power_usage_level() { 26 | RobotCleanSweep.getInstance().getPowerManager().updateBatteryLevel(4); 27 | } 28 | @Test 29 | public void robot_goes_to_LOW_BATTERY_state_when_battery_level_is_below_a_certain_amount() { 30 | while(RobotCleanSweep.getInstance().getPowerManager().getBatteryLevel() > 1) { 31 | RobotCleanSweep.getInstance().getPowerManager().updateBatteryLevel(1); 32 | } 33 | RobotCleanSweep.getInstance().getPowerManager().updateBatteryLevel(1); 34 | assertEquals(State.LOW_BATTERY, RobotCleanSweep.getInstance().getState()); 35 | } 36 | @After 37 | public void resetRobt() { 38 | RobotCleanSweep.getInstance().setState(State.WORKING); 39 | RobotCleanSweep.getInstance().getPowerManager().recharge(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /robot/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.team4 8 | robot 9 | 1.0-SNAPSHOT 10 | robot 11 | 12 | com.team4 13 | cleansweep 14 | 1.0-SNAPSHOT 15 | 16 | 17 | 18 | com.team4 19 | sensor 20 | 1.0-SNAPSHOT 21 | 22 | 23 | com.team4 24 | commons 25 | 1.0-SNAPSHOT 26 | 27 | 28 | 29 | 30 | 31 | maven-assembly-plugin 32 | 3.1.1 33 | 34 | 35 | jar-with-dependencies 36 | 37 | 38 | 39 | com.team4.CleanSweepApp 40 | 41 | 42 | 43 | 44 | 45 | make-assembly 46 | package 47 | 48 | single 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/NavigatorBeta.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.Direction; 4 | import com.team4.commons.LocationFactory; 5 | import static com.team4.commons.Direction.*; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | 10 | public class NavigatorBeta implements Navigator { 11 | 12 | @Override 13 | public Direction traverseFloor(Direction [] directions) { 14 | 15 | int x = RobotCleanSweep.getInstance().getLocation().getX(); 16 | int y = RobotCleanSweep.getInstance().getLocation().getY(); 17 | 18 | //SOUTH --> EAST --> NORTH --> WEST 19 | //only if-statements because it is in order of priority 20 | //if one if-statement doesn't return, then we will check the next 21 | //System.out.println(Arrays.toString(directions)); 22 | 23 | ArrayList dirList = new ArrayList<>(Arrays.asList(directions)); 24 | 25 | if(dirList.contains(SOUTH) && !(RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x,y+1)))) { 26 | if(!(dirList.contains(WEST))){ 27 | return SOUTH; 28 | } 29 | if((dirList.contains(WEST)) && (RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x-1,y)))) { 30 | return SOUTH; 31 | } 32 | } 33 | 34 | if(dirList.contains(EAST) && !(RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x+1,y)))) { 35 | return EAST; 36 | } 37 | 38 | if(dirList.contains(NORTH) && !(RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x,y-1)))) { 39 | return NORTH; 40 | } 41 | 42 | if(dirList.contains(WEST) && !(RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x-1,y)))) { 43 | 44 | return WEST; 45 | } 46 | return null; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/ConfigManager.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | import org.w3c.dom.Document; 4 | import org.w3c.dom.Element; 5 | import org.w3c.dom.NodeList; 6 | import org.xml.sax.SAXException; 7 | 8 | import javax.xml.parsers.DocumentBuilderFactory; 9 | import javax.xml.parsers.ParserConfigurationException; 10 | import java.io.IOException; 11 | import java.io.InputStream; 12 | import java.util.Hashtable; 13 | 14 | public class ConfigManager { 15 | 16 | private static Hashtable configurationTable = null; 17 | 18 | private ConfigManager() { 19 | configurationTable = new Hashtable<>(); 20 | String configurationFile = "configuration.xml"; 21 | InputStream inputStream = getClass().getClassLoader().getResourceAsStream(configurationFile); 22 | try { 23 | Document configuration = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream); 24 | NodeList properties = configuration.getElementsByTagName("property"); 25 | for(int i = 0; i < properties.getLength(); i++) { 26 | Element property = (Element) properties.item(i); 27 | String config = property.getAttribute("name"); 28 | String value = property.getAttribute("value"); 29 | configurationTable.put(config, value); 30 | } 31 | } catch(IOException ioe) { 32 | throw new RobotException("Cannot read configuration file."); 33 | } catch(ParserConfigurationException pe) { 34 | throw new RobotException("ERROR: DocumentBuilder cannot be created."); 35 | } catch(SAXException se) { 36 | throw new RobotException("Parse error in configuration file."); 37 | } catch (IllegalArgumentException iae) { 38 | throw new RobotException("ERROR: Check if the system configuration file " + configurationFile + " exists."); 39 | } 40 | } 41 | 42 | public static void initializeSystemConfiguration() { 43 | if(configurationTable == null) { 44 | synchronized (ConfigManager.class) { 45 | if(configurationTable == null) { 46 | new ConfigManager(); 47 | } 48 | } 49 | } 50 | } 51 | 52 | public static String getConfiguration(String config) { 53 | initializeSystemConfiguration(); 54 | return configurationTable.get(config); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/NavigatorOmega.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import static com.team4.commons.Direction.EAST; 4 | import static com.team4.commons.Direction.NORTH; 5 | import static com.team4.commons.Direction.SOUTH; 6 | import static com.team4.commons.Direction.WEST; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | 11 | import com.team4.commons.Direction; 12 | import com.team4.commons.Location; 13 | import com.team4.commons.LocationFactory; 14 | 15 | class NavigatorOmega implements Navigator { 16 | 17 | @Override 18 | public Direction traverseFloor(Direction [] directions) { 19 | 20 | int x = RobotCleanSweep.getInstance().getLocation().getX(); 21 | int y = RobotCleanSweep.getInstance().getLocation().getY(); 22 | 23 | ArrayList dirList = new ArrayList<>(Arrays.asList(directions)); 24 | if(dirList.contains(SOUTH) && !(RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x,y+1)))) { 25 | 26 | if(!(dirList.contains(WEST))) { 27 | return SOUTH; 28 | } else { 29 | //if west visited 30 | if((RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x-1, y)))) { 31 | return SOUTH; 32 | } 33 | //west open and not visited 34 | if(dirList.contains(EAST) && !(RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x+1, y)))) { 35 | return WEST; 36 | } 37 | } 38 | } 39 | 40 | if(dirList.contains(EAST) && !(RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x+1, y)))) { 41 | return EAST; 42 | } 43 | 44 | if(dirList.contains(NORTH) && !(RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x,y-1)))) { 45 | return NORTH; 46 | } 47 | 48 | if(dirList.contains(WEST)) { 49 | if (!(RobotCleanSweep.getInstance().visitedLocation(LocationFactory.createLocation(x-1, y)))) { 50 | return WEST; 51 | } 52 | } 53 | //A* to go to last unvisited cell 54 | if(!RobotCleanSweep.getInstance().visitedAll()) { 55 | Location current = RobotCleanSweep.getInstance().getLocation(); 56 | Location goal = RobotCleanSweep.getInstance().lastUnvisited(); 57 | AStar aStar = new AStar(RobotCleanSweep.getInstance().getGraph(), current, goal,1); 58 | if(aStar.search() != null && !aStar.search().empty()) { 59 | 60 | return aStar.search().pop(); 61 | } 62 | } 63 | return null; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/DirtManager.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.*; 4 | import com.team4.sensor.*; 5 | 6 | import static com.team4.commons.WorkingMode.DEPLOYED; 7 | 8 | class DirtManager implements VacuumCleaner { 9 | 10 | private final int MAX_DIRT = Integer.parseInt(ConfigManager.getConfiguration("dirtCapacity")); 11 | 12 | private int dirtLevel; 13 | 14 | DirtManager() { 15 | dirtLevel = 0; 16 | } 17 | 18 | @Override 19 | public void clean(double cost) { 20 | int newDirtLevel = getDirtLevel() + 1; 21 | if(newDirtLevel > MAX_DIRT) { 22 | RobotCleanSweep.getInstance().setState(State.FULL_TANK); 23 | waitUntilTankEmpty(); 24 | } else { 25 | setDirtLevel(newDirtLevel); 26 | RobotCleanSweep.getInstance().getPowerManager().updateBatteryLevel(cost); 27 | SensorSimulator.getInstance().removeDirtFromLocation(RobotCleanSweep.getInstance().getLocation()); 28 | } 29 | } 30 | 31 | @Override 32 | public int getDirtLevel() { 33 | return this.dirtLevel; 34 | } 35 | 36 | private void setDirtLevel(int dirt) { 37 | if(dirt < 0) { 38 | throw new RobotException("Invalid dirt level: " + dirt); 39 | } 40 | this.dirtLevel = dirt; 41 | } 42 | 43 | private void waitUntilTankEmpty() { 44 | String dirtLevel = Integer.toString(RobotCleanSweep.getInstance().getVacuumCleaner().getDirtLevel()); 45 | String batteryLevel = Double.toString(RobotCleanSweep.getInstance().getPowerManager().getBatteryLevel()); 46 | for(int i =0; i<= 4; i++) { 47 | LogManager.logForUnity(RobotCleanSweep.getInstance().getLocation(), "DIRT_FULL",batteryLevel , dirtLevel); 48 | } 49 | if(RobotCleanSweep.workingMode == DEPLOYED) { 50 | Utilities.doLoopedTimeDelay("Dirt tank full", 1000L, RobotCleanSweep.getInstance().getZeroTime()); 51 | } 52 | emptyTank(); 53 | } 54 | 55 | private void emptyTank() { 56 | setDirtLevel(0); 57 | FloorDao floorDao = SensorSimulator.getInstance().getLocationInfo(RobotCleanSweep.getInstance().getLocation()); 58 | 59 | String dirtLevel = Integer.toString(RobotCleanSweep.getInstance().getVacuumCleaner().getDirtLevel()); 60 | String batteryLevel = Double.toString(RobotCleanSweep.getInstance().getPowerManager().getBatteryLevel()); 61 | clean(floorDao.floorType.getCost()); 62 | LogManager.logForUnity(RobotCleanSweep.getInstance().getLocation(), "DIRT_EMPTY",batteryLevel , dirtLevel); 63 | if(RobotCleanSweep.workingMode == DEPLOYED) { 64 | LogManager.print("Dirt tank emptied", RobotCleanSweep.getInstance().getZeroTime()); 65 | } 66 | RobotCleanSweep.getInstance().setState(State.WORKING); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/Tile.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.FloorType; 4 | import com.team4.commons.Location; 5 | import com.team4.commons.RobotException; 6 | 7 | class Tile { 8 | private Location location; 9 | private FloorType floorType; 10 | private boolean isChargingStation; 11 | private boolean isNorthOpen; 12 | private boolean isSouthOpen; 13 | private boolean isEastOpen; 14 | private boolean isWestOpen; 15 | private boolean isClean; 16 | private int dirtUnits; 17 | 18 | private Tile() { 19 | } 20 | 21 | Tile(Location location) { 22 | this.location = location; 23 | } 24 | 25 | Location getLocation() { 26 | return location; 27 | } 28 | 29 | FloorType getFloorType() { 30 | return floorType; 31 | } 32 | 33 | boolean isChargingStation() { 34 | return isChargingStation; 35 | } 36 | 37 | boolean isNorthOpen() { 38 | return isNorthOpen; 39 | } 40 | 41 | boolean isSouthOpen() { 42 | return isSouthOpen; 43 | } 44 | 45 | boolean isEastOpen() { 46 | return isEastOpen; 47 | } 48 | 49 | boolean isWestOpen() { 50 | return isWestOpen; 51 | } 52 | 53 | boolean isClean() { 54 | return isClean; 55 | } 56 | 57 | int getDirtUnits() { 58 | return dirtUnits; 59 | } 60 | 61 | void setFloorType(FloorType floorType) { 62 | if(floorType == null) { 63 | throw new RobotException("Null floor type not allowed in Tiles."); 64 | } 65 | this.floorType = floorType; 66 | } 67 | 68 | void setChargingStation(boolean chargingStation) { 69 | isChargingStation = chargingStation; 70 | } 71 | 72 | void setNorthOpen(boolean northOpen) { 73 | isNorthOpen = northOpen; 74 | } 75 | 76 | void setSouthOpen(boolean southOpen) { 77 | isSouthOpen = southOpen; 78 | } 79 | 80 | void setEastOpen(boolean eastOpen) { 81 | isEastOpen = eastOpen; 82 | } 83 | 84 | void setWestOpen(boolean westOpen) { 85 | isWestOpen = westOpen; 86 | } 87 | 88 | void setClean(boolean clean) { 89 | isClean = clean; 90 | } 91 | 92 | void setDirtUnits(int dirtUnits) { 93 | if(0 <= dirtUnits && dirtUnits <= 10) {//(dirtUnits == 0 || dirtUnits == 1 || dirtUnits == 2 || dirtUnits == 3) { 94 | this.dirtUnits = dirtUnits; 95 | if(dirtUnits == 0) { 96 | setClean(true); 97 | } 98 | } else { 99 | throw new RobotException("Invalid dirt level " + dirtUnits); 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /commons/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.team4 8 | commons 9 | 1.0-SNAPSHOT 10 | commons 11 | 12 | 13 | UTF-8 14 | 1.8 15 | 1.8 16 | 17 | 18 | 19 | 20 | junit 21 | junit 22 | 4.12 23 | test 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | maven-clean-plugin 33 | 3.1.0 34 | 35 | 36 | 37 | maven-resources-plugin 38 | 3.0.2 39 | 40 | 41 | maven-compiler-plugin 42 | 3.8.0 43 | 44 | 45 | maven-surefire-plugin 46 | 2.22.1 47 | 48 | 49 | maven-jar-plugin 50 | 3.0.2 51 | 52 | 53 | maven-install-plugin 54 | 2.5.2 55 | 56 | 57 | maven-deploy-plugin 58 | 2.8.2 59 | 60 | 61 | 62 | maven-site-plugin 63 | 3.7.1 64 | 65 | 66 | maven-project-info-reports-plugin 67 | 3.0.0 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /sensor/src/main/resources/FP1.json: -------------------------------------------------------------------------------- 1 | { 2 | "southEastCornerCoordinates": {"x": 10, "y": 10}, 3 | "obstacles" : [ 4 | { "from" : { "x" : 0, "y" : 0 }, "to" : { "x" : 10, "y" : 0 }, "obstacleType" : "WALL" }, 5 | { "from" : { "x" : 0, "y" : 0 }, "to" : { "x" : 0, "y" : 10 }, "obstacleType" : "WALL" }, 6 | { "from" : { "x" : 0, "y" : 10 }, "to" : { "x" : 10, "y" : 10 }, "obstacleType" : "WALL" }, 7 | { "from" : { "x" : 10, "y" : 0 }, "to" : { "x" : 10, "y" : 10 }, "obstacleType" : "WALL" }, 8 | { "from" : { "x" : 4, "y" : 0 }, "to" : { "x" : 4, "y" : 10 }, "obstacleType" : "WALL" }, 9 | { "from" : { "x" : 2, "y" : 3 }, "to" : { "x" : 2, "y" : 4 }, "obstacleType" : "WALL" }, 10 | { "from" : { "x" : 6, "y" : 1 }, "to" : { "x" : 6, "y" : 10 }, "obstacleType" : "WALL" }, 11 | { "from" : { "x" : 8, "y" : 0 }, "to" : { "x" : 8, "y" : 3 }, "obstacleType" : "WALL" }, 12 | { "from" : { "x" : 4, "y" : 1 }, "to" : { "x" : 8, "y" : 1 }, "obstacleType" : "WALL" }, 13 | { "from" : { "x" : 4, "y" : 2 }, "to" : { "x" : 6, "y" : 2 }, "obstacleType" : "WALL" }, 14 | { "from" : { "x" : 0, "y" : 3 }, "to" : { "x" : 4, "y" : 3 }, "obstacleType" : "WALL" }, 15 | { "from" : { "x" : 8, "y" : 3 }, "to" : { "x" : 10, "y" : 3 }, "obstacleType" : "WALL" }, 16 | { "from" : { "x" : 0, "y" : 4 }, "to" : { "x" : 4, "y" : 4 }, "obstacleType" : "WALL" }, 17 | { "from" : { "x" : 0, "y" : 5 }, "to" : { "x" : 4, "y" : 5 }, "obstacleType" : "WALL" }, 18 | { "from" : { "x" : 4, "y" : 10 }, "to" : { "x" : 5, "y" : 10 }, "obstacleType" : "STAIRS" } 19 | ], 20 | "passages" : [ 21 | { "from" : { "x" : 6, "y" : 0 }, "to" : { "x" : 6, "y" : 1 } }, 22 | { "from" : { "x" : 7, "y" : 0 }, "to" : { "x" : 7, "y" : 1 } }, 23 | { "from" : { "x" : 7, "y" : 1 }, "to" : { "x" : 8, "y" : 1 } }, 24 | { "from" : { "x" : 4, "y" : 1 }, "to" : { "x" : 4, "y" : 2 } }, 25 | { "from" : { "x" : 5, "y" : 1 }, "to" : { "x" : 5, "y" : 2 } }, 26 | { "from" : { "x" : 5, "y" : 2 }, "to" : { "x" : 6, "y" : 2 } }, 27 | { "from" : { "x" : 3, "y" : 2 }, "to" : { "x" : 4, "y" : 2 } }, 28 | { "from" : { "x" : 0, "y" : 2 }, "to" : { "x" : 0, "y" : 3 } }, 29 | { "from" : { "x" : 1, "y" : 2 }, "to" : { "x" : 1, "y" : 3 } }, 30 | { "from" : { "x" : 3, "y" : 3 }, "to" : { "x" : 4, "y" : 3 } }, 31 | { "from" : { "x" : 1, "y" : 4 }, "to" : { "x" : 1, "y" : 5 } }, 32 | { "from" : { "x" : 2, "y" : 4 }, "to" : { "x" : 2, "y" : 5 } }, 33 | { "from" : { "x" : 3, "y" : 6 }, "to" : { "x" : 4, "y" : 6 } } 34 | ], 35 | "chargingStations" : [ 36 | { "x" : 0, "y" : 9 } 37 | 38 | ], 39 | "areas" : [ 40 | { "topLeft" : { "x" : 0, "y" : 0 }, "bottomRight" : { "x" : 3, "y" : 2 }, "floorType" : "LOW_PILE" }, 41 | { "topLeft" : { "x" : 0, "y" : 5 }, "bottomRight" : { "x" : 3, "y" : 9 }, "floorType" : "LOW_PILE" }, 42 | { "topLeft" : { "x" : 7, "y" : 4 }, "bottomRight" : { "x" : 8, "y" : 8 }, "floorType" : "HIGH_PILE" } 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.team4 8 | cleansweep 9 | 1.0-SNAPSHOT 10 | pom 11 | clean-sweep-robotic-vacuum-cleaner 12 | 13 | robot 14 | sensor 15 | commons 16 | 17 | 18 | UTF-8 19 | 1.8 20 | 1.8 21 | 22 | 23 | 24 | 25 | junit 26 | junit 27 | 4.12 28 | test 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | maven-clean-plugin 38 | 3.1.0 39 | 40 | 41 | 42 | maven-resources-plugin 43 | 3.0.2 44 | 45 | 46 | maven-compiler-plugin 47 | 3.8.0 48 | 49 | 50 | maven-surefire-plugin 51 | 2.22.1 52 | 53 | 54 | maven-jar-plugin 55 | 3.0.2 56 | 57 | 58 | maven-install-plugin 59 | 2.5.2 60 | 61 | 62 | maven-deploy-plugin 63 | 2.8.2 64 | 65 | 66 | 67 | maven-site-plugin 68 | 3.7.1 69 | 70 | 71 | maven-project-info-reports-plugin 72 | 3.0.0 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /sensor/src/test/java/com/team4/sensor/GetLocationInfoNeighborsWithinRadiusTest.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.Location; 4 | import com.team4.commons.LocationFactory; 5 | import org.junit.Ignore; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.junit.runners.Parameterized; 9 | import org.junit.runners.Parameterized.Parameters; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Collection; 13 | import java.util.stream.Collectors; 14 | import java.util.stream.Stream; 15 | 16 | import static org.junit.Assert.assertArrayEquals; 17 | 18 | @RunWith(Parameterized.class) 19 | public class GetLocationInfoNeighborsWithinRadiusTest { 20 | private Location location; 21 | private Location[] neighbors; 22 | 23 | public GetLocationInfoNeighborsWithinRadiusTest(Location location, Location[] neighbors) { 24 | this.location = location; 25 | this.neighbors = neighbors; 26 | } 27 | 28 | @Parameters 29 | public static Collection data() { 30 | return (ArrayList) Stream.of(new Object[][] { 31 | { 32 | LocationFactory.createLocation(0, 0), 33 | new Location [] { 34 | LocationFactory.createLocation(1, 0), 35 | LocationFactory.createLocation(2, 0), 36 | LocationFactory.createLocation(0, 1), 37 | LocationFactory.createLocation(1, 1), 38 | LocationFactory.createLocation(0, 2), 39 | } 40 | }, 41 | { 42 | LocationFactory.createLocation(2, 2), 43 | new Location [] { 44 | LocationFactory.createLocation(2, 0), 45 | LocationFactory.createLocation(1, 1), 46 | LocationFactory.createLocation(2, 1), 47 | LocationFactory.createLocation(3, 1), 48 | LocationFactory.createLocation(0, 2), 49 | LocationFactory.createLocation(1, 2), 50 | LocationFactory.createLocation(3, 2), 51 | LocationFactory.createLocation(4, 2), 52 | LocationFactory.createLocation(1, 3), 53 | LocationFactory.createLocation(2, 3), 54 | LocationFactory.createLocation(3, 3), 55 | LocationFactory.createLocation(2, 4), 56 | } 57 | } 58 | 59 | }).collect(Collectors.toList()); 60 | } 61 | 62 | @Test @Ignore 63 | public void getLocationInfo_returns_correct_neighbors_within_a_given_radius() { 64 | FloorDao floorDao = SensorSimulator.getInstance().getLocationInfo(location); 65 | assertArrayEquals(neighbors, floorDao.chargingStations); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /sensor/src/main/resources/FP2.json: -------------------------------------------------------------------------------- 1 | { 2 | "southEastCornerCoordinates": {"x": 10, "y": 10}, 3 | "obstacles" : [ 4 | { "from" : { "x" : 0, "y" : 0 }, "to" : { "x" : 10, "y" : 0 }, "obstacleType" : "WALL" }, 5 | { "from" : { "x" : 0, "y" : 0 }, "to" : { "x" : 0, "y" : 10 }, "obstacleType" : "WALL" }, 6 | { "from" : { "x" : 0, "y" : 10 }, "to" : { "x" : 10, "y" : 10 }, "obstacleType" : "WALL" }, 7 | { "from" : { "x" : 10, "y" : 0 }, "to" : { "x" : 10, "y" : 10 }, "obstacleType" : "WALL" }, 8 | { "from" : { "x" : 4, "y" : 0 }, "to" : { "x" : 4, "y" : 10 }, "obstacleType" : "WALL" }, 9 | { "from" : { "x" : 2, "y" : 3 }, "to" : { "x" : 2, "y" : 4 }, "obstacleType" : "WALL" }, 10 | { "from" : { "x" : 6, "y" : 1 }, "to" : { "x" : 6, "y" : 10 }, "obstacleType" : "WALL" }, 11 | { "from" : { "x" : 8, "y" : 0 }, "to" : { "x" : 8, "y" : 3 }, "obstacleType" : "WALL" }, 12 | { "from" : { "x" : 4, "y" : 1 }, "to" : { "x" : 8, "y" : 1 }, "obstacleType" : "WALL" }, 13 | { "from" : { "x" : 4, "y" : 2 }, "to" : { "x" : 6, "y" : 2 }, "obstacleType" : "WALL" }, 14 | { "from" : { "x" : 0, "y" : 3 }, "to" : { "x" : 4, "y" : 3 }, "obstacleType" : "WALL" }, 15 | { "from" : { "x" : 8, "y" : 3 }, "to" : { "x" : 10, "y" : 3 }, "obstacleType" : "WALL" }, 16 | { "from" : { "x" : 0, "y" : 4 }, "to" : { "x" : 4, "y" : 4 }, "obstacleType" : "WALL" }, 17 | { "from" : { "x" : 0, "y" : 5 }, "to" : { "x" : 4, "y" : 5 }, "obstacleType" : "WALL" }, 18 | { "from" : { "x" : 4, "y" : 10 }, "to" : { "x" : 5, "y" : 10 }, "obstacleType" : "STAIRS" } 19 | ], 20 | "passages" : [ 21 | { "from" : { "x" : 6, "y" : 0 }, "to" : { "x" : 6, "y" : 1 } }, 22 | { "from" : { "x" : 7, "y" : 0 }, "to" : { "x" : 7, "y" : 1 } }, 23 | { "from" : { "x" : 7, "y" : 1 }, "to" : { "x" : 8, "y" : 1 } }, 24 | { "from" : { "x" : 4, "y" : 1 }, "to" : { "x" : 4, "y" : 2 } }, 25 | { "from" : { "x" : 5, "y" : 1 }, "to" : { "x" : 5, "y" : 2 } }, 26 | { "from" : { "x" : 5, "y" : 2 }, "to" : { "x" : 6, "y" : 2 } }, 27 | { "from" : { "x" : 3, "y" : 2 }, "to" : { "x" : 4, "y" : 2 } }, 28 | { "from" : { "x" : 0, "y" : 2 }, "to" : { "x" : 0, "y" : 3 } }, 29 | { "from" : { "x" : 1, "y" : 2 }, "to" : { "x" : 1, "y" : 3 } }, 30 | { "from" : { "x" : 3, "y" : 3 }, "to" : { "x" : 4, "y" : 3 } }, 31 | { "from" : { "x" : 1, "y" : 4 }, "to" : { "x" : 1, "y" : 5 } }, 32 | { "from" : { "x" : 2, "y" : 4 }, "to" : { "x" : 2, "y" : 5 } }, 33 | { "from" : { "x" : 3, "y" : 6 }, "to" : { "x" : 4, "y" : 6 } } 34 | ], 35 | "chargingStations" : [ 36 | { "x" : 0, "y" : 9 }, 37 | { "x" : 9, "y" : 0 }, 38 | { "x" : 0, "y" : 0 }, 39 | { "x" : 9, "y" : 9 } 40 | 41 | ], 42 | "areas" : [ 43 | { "topLeft" : { "x" : 0, "y" : 0 }, "bottomRight" : { "x" : 3, "y" : 2 }, "floorType" : "LOW_PILE" }, 44 | { "topLeft" : { "x" : 0, "y" : 5 }, "bottomRight" : { "x" : 3, "y" : 9 }, "floorType" : "LOW_PILE" }, 45 | { "topLeft" : { "x" : 7, "y" : 4 }, "bottomRight" : { "x" : 8, "y" : 8 }, "floorType" : "HIGH_PILE" } 46 | ] 47 | } -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/FloorPlan.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | 4 | import com.team4.commons.Location; 5 | import com.team4.commons.RobotException; 6 | 7 | import java.util.List; 8 | 9 | class FloorPlan { 10 | 11 | private Location southEastCornerCoordinates; 12 | private List obstacles; 13 | private List passages; 14 | private List chargingStations; 15 | private List areas; 16 | 17 | FloorPlan(Location southEastCornerCoordinates, List obstacles, List passages, List chargingStations, List areas) { 18 | this.southEastCornerCoordinates = southEastCornerCoordinates; 19 | this.obstacles = obstacles; 20 | this.passages = passages; 21 | this.chargingStations = chargingStations; 22 | this.areas = areas; 23 | } 24 | 25 | Location getSouthEastCornerCoordinates() { 26 | return southEastCornerCoordinates; 27 | } 28 | 29 | void setSouthEastCornerCoordinates(Location southEastCornerCoordinates) { 30 | if(southEastCornerCoordinates == null) { 31 | throw new RobotException("Invalid South East Corner Coordinates"); 32 | } 33 | this.southEastCornerCoordinates = southEastCornerCoordinates; 34 | } 35 | 36 | List getObstacles() { 37 | return obstacles; 38 | } 39 | 40 | List getPassages() { 41 | return passages; 42 | } 43 | 44 | List getChargingStations() { 45 | return chargingStations; 46 | } 47 | 48 | List getAreas() { 49 | return areas; 50 | } 51 | 52 | class Obstacle { 53 | private Location from; 54 | private Location to; 55 | private String obstacleType; 56 | 57 | Obstacle(Location from, Location to, String obstacleType) { 58 | this.from = from; 59 | this.to = to; 60 | this.obstacleType = obstacleType; 61 | } 62 | 63 | Location getFrom() { 64 | return from; 65 | } 66 | 67 | Location getTo() { 68 | return to; 69 | } 70 | 71 | String getObstacleType() { 72 | return obstacleType; 73 | } 74 | } 75 | class Passage { 76 | private Location from; 77 | private Location to; 78 | 79 | Passage(Location from, Location to) { 80 | this.from = from; 81 | this.to = to; 82 | } 83 | 84 | Location getFrom() { 85 | return from; 86 | } 87 | 88 | Location getTo() { 89 | return to; 90 | } 91 | } 92 | 93 | class Area { 94 | private Location topLeft; 95 | private Location bottomRight; 96 | private String floorType; 97 | 98 | Area(Location topLeft, Location bottomRight, String floorType) { 99 | this.topLeft = topLeft; 100 | this.bottomRight = bottomRight; 101 | this.floorType = floorType; 102 | } 103 | 104 | Location getTopLeft() { 105 | return topLeft; 106 | } 107 | 108 | Location getBottomRight() { 109 | return bottomRight; 110 | } 111 | 112 | String getFloorType() { 113 | return floorType; 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/Node.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.Location; 4 | import com.team4.sensor.FloorDao; 5 | import com.team4.sensor.SensorSimulator; 6 | import com.team4.commons.*; 7 | 8 | class Node { 9 | private Location location; 10 | private Node parent; 11 | private int cost; 12 | private int maxCost; 13 | private Direction directionFromParent; 14 | private int f; 15 | private double floorCost; 16 | private double maxFloorCost; 17 | private FloorType floor; 18 | 19 | public Node(Location location) { 20 | setLocation(location); 21 | setParent(null); 22 | directionFromParent = null; 23 | setCost(0); 24 | setMaxCost(0); 25 | setF(0); 26 | setFloorCost(0); 27 | setMaxFloorCost(0); 28 | FloorDao floorDao = SensorSimulator.getInstance().getLocationInfo(location); 29 | setFloor(floorDao.floorType); 30 | } 31 | 32 | public Location getLocation() { 33 | return location; 34 | } 35 | 36 | public void setLocation(Location location) { 37 | this.location = location; 38 | } 39 | 40 | public Node getParent() { 41 | return parent; 42 | } 43 | 44 | public void setParent(Node parent) { 45 | this.parent = parent; 46 | } 47 | 48 | public int getCost() { 49 | return cost; 50 | } 51 | 52 | public void setCost(int cost) { 53 | this.cost = cost; 54 | } 55 | 56 | public int getMaxCost() { 57 | return maxCost; 58 | } 59 | 60 | public void setMaxCost(int maxCost) { 61 | this.maxCost = maxCost; 62 | } 63 | 64 | public Direction getDirection() { 65 | return directionFromParent; 66 | } 67 | 68 | public void setDirection() { 69 | //sameX 70 | int parentX = getParent().getLocation().getX(); 71 | int parentY = getParent().getLocation().getY(); 72 | int meX = getLocation().getX(); 73 | int meY= getLocation().getY(); 74 | 75 | if(meX == parentX) { 76 | if(meY == parentY + 1) { 77 | this.directionFromParent = Direction.SOUTH; 78 | return; 79 | } 80 | if(meY == parentY -1) { 81 | this.directionFromParent = Direction.NORTH; 82 | return; 83 | } 84 | } 85 | 86 | if(meY == parentY) { 87 | if(meX == parentX + 1) { 88 | this.directionFromParent = Direction.EAST; 89 | return; 90 | } 91 | if(meX == parentX - 1) { 92 | this.directionFromParent = Direction.WEST; 93 | return; 94 | } 95 | } 96 | } 97 | 98 | public int getF() { 99 | return f; 100 | } 101 | 102 | public void setF(int f) { 103 | this.f = f; 104 | } 105 | 106 | @Override 107 | public boolean equals(Object object ) { 108 | 109 | if(!(object instanceof Node)) { 110 | return false; 111 | } 112 | Node node = (Node) object; 113 | 114 | return node.getLocation().equals(getLocation()); 115 | } 116 | 117 | @Override 118 | public int hashCode() { 119 | 120 | return this.getLocation().hashCode(); 121 | } 122 | 123 | public double getFloorCost() { 124 | return floorCost; 125 | } 126 | 127 | public void setFloorCost(double floorCost) { 128 | this.floorCost = floorCost; 129 | } 130 | 131 | public double getMaxFloorCost() { 132 | return maxFloorCost; 133 | } 134 | 135 | public void setMaxFloorCost(double floorCost) { 136 | this.maxFloorCost = floorCost; 137 | } 138 | 139 | public FloorType getFloor() { 140 | return floor; 141 | } 142 | 143 | public void setFloor(FloorType floor) { 144 | this.floor = floor; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /sensor/src/test/java/com/team4/sensor/GetLocationInfoCorrectDirectionsOfPassageTest.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.Direction; 4 | import static com.team4.commons.Direction.*; 5 | import static org.junit.Assert.assertArrayEquals; 6 | 7 | import com.team4.commons.Location; 8 | import com.team4.commons.LocationFactory; 9 | import org.junit.Before; 10 | import org.junit.Ignore; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.junit.runners.Parameterized; 14 | import org.junit.runners.Parameterized.Parameters; 15 | 16 | import java.util.ArrayList; 17 | import java.util.Collection; 18 | import java.util.stream.Collectors; 19 | import java.util.stream.Stream; 20 | 21 | @RunWith(Parameterized.class) 22 | public class GetLocationInfoCorrectDirectionsOfPassageTest { 23 | 24 | private Location location; 25 | private Direction[] passages; 26 | 27 | public GetLocationInfoCorrectDirectionsOfPassageTest(Location location, Direction[] passages) { 28 | this.location = location; 29 | this.passages = passages; 30 | } 31 | 32 | @Before 33 | public void init() { 34 | } 35 | 36 | @Parameters(name = "{index}") 37 | public static Collection data() { 38 | return (ArrayList) Stream.of(new Object[][] { 39 | { LocationFactory.createLocation(0, 0), new Direction[] {SOUTH, EAST} }, 40 | { LocationFactory.createLocation(0, 9), new Direction[] {NORTH, EAST} }, 41 | { LocationFactory.createLocation(9, 0), new Direction[] {SOUTH, WEST} }, 42 | { LocationFactory.createLocation(9, 9), new Direction[] {NORTH, WEST} }, 43 | { LocationFactory.createLocation(5, 0), new Direction[] {SOUTH, EAST, WEST} }, 44 | { LocationFactory.createLocation(5, 9), new Direction[] {NORTH, EAST, WEST} }, 45 | { LocationFactory.createLocation(0, 5), new Direction[] {NORTH, SOUTH, EAST} }, 46 | { LocationFactory.createLocation(9, 5), new Direction[] {NORTH, SOUTH, WEST} } 47 | //{ LocationFactory.createLocation(0, 1), new Direction[] {NORTH, SOUTH, EAST} }, 48 | //{ LocationFactory.createLocation(0, 5), new Direction[] {SOUTH, EAST} }, 49 | //{ LocationFactory.createLocation(0, 4), new Direction[] {EAST} }, 50 | //{ LocationFactory.createLocation(1, 0), new Direction[] {SOUTH, EAST, WEST} }, 51 | //{ LocationFactory.createLocation(2, 0), new Direction[] {SOUTH, EAST, WEST} }, 52 | //{ LocationFactory.createLocation(1, 1), new Direction[] {NORTH, SOUTH, EAST, WEST} }, 53 | //{ LocationFactory.createLocation(5, 3), new Direction[] {NORTH, SOUTH, WEST} }, 54 | //{ LocationFactory.createLocation(6, 2), new Direction[] {NORTH, SOUTH, EAST, WEST} }, 55 | //{ LocationFactory.createLocation(4, 7), new Direction[] {NORTH, SOUTH, EAST} }, 56 | //{ LocationFactory.createLocation(8, 8), new Direction[] {NORTH, SOUTH, EAST, WEST} }, 57 | //{ LocationFactory.createLocation(1, 5), new Direction[] {NORTH, SOUTH, EAST, WEST} }, 58 | //{ LocationFactory.createLocation(7, 6), new Direction[] {NORTH, SOUTH, EAST, WEST} }, 59 | //{ LocationFactory.createLocation(3, 3), new Direction[] {EAST, WEST} }, 60 | //{ LocationFactory.createLocation(2, 7), new Direction[] {NORTH, SOUTH, EAST, WEST} } 61 | }).collect(Collectors.toList()); 62 | } 63 | @Test @Ignore 64 | public void getLocationInfo_returns_correct_direction_of_open_passages_for_a_given_location() { 65 | FloorDao dao = SensorSimulator.getInstance().getLocationInfo(location); 66 | assertArrayEquals(passages, dao.openPassages); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/AStar.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.PriorityQueue; 8 | import java.util.Stack; 9 | 10 | import com.team4.commons.Direction; 11 | import com.team4.commons.Location; 12 | 13 | class AStar { 14 | 15 | private HashMap> graph = new HashMap<>(); 16 | private Node initial; 17 | private Location goal; 18 | private int chooseComp; 19 | private Node pathNode; 20 | 21 | public AStar(HashMap> graph, Location location, Location goal, int comp) { 22 | this.graph = graph; 23 | this.initial = new Node(location); 24 | this.goal = goal; 25 | this.chooseComp = comp; 26 | this.pathNode = null; 27 | } 28 | 29 | class fComparator implements Comparator { 30 | Heuristics h = new Heuristics(getGoal()); 31 | public int compare(Node a, Node b) { 32 | a.setF( h.manhattan(a) + a.getCost()); 33 | b.setF((b.getCost() + h.manhattan(b))); 34 | return (a.getF() - b.getF()); 35 | } 36 | } 37 | 38 | class gComparator implements Comparator { 39 | public int compare(Node a, Node b) { 40 | if( a.getMaxFloorCost() < b.getMaxFloorCost() )return -1; 41 | if( a.getMaxFloorCost() > b.getMaxFloorCost() )return 1; 42 | return 0; 43 | } 44 | } 45 | 46 | Node getPathNode(){ 47 | return this.pathNode; 48 | } 49 | 50 | void setPathNode(Node node) { 51 | this.pathNode = node; 52 | } 53 | Stack path(Node node){ 54 | Node node1 = node; 55 | Stack directions = new Stack(); 56 | while(node1.getParent()!= null) { 57 | directions.add(node1.getDirection()); 58 | node1 = node1.getParent(); 59 | } 60 | return directions; 61 | } 62 | 63 | Location getGoal() { 64 | return this.goal; 65 | } 66 | 67 | Stack search() { 68 | PriorityQueue pQueue; 69 | if(chooseComp == 1) { 70 | pQueue = new PriorityQueue(new fComparator()); 71 | } 72 | else if( chooseComp == 2) { 73 | pQueue = new PriorityQueue(new gComparator()); 74 | } 75 | else { 76 | pQueue = null; 77 | } 78 | 79 | ArrayList expanded = new ArrayList(); 80 | Node node = initial; 81 | pQueue.add(node); 82 | 83 | while(!pQueue.isEmpty()) { 84 | node = pQueue.poll(); 85 | 86 | pQueue.remove(node); 87 | expanded.add(node.getLocation()); 88 | 89 | // if we are the goal location 90 | if(node.getLocation().equals(getGoal())) { 91 | setPathNode(node); 92 | path(node); 93 | return path(node); 94 | } 95 | 96 | //generate children 97 | List children = getGraph().get(node.getLocation()); 98 | 99 | if(children != null) { 100 | 101 | for(Location child : children) { 102 | 103 | if(!expanded.contains(child)) { 104 | 105 | Node childNode = new Node(child); 106 | childNode.setParent(node); 107 | childNode.setCost(node.getCost()+1); 108 | childNode.setDirection(); 109 | double parentCost = childNode.getParent().getFloor().getCost(); 110 | double myCost = childNode.getFloor().getCost(); 111 | double cost = (parentCost + myCost)/2; 112 | childNode.setFloorCost(cost); 113 | childNode.setMaxFloorCost(childNode.getFloorCost() + childNode.getParent().getMaxFloorCost()); 114 | pQueue.add(childNode); 115 | } 116 | } 117 | } 118 | } 119 | 120 | return null; 121 | } 122 | 123 | public HashMap> getGraph() { 124 | return graph; 125 | } 126 | 127 | public void setGraph(HashMap> graph) { 128 | this.graph = graph; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/LogManager.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | 6 | public class LogManager { 7 | 8 | public static void print(String msg, long zeroTime) { 9 | StringBuilder sb = new StringBuilder(Utilities.formatElapsedTime(System.currentTimeMillis(), zeroTime)); 10 | sb.append(msg); 11 | 12 | System.out.println(sb.toString()); 13 | 14 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); 15 | String logFileName = formatter.format(new Date()) + "-daily.log"; 16 | TextFileLogger textFileLogger = new TextFileLogger(logFileName); 17 | textFileLogger.log(sb.toString()); 18 | } 19 | 20 | /** 21 | * Save performance figures for each run. 22 | * @param zeroTime 23 | * @param currentMillis 24 | * @param initLocation 25 | * @param lastLocation 26 | * @param percentDone 27 | */ 28 | //START_DATETIME FINISH_DATETIME INITIAL_LOCATION FINAL_LOCATION PERCENTAGE_DONE TIME_TAKEN 29 | public static void logCurrentRun(long zeroTime, long currentMillis, String initLocation, String lastLocation, double percentDone) { 30 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss"); 31 | Date startTime = new Date(zeroTime); 32 | Date finishTime = new Date(currentMillis); 33 | long duration = currentMillis - zeroTime; 34 | StringBuilder sb = new StringBuilder(); 35 | sb.append(formatter.format(startTime)); 36 | sb.append("\t"); 37 | sb.append(formatter.format(finishTime)); 38 | sb.append("\t"); 39 | sb.append(initLocation); 40 | sb.append("\t"); 41 | sb.append(lastLocation); 42 | sb.append("\t"); 43 | sb.append(percentDone); 44 | sb.append("\t"); 45 | sb.append(duration); 46 | 47 | String summaryLogFile = ConfigManager.getConfiguration("summaryLogFile"); 48 | TextFileLogger textFileLogger = new TextFileLogger(summaryLogFile); 49 | textFileLogger.log(sb.toString()); 50 | } 51 | 52 | public static void logForUnity(Location location, boolean floorIsCleanBefore, boolean floorIsCleanAfter, String batteryAfter, String dirtAfter) { 53 | StringBuilder simple = new StringBuilder(); 54 | String loc = location.toString().replace(", ", ","); 55 | simple.append(Utilities.padSpacesToFront("pos" + loc.replace("(", "[").replace(")", "]"), 8)); 56 | simple.append(" "); 57 | simple.append(Utilities.padSpacesToFront((floorIsCleanBefore) ? "act[ALREADY_CLEAN]" : "", 0)); 58 | 59 | simple.append(Utilities.padSpacesToFront((floorIsCleanAfter && !floorIsCleanBefore) ? "act[CLEAN]" : "",0)); 60 | simple.append(" "); 61 | simple.append(Utilities.padSpacesToFront( "b[" + batteryAfter + "]" ,0)); 62 | simple.append(" "); 63 | simple.append(Utilities.padSpacesToFront( "d[" + dirtAfter + "]" ,0)); 64 | 65 | TextFileLogger textFileLogger = new TextFileLogger(ConfigManager.getConfiguration("unityLogFile")); 66 | textFileLogger.log(simple.toString()); 67 | } 68 | public static void logForUnity(Location location, String state, String batteryAfter, String dirtAfter) { 69 | StringBuilder simple = new StringBuilder(); 70 | String loc = location.toString().replace(", ", ","); 71 | simple.append(Utilities.padSpacesToFront("pos" + loc.replace("(", "[").replace(")", "]"), 8)); 72 | simple.append(" "); 73 | simple.append(Utilities.padSpacesToFront( "act[" + state + "]" ,0)); 74 | simple.append(" "); 75 | simple.append(Utilities.padSpacesToFront( "b[" + batteryAfter + "]" ,0)); 76 | simple.append(" "); 77 | simple.append(Utilities.padSpacesToFront( "d[" + dirtAfter + "]" ,0)); 78 | TextFileLogger textFileLogger = new TextFileLogger(ConfigManager.getConfiguration("unityLogFile")); 79 | textFileLogger.log(simple.toString()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/NavigatorAlpha.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.Direction; 4 | 5 | 6 | import com.team4.commons.LocationFactory; 7 | import com.team4.sensor.SensorSimulator; 8 | import static com.team4.commons.Direction.*; 9 | import static com.team4.commons.State.*; 10 | 11 | /** 12 | * This class was just for test and demo purposes 13 | * only and is now supplanted by NavigatorBeta 14 | */ 15 | 16 | class NavigatorAlpha implements Navigator { 17 | 18 | @Override 19 | public Direction traverseFloor(Direction [] directions) { 20 | final int FLOOR_WIDTH = SensorSimulator.getInstance().getFloorDimension()[0]; 21 | final int FLOOR_LENGTH = SensorSimulator.getInstance().getFloorDimension()[1]; 22 | System.out.println("Robot in " + RobotCleanSweep.getInstance().getState() + " mode."); 23 | System.out.println("Robot going into " + WORKING.toString() + " mode"); 24 | RobotCleanSweep.getInstance().setState(WORKING); 25 | Direction next = EAST; 26 | int x = RobotCleanSweep.getInstance().getLocation().getX(); 27 | int y = RobotCleanSweep.getInstance().getLocation().getY(); 28 | for(int j = 0; j < y; j++) { 29 | System.out.println(); 30 | } 31 | while(RobotCleanSweep.getInstance().getLocation().getY() < FLOOR_LENGTH - 1) { 32 | System.out.println("(" + RobotCleanSweep.getInstance().getLocation().getX() + ", " + RobotCleanSweep.getInstance().getLocation().getY() + ")"); 33 | try { 34 | Thread.sleep(500L); 35 | if(RobotCleanSweep.getInstance().getLocation().getY() == FLOOR_LENGTH - 1) { 36 | break; 37 | } 38 | move(SOUTH); 39 | } catch (InterruptedException ie) { 40 | ie.printStackTrace(); 41 | } 42 | } 43 | String tabs = ""; 44 | for(int i = 0; i < x; i++) { 45 | tabs = tabs + "\t"; 46 | } 47 | System.out.print(tabs); 48 | while(RobotCleanSweep.getInstance().getLocation().getX() <= FLOOR_WIDTH - 1) { 49 | System.out.print("(" + RobotCleanSweep.getInstance().getLocation().getX() + ", " + RobotCleanSweep.getInstance().getLocation().getY() + ")\t"); 50 | try { 51 | Thread.sleep(500L); 52 | if(RobotCleanSweep.getInstance().getLocation().getX() == FLOOR_WIDTH - 1) { 53 | break; 54 | } 55 | move(EAST); 56 | tabs = (next == EAST) ? tabs + "\t" : tabs; 57 | next = (next == EAST) ? SOUTH : EAST; 58 | } catch (InterruptedException ie) { 59 | ie.printStackTrace(); 60 | } 61 | } 62 | RobotCleanSweep.getInstance().setState(STANDBY); 63 | System.out.println("\nRobot in " + RobotCleanSweep.getInstance().getState() + " mode."); 64 | return null; 65 | } 66 | 67 | void move(Direction direction) { 68 | final int FLOOR_WIDTH = SensorSimulator.getInstance().getFloorDimension()[0]; 69 | final int FLOOR_LENGTH = SensorSimulator.getInstance().getFloorDimension()[1]; 70 | 71 | int currentX = RobotCleanSweep.getInstance().getLocation().getX(); 72 | int currentY = RobotCleanSweep.getInstance().getLocation().getY(); 73 | switch(direction) { 74 | case EAST: 75 | if(currentX == FLOOR_WIDTH - 1) { 76 | return; 77 | } 78 | RobotCleanSweep.getInstance().setLocation(LocationFactory.createLocation(currentX + 1, currentY)); 79 | return; 80 | case WEST: 81 | if(currentX == 0) { 82 | return; 83 | } 84 | RobotCleanSweep.getInstance().setLocation(LocationFactory.createLocation(currentX - 1, currentY)); 85 | return; 86 | case NORTH: 87 | if(currentY == 0) { 88 | return; 89 | } 90 | RobotCleanSweep.getInstance().setLocation(LocationFactory.createLocation(currentX, currentY - 1)); 91 | return; 92 | case SOUTH: 93 | if(currentX == FLOOR_LENGTH) { 94 | return; 95 | } 96 | RobotCleanSweep.getInstance().setLocation(LocationFactory.createLocation(currentX, currentY + 1)); 97 | return; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/PowerUnit.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.*; 4 | import com.team4.sensor.FloorDao; 5 | import com.team4.sensor.SensorSimulator; 6 | 7 | import static com.team4.commons.State.LOW_BATTERY; 8 | import static com.team4.commons.State.CHARGING; 9 | import static com.team4.commons.State.OFF; 10 | import static com.team4.commons.WorkingMode.DEPLOYED; 11 | 12 | class PowerUnit implements PowerManager { 13 | 14 | private double batteryLevel; 15 | 16 | PowerUnit() { 17 | int maxBatteryLevel = Integer.parseInt(ConfigManager.getConfiguration("maxBatteryLevel")); 18 | setBatteryLevel(maxBatteryLevel); 19 | } 20 | 21 | @Override 22 | public void recharge() { 23 | RobotCleanSweep.getInstance().setState(CHARGING); 24 | long timeToCharge = Long.parseLong(ConfigManager.getConfiguration("timeToCharge")); 25 | if(RobotCleanSweep.workingMode == DEPLOYED) { 26 | LogManager.print("Charging", RobotCleanSweep.getInstance().getZeroTime()); 27 | Utilities.doTimeDelay(timeToCharge); 28 | } 29 | int maxBatteryLevel = Integer.parseInt(ConfigManager.getConfiguration("maxBatteryLevel")); 30 | setBatteryLevel(maxBatteryLevel); 31 | } 32 | 33 | @Override 34 | public double getBatteryLevel() { 35 | return batteryLevel; 36 | } 37 | 38 | @Override 39 | public void updateBatteryLevel(double units) { 40 | if(units < 0 || units > 3) { 41 | throw new RobotException("Invalid power usage level."); 42 | } 43 | setBatteryLevel(getBatteryLevel() - units); 44 | 45 | double batteryNeededToReachToKnownChargingStation; 46 | if(RobotCleanSweep.getInstance().getChargingStations().size() > 0) { 47 | batteryNeededToReachToKnownChargingStation = 200; 48 | } else { 49 | batteryNeededToReachToKnownChargingStation = 0; 50 | } 51 | 52 | if(getBatteryLevel() <= batteryNeededToReachToKnownChargingStation) { 53 | for(Location chargingStation : RobotCleanSweep.getInstance().getChargingStations()) { 54 | AStar aStar = new AStar(RobotCleanSweep.getInstance().getGraph(),RobotCleanSweep.getInstance().getLocation(),chargingStation ,2); 55 | if(aStar.search()!=null) { 56 | double temp = aStar.getPathNode().getMaxFloorCost() + 7.0; 57 | 58 | if(temp <= batteryNeededToReachToKnownChargingStation) { 59 | batteryNeededToReachToKnownChargingStation = temp; 60 | RobotCleanSweep.getInstance().setCurrentChargingStation(chargingStation); 61 | } 62 | } else { 63 | if(batteryNeededToReachToKnownChargingStation == 200) { 64 | batteryNeededToReachToKnownChargingStation = 0; 65 | } 66 | } 67 | } 68 | } 69 | 70 | if(getBatteryLevel() <= batteryNeededToReachToKnownChargingStation) { 71 | String dirtLevel = Integer.toString(RobotCleanSweep.getInstance().getVacuumCleaner().getDirtLevel()); 72 | LogManager.logForUnity(RobotCleanSweep.getInstance().getLocation(), "GO_CHARGE", Double.toString(getBatteryLevel()), dirtLevel); 73 | if(RobotCleanSweep.getInstance().getState() != LOW_BATTERY) { 74 | RobotCleanSweep.getInstance().setState(LOW_BATTERY); 75 | } 76 | FloorDao floorDao = SensorSimulator.getInstance().getLocationInfo(RobotCleanSweep.getInstance().getLocation()); 77 | if(RobotCleanSweep.workingMode == DEPLOYED) { 78 | Long timeInTile = Long.parseLong(ConfigManager.getConfiguration("timeInTile")); 79 | Utilities.doTimeDelay(timeInTile); 80 | RobotCleanSweep.getInstance().logTileInfo( 81 | floorDao, 82 | floorDao, 83 | RobotCleanSweep.getInstance().getPowerManager().getBatteryLevel(), 84 | RobotCleanSweep.getInstance().getVacuumCleaner().getDirtLevel(), 85 | RobotCleanSweep.getInstance().getVacuumCleaner().getDirtLevel(), 86 | null); 87 | } 88 | } 89 | } 90 | 91 | private void setBatteryLevel(double batteryLevel) { 92 | final int maxBatteryLevel = Integer.parseInt(ConfigManager.getConfiguration("maxBatteryLevel")); 93 | if( batteryLevel > maxBatteryLevel) { 94 | throw new RobotException("Invalid battery level."); 95 | } 96 | if(batteryLevel < 0) { 97 | RobotCleanSweep.getInstance().setState(OFF); 98 | return; 99 | } 100 | this.batteryLevel = batteryLevel; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/SensorSimulator.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.*; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | 8 | import static com.team4.commons.Direction.*; 9 | 10 | public class SensorSimulator implements Sensor { 11 | 12 | private static Floor floor; 13 | private ArrayList doneTiles = new ArrayList<>(); 14 | 15 | private static SensorSimulator simulator = null; 16 | private SensorSimulator() { 17 | this.floor = Floor.getInstance(); 18 | } 19 | 20 | /** 21 | * SensorSimulator is a singleton since there is only one simulator per robot. 22 | * 23 | * @return SensorSimulator 24 | */ 25 | public static SensorSimulator getInstance() { 26 | if(simulator == null) { 27 | synchronized (SensorSimulator.class) { 28 | if(simulator == null) { 29 | simulator = new SensorSimulator(); 30 | } 31 | } 32 | } 33 | return simulator; 34 | } 35 | 36 | static Floor getFloor() { 37 | return floor; 38 | } 39 | 40 | ArrayList getDoneTiles() { 41 | return doneTiles; 42 | } 43 | 44 | @Override 45 | public FloorDao getLocationInfo(Location location) { 46 | Tile tile = getFloor().getTile(location); 47 | if(tile == null) { 48 | throw new RobotException("There is no such location: " + location.toString()); 49 | } 50 | FloorDao dao = new FloorDao(); 51 | dao.floorType = tile.getFloorType(); 52 | Direction [] directions = new Direction[4]; 53 | int index = 0; 54 | if(tile.isNorthOpen()) { 55 | directions[index++] = NORTH; 56 | } 57 | if(tile.isSouthOpen()) { 58 | directions[index++] = SOUTH; 59 | } 60 | if(tile.isEastOpen()) { 61 | directions[index++] = EAST; 62 | } 63 | if(tile.isWestOpen()) { 64 | directions[index++] = WEST; 65 | } 66 | dao.openPassages = Arrays.copyOf(directions, index); 67 | dao.chargingStations = getChargingStations(tile.getLocation()); 68 | dao.isClean = tile.isClean(); 69 | return dao; 70 | } 71 | 72 | @Override 73 | public void setTileDone(Location location) { 74 | if(location == null) { 75 | throw new RobotException("Null location is not allowed"); 76 | } 77 | TileFactory.createTile(location).setClean(true); 78 | if(!getDoneTiles().contains(location)) { 79 | getDoneTiles().add(location); 80 | } 81 | } 82 | 83 | @Override 84 | public void removeDirtFromLocation(Location location) { 85 | if(location == null) { 86 | throw new RobotException("Null location is not allowed"); 87 | } 88 | int dirtUnits = TileFactory.createTile(location).getDirtUnits(); 89 | if(dirtUnits == 0) { 90 | throw new RobotException("Robot was trying to vacuum a clean tile."); 91 | } 92 | dirtUnits--; 93 | TileFactory.createTile(location).setDirtUnits(dirtUnits); 94 | if(dirtUnits == 0) { 95 | TileFactory.createTile(location).setClean(true); 96 | setTileDone(location); 97 | } else { 98 | TileFactory.createTile(location).setClean(false); 99 | } 100 | } 101 | 102 | public double getDonePercentage() { 103 | double percentageOfDoneTiles = ((double) getDoneTiles().size() / (double) getFloor().getTiles().size()) * 100.0; 104 | return percentageOfDoneTiles; 105 | } 106 | 107 | /** 108 | * Right now, done means all tiles visited. 109 | * 110 | * @return boolean 111 | */ 112 | public boolean isFloorDone() { 113 | System.out.printf("Done tiles = %d & Total # = %d", getDoneTiles().size(), getFloor().getTiles().size()); 114 | if(getDoneTiles().size() == getFloor().getTiles().size()) { 115 | return true; 116 | } 117 | return false; 118 | } 119 | 120 | public static int [] getFloorDimension() { 121 | return new int[] { Floor.WIDTH, Floor.LENGTH }; 122 | } 123 | 124 | private int getNumberOfNeighborsWithinRadius(int radius) { 125 | if(radius < 1) { 126 | throw new RobotException("A neighboring cell is at least 1 cell away."); 127 | } 128 | switch(radius) { 129 | case 1: 130 | return 4; 131 | case 2: 132 | default: 133 | return 12; 134 | } 135 | } 136 | 137 | private Location [] getChargingStations(Location location) { 138 | Location [] neighbors = getNeighborsWithinChargingStationDetectionRadius(location); 139 | final int NUMBER_OF_NEIGHBORS_WITHIN_RADIUS = getNumberOfNeighborsWithinRadius(2); 140 | Location [] chargingStations = new Location[NUMBER_OF_NEIGHBORS_WITHIN_RADIUS + 1]; 141 | int index = 0; 142 | if(TileFactory.createTile(location).isChargingStation()) { 143 | chargingStations[index++] = location; 144 | } 145 | for(Location l : neighbors) { 146 | if(TileFactory.createTile(l).isChargingStation()) { 147 | chargingStations[index++] = l; 148 | } 149 | } 150 | return Arrays.copyOf(chargingStations, index); 151 | } 152 | 153 | private Location [] getNeighborsWithinChargingStationDetectionRadius(Location location) { 154 | int chargingStationDetectionRadius = Integer.parseInt(ConfigManager.getConfiguration("chargingStationDetectionRadius")); 155 | switch(chargingStationDetectionRadius) { 156 | case 1: 157 | return neighborsWithinRadiusOf1(location); 158 | case 2: 159 | default: 160 | return neighborsWithinRadiusOf2(location); 161 | } 162 | } 163 | private Location [] neighborsWithinRadiusOf1(Location location) { 164 | final int WIDTH = Floor.WIDTH; 165 | final int LENGTH = Floor.LENGTH; 166 | final int NUMBER_OF_NEIGHBORS_WITHIN_RADIUS = getNumberOfNeighborsWithinRadius(1); 167 | Location [] neighbors = new Location[NUMBER_OF_NEIGHBORS_WITHIN_RADIUS]; 168 | int index = 0; 169 | //(x, y-1) 170 | if(location.getY() >= 1) { 171 | neighbors[index++] = LocationFactory.createLocation(location.getX(), location.getY() - 1); 172 | } 173 | //(x, y+1) 174 | if(location.getY() <= LENGTH - 2) { 175 | neighbors[index++] = LocationFactory.createLocation(location.getX(), location.getY() + 1); 176 | } 177 | //(x-1, y) 178 | if(location.getX() >= 1) { 179 | neighbors[index++] = LocationFactory.createLocation(location.getX() - 1, location.getY()); 180 | } 181 | //(x+1, y) 182 | if(location.getX() <= WIDTH - 2) { 183 | neighbors[index++] = LocationFactory.createLocation(location.getX() + 1, location.getY()); 184 | } 185 | return Arrays.copyOf(neighbors, index); 186 | } 187 | private Location [] neighborsWithinRadiusOf2(Location location) { 188 | int x = location.getX(); 189 | int y = location.getY(); 190 | final int WIDTH = Floor.WIDTH; 191 | final int LENGTH = Floor.LENGTH; 192 | final int NUMBER_OF_NEIGHBORS_WITHIN_RADIUS = getNumberOfNeighborsWithinRadius(2); 193 | Location [] neighbors = new Location[NUMBER_OF_NEIGHBORS_WITHIN_RADIUS]; 194 | int index = 0; 195 | //(x, y-2) 196 | if(y >= 2) { 197 | neighbors[index++] = LocationFactory.createLocation(x, y - 2); 198 | } 199 | //(x-1, y-1) 200 | if(x >= 1 && y >=1) { 201 | neighbors[index++] = LocationFactory.createLocation(x-1, y-1); 202 | } 203 | //(x, y-1) 204 | if(y >= 1) { 205 | neighbors[index++] = LocationFactory.createLocation(x, y-1); 206 | } 207 | //(x+1, y-1) 208 | if(x <= WIDTH - 2 && y >= 1) { 209 | neighbors[index++] = LocationFactory.createLocation(x+1, y-1); 210 | } 211 | //(x-2, y) 212 | if(x >= 2) { 213 | neighbors[index++] = LocationFactory.createLocation(x-2, y); 214 | } 215 | //(x-1, y) 216 | if(x >= 1) { 217 | neighbors[index++] = LocationFactory.createLocation(x-1, y); 218 | } 219 | //(x+1, y) 220 | if(x <= WIDTH - 2) { 221 | neighbors[index++] = LocationFactory.createLocation(x+1, y); 222 | } 223 | //(x+2, y) 224 | if(x <= WIDTH - 3) { 225 | neighbors[index++] = LocationFactory.createLocation(x+2, y); 226 | } 227 | //(x-1, y+1) 228 | if(x >= 1 && y <= LENGTH - 2) { 229 | neighbors[index++] = LocationFactory.createLocation(x-1, y+1); 230 | } 231 | //(x, y+1) 232 | if(y <= LENGTH - 2) { 233 | neighbors[index++] = LocationFactory.createLocation(x, y+1); 234 | } 235 | //(x+1, y+1) 236 | if(x <= WIDTH - 2 && y <= LENGTH - 2) { 237 | neighbors[index++] = LocationFactory.createLocation(x+1, y+1); 238 | } 239 | //(x, y+2) 240 | if(y <= LENGTH - 3) { 241 | neighbors[index++] = LocationFactory.createLocation(x, y+2); 242 | } 243 | return Arrays.copyOf(neighbors, index); 244 | } 245 | } -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/InMemoryFloorBuilder.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.team4.commons.*; 4 | 5 | 6 | 7 | import java.util.HashMap; 8 | 9 | import static com.team4.commons.FloorType.*; 10 | 11 | class InMemoryFloorBuilder implements FloorBuilder { 12 | 13 | private HashMap tiles; 14 | 15 | InMemoryFloorBuilder(HashMap tiles) { 16 | if(tiles == null) { 17 | throw new RobotException("Null HashMap not allowed for tiles."); 18 | } 19 | this.tiles = tiles; 20 | } 21 | 22 | private HashMap getTiles() { 23 | return tiles; 24 | } 25 | 26 | @Override 27 | public void buildFloor() { 28 | Floor.WIDTH = Integer.parseInt(ConfigManager.getConfiguration("floorWidth")); 29 | Floor.LENGTH = Integer.parseInt(ConfigManager.getConfiguration("floorLength")); 30 | final int W = Floor.WIDTH; 31 | final int L = Floor.LENGTH; 32 | buildCornerTiles(W, L); 33 | buildNorthWall(W, L); 34 | buildSouthWall(W, L); 35 | buildWestWall(W, L); 36 | buildEastWall(W, L); 37 | buildInteriorTiles(W, L); 38 | //vert wall 39 | buildWall( 40 | LocationFactory.createLocation(W - 3, 0), 41 | LocationFactory.createLocation(W - 3, L - 3), 42 | new Location [] { LocationFactory.createLocation(W - 3, L - 2) }, true); //changed from L-3 43 | //hor wall 44 | buildWall( 45 | LocationFactory.createLocation(W - 3, L - 2), 46 | LocationFactory.createLocation(W - 1, L - 2), 47 | new Location [] {}, true); 48 | 49 | //vert wall 50 | buildWall( 51 | LocationFactory.createLocation(W - 7, L-4), 52 | LocationFactory.createLocation(W - 7, L - 1), 53 | new Location [] { }, false); 54 | //hor 55 | buildWall( 56 | LocationFactory.createLocation(0, L-4), 57 | LocationFactory.createLocation(W - 7, L - 4), 58 | new Location [] { },false); 59 | } 60 | 61 | private void buildCornerTiles(int W, int L) { 62 | Tile t = null; 63 | Location l = null; 64 | l = LocationFactory.createLocation(0,0); 65 | t = TileFactory.createTile(l); 66 | t.setFloorType(BARE); 67 | t.setClean(false); 68 | t.setWestOpen(false); 69 | t.setNorthOpen(false); 70 | t.setSouthOpen(true); 71 | t.setEastOpen(true); 72 | getTiles().put(l, t); 73 | l = LocationFactory.createLocation(W - 1,0); 74 | t = TileFactory.createTile(l); 75 | t.setFloorType(BARE); 76 | t.setClean(false); 77 | t.setWestOpen(true); 78 | t.setNorthOpen(false); 79 | t.setSouthOpen(true); 80 | t.setEastOpen(false); 81 | getTiles().put(l, t); 82 | l = LocationFactory.createLocation(0,L - 1); 83 | t = TileFactory.createTile(l); 84 | t.setFloorType(BARE); 85 | t.setClean(false); 86 | t.setWestOpen(false); 87 | t.setNorthOpen(true); 88 | t.setSouthOpen(false); 89 | t.setEastOpen(true); 90 | getTiles().put(l, t); 91 | l = LocationFactory.createLocation(W - 1,L - 1); 92 | t = TileFactory.createTile(l); 93 | t.setFloorType(BARE); 94 | t.setClean(false); 95 | t.setWestOpen(true); 96 | t.setNorthOpen(true); 97 | t.setSouthOpen(false); 98 | t.setEastOpen(false); 99 | getTiles().put(l, t); 100 | } 101 | 102 | private void buildNorthWall(int W, int L) { 103 | for(int i = 1; i <= W - 2; i++) { 104 | Tile t = null; 105 | Location l = null; 106 | l = LocationFactory.createLocation(i,0); 107 | t = TileFactory.createTile(l); 108 | t.setFloorType(BARE); 109 | t.setClean(false); 110 | t.setWestOpen(true); 111 | t.setNorthOpen(false); 112 | t.setSouthOpen(true); 113 | t.setEastOpen(true); 114 | getTiles().put(l, t); 115 | } 116 | } 117 | 118 | private void buildSouthWall(int W, int L) { 119 | for(int i = 1; i <= W - 2; i++) { 120 | Tile t = null; 121 | Location l = null; 122 | l = LocationFactory.createLocation(i,L - 1); 123 | t = TileFactory.createTile(l); 124 | t.setFloorType(BARE); 125 | t.setClean(false); 126 | t.setWestOpen(true); 127 | t.setNorthOpen(true); 128 | t.setSouthOpen(false); 129 | t.setEastOpen(true); 130 | getTiles().put(l, t); 131 | } 132 | } 133 | 134 | private void buildWestWall(int W, int L) { 135 | for(int j = 1; j <= L - 2; j++) { 136 | Tile t = null; 137 | Location l = null; 138 | l = LocationFactory.createLocation(0, j); 139 | t = TileFactory.createTile(l); 140 | t.setFloorType(BARE); 141 | t.setClean(false); 142 | t.setWestOpen(false); 143 | t.setNorthOpen(true); 144 | t.setSouthOpen(true); 145 | t.setEastOpen(true); 146 | getTiles().put(l, t); 147 | } 148 | } 149 | 150 | private void buildEastWall(int W, int L) { 151 | for(int j = 1; j <= L - 2; j++) { 152 | Tile t = null; 153 | Location l = null; 154 | l = LocationFactory.createLocation(W - 1, j); 155 | t = TileFactory.createTile(l); 156 | t.setFloorType(BARE); 157 | t.setClean(false); 158 | t.setWestOpen(true); 159 | t.setNorthOpen(true); 160 | t.setSouthOpen(true); 161 | t.setEastOpen(false); 162 | getTiles().put(l, t); 163 | } 164 | } 165 | 166 | private void buildInteriorTiles(int W, int L) { 167 | for(int i = 1; i <= W - 2; i++) { 168 | for(int j = 1; j <= L - 2; j++) { 169 | Tile t = null; 170 | Location l = null; 171 | l = LocationFactory.createLocation(i, j); 172 | t = TileFactory.createTile(l); 173 | t.setFloorType(BARE); 174 | t.setClean(false); 175 | t.setWestOpen(true); 176 | t.setNorthOpen(true); 177 | t.setSouthOpen(true); 178 | t.setEastOpen(true); 179 | getTiles().put(l, t); 180 | } 181 | } 182 | } 183 | 184 | private void buildWall(Location from, Location to, Location [] doors, boolean val) { 185 | //validate inputs - line has to be horizontal of vertical. 186 | int fromX = from.getX(); 187 | int fromY = from.getY(); 188 | 189 | int toX = to.getX(); 190 | int toY = to.getY(); 191 | 192 | // code for door 193 | for(int i = 0; i < doors.length;i++) { 194 | Location door = doors[i]; 195 | Location outDoor = LocationFactory.createLocation(door.getX()-1, door.getY()); 196 | 197 | Tile outDoorTile = TileFactory.createTile(outDoor); 198 | outDoorTile.setEastOpen(true); 199 | } 200 | 201 | if((fromX == toX && fromY == toY) || (fromX != toX && fromY != toY)) { 202 | throw new RobotException("Wall inputs not valid."); 203 | } 204 | //Horizontal Wall 205 | if(fromY == toY) { 206 | int x1 = Utilities.min(fromX, toX); 207 | int x2 = Utilities.max(fromX, toX); 208 | int y = fromY; 209 | if(y == 0) { 210 | throw new RobotException("Outer wall already constructed."); 211 | } 212 | Location l1 = null; 213 | Location l2 = null; 214 | Tile t1 = null; 215 | Tile t2 = null; 216 | for(int i = x1; i <= x2; i++) { 217 | //made a change to make the horizontal wall outer 218 | if(val) { 219 | l1 = LocationFactory.createLocation(i, y ); 220 | l2 = LocationFactory.createLocation(i, y + 1 ); 221 | } 222 | else { 223 | l1 = LocationFactory.createLocation(i, y -1 ); 224 | l2 = LocationFactory.createLocation(i, y ); 225 | } 226 | 227 | t1 = TileFactory.createTile(l1); 228 | t2 = TileFactory.createTile(l2); 229 | t1.setSouthOpen(false); 230 | t2.setNorthOpen(false); 231 | } 232 | } else if(fromX == toX) { 233 | 234 | int y1 = Utilities.min(fromY, toY); 235 | int y2 = Utilities.max(fromY, toY); 236 | int x = fromX; 237 | if(x == 0) { 238 | throw new RobotException("Outer wall already constructed."); 239 | } 240 | 241 | Location l1 = null; 242 | Location l2 = null; 243 | Tile t1 = null; 244 | Tile t2 = null; 245 | for(int i = y1; i <= y2; i++) { 246 | if (val) { 247 | l1 = LocationFactory.createLocation(x -1, i); 248 | l2 = LocationFactory.createLocation(x, i); 249 | } 250 | else { 251 | l1 = LocationFactory.createLocation(x, i); 252 | l2 = LocationFactory.createLocation(x+1, i); 253 | } 254 | 255 | t1 = TileFactory.createTile(l1); 256 | t2 = TileFactory.createTile(l2); 257 | t1.setEastOpen(false); 258 | t2.setWestOpen(false); 259 | } 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Clean Sweep Robotic Vacuum Cleaner & Sensor Simulator 2 | ## Travis-CI Build Status: [![Build Status](https://travis-ci.org/nardost/clean-sweep-robot.svg?branch=master)](https://travis-ci.org/nardost/clean-sweep-robot) 3 | ## Agile Software Development Term Project 4 | ### Team Members 5 | 1. John Diatte 6 | 2. Nardos Tessema 7 | 3. Ruiming Chen 8 | 4. Vinit Patel 9 | 10 | ## Demo of Smart Robot Vacuum 11 | 12 | ![](SmartRoboVacuum.gif) 13 | 14 | 15 | ## Clean Sweep Robot Configuration 16 | Configuration of Clean Sweep Robot is externalized to an xml file. The configuration options are listed below. 17 | ``` 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ``` 38 | 1. ```navigator``` - The navigator implementation deployed (Strategy pattern used) 39 | - omega (Current implementation. Do not use other implementations.) 40 | - alpha (Deprecated. Basic navigator used in earlier version of software) 41 | 2. ```scheduledWait``` - The time in seconds the robot waits for scheduled cleaning time after it gets turned on. 42 | 3. ```timeInTile``` - The total amount of time in seconds the robot stays in a tile while working and traveling. 43 | 4. ```timeToCharge``` - The amount of time in seconds needed to recharge the battery from battery level 0 to full. 44 | ## Clean Sweep Robotic Vacuum Cleaner & Sensor Simulator 45 | Your company has assembled a small team of developers and business people to work on the 46 | implementation of the Clean Sweep robotic vacuum cleaner control system. 47 | ### 1. Control System 48 | The control system integrates the Clean Sweep hardware components so that the vacuum can 49 | successfully navigate and clean a typical home. 50 | #### 1.1 Navigation 51 | The Clean Sweep roams around the home sweeping up any dirt that it finds. Therefore it must 52 | be able to discover the basic floor plan of a home based on the input from its sensors. The Clean 53 | Sweep has four directional sensors around its perimeter that detect when it is about to encounter 54 | an obstacle. The control system must interpret the output from these sensors and stop the vacuum 55 | from running into any obstacles. Upon encountering an obstacle, the Clean Sweep must 56 | determine a new direction that prevents it from running into another obstacle. If the Clean Sweep 57 | is unable to move, it should shut itself down and wait for the customer to move it to a new location. 58 | 59 | Under no circumstances should the Clean Sweep attempt to traverse stairs. A sensor on the bottom 60 | of the Clean Sweep can detect the edge of stairs or other declines. If this sensor detects anything, 61 | the control system should treat it as though it had encountered any other kind of obstacle. 62 | 63 | The Clean Sweep can move in four directions. If you envision the Clean Sweep as traversing a grid, 64 | and assume that it is located at position (x, y), then it can move to positions: (x+1, y), (x−1, y), 65 | (x, y +1), and (x, y −1). There is no need for the vacuum to change its orientation or turn around 66 | in order to navigate. 67 | 68 | #### 1.2 Dirt Detection and Cleaning 69 | As the Clean Sweep moves, a sensor determines if there is dirt to collect. If so, the control system 70 | should tell the vacuum to sweep up the dirt. The sensor is not able to tell how much dirt is present, 71 | only whether or not the current location is considered clean. Therefore multiple uses of the vacuum 72 | may be required to clean a particularly dirty area. 73 | 74 | The Clean Sweep is equipped with a surface sensor that allows it to detect whether or not the 75 | surface it is currently traversing is bare floor, low-pile carpet, or high-pile carpet. It should automatically 76 | shift its cleaning mode between the surfaces accordingly. 77 | 78 | Each use of the vacuum removes 1 unit of dirt. The Clean Sweep has a dirt-carrying capacity of 79 | 50 units. Once that capacity has been reached, it should return to its charging base and turn on 80 | its Empty Me indicator. The Clean Sweep doesn’t stop cleaning until: 81 | - It has visited every accessible location on the current floor. 82 | - Its dirt capacity has been filled. 83 | - It only has enough power remaining to return to its charging station. See Power Management 84 | for more details. 85 | 86 | Each time the Clean Sweep begins a cleaning cycle, it automatically assumes that each location is 87 | dirty. A cleaning cycle is defined as the start of the next cleaning after all reachable areas have 88 | been cleaned. So, if the Clean Sweep is partially done cleaning a floor and has returned to its base 89 | station to recharge, it will assume that the current cleaning cycle is not yet complete. 90 | 91 | ### 1.3 Power Management 92 | The Clean Sweep has a limited battery life of 250 units of charge. Each movement and vacuum 93 | operation requires units of charge depending on the surfaces being traversed: 94 | - Bare floor - 1 unit 95 | - Low-pile carpet - 2 units 96 | - High-pile carpet - 3 units 97 | 98 | The charge required for the Clean Sweep to move from location A to location B is the average of 99 | the required charge costs for the surfaces at the two locations. For example, if the Clean Sweep 100 | moves from a bare floor to low-pile carpet, then the charge required is 1.5 units. It costs the same 101 | amount of charge to clean the current location is it does to traverse that location. 102 | 103 | The Clean Sweep should always return to its charging station before it runs out of power. Homeowners 104 | that come home and discover their robotic vacuum out of power in the middle of a room 105 | tend to be dissatisfied customers. As it returns to its charging station, the Clean Sweep will not 106 | perform any cleaning. Upon returning to its charging station, the Clean Sweep will automatically 107 | re-charge to full capacity. Once re-charged, it will resume cleaning until it detects that it has 108 | visited every accessible location on the current floor. 109 | 110 | ### 1.4 Diagnostics and Troubleshooting 111 | Since the Clean Sweep is a new product, it is important that technical support personnel are able 112 | to troubleshoot its operation. 113 | #### 1.4.1 Activity Log 114 | On request, the Clean Sweep should be able to dump a log of its activity for the latest cleaning. 115 | This log should provide relevant information including: 116 | - Sensor checks. 117 | - Movement. 118 | - Cleaning. 119 | - Power remaining after each activity. 120 | - Recharging. 121 | ## 2 Sensor Simulator 122 | The Clean Sweep must maintain an internal map of the home. In order to write the control software, 123 | it is necessary to simulate the input of the Clean Sweep’s sensor array as it might respond within 124 | actual homes. The Sensor Simulator provides this capability and acts as a virtual sensor array. 125 | These simulations will act as test data against which you can test your control software. 126 | ### 2.1 Floor Plan Representation 127 | You will need to define a persistent floor plan layout. The layout file will need to represent different 128 | attributes of floor plan. 129 | #### 2.1.1 Layout Files 130 | To facilitate the testing of the Clean Sweep, the Sensor Simulator must be able to “play back” 131 | pre-defined floor plans. This means that the Sensor Simulator should be able to read a floor plan 132 | file and use that file to respond to requests from the Clean Sweep’s control software. 133 | 134 | The control software itself should have no knowledge of the floor plan layout. This file is only 135 | intended to allow the sensor simulator to emit controlled output based on interactions with the 136 | control software. The fact that the entire floor plan is available to the sensor simulator does not 137 | mean that the Clean Sweep has a priori knowledge of the home’s layout. The Clean Sweep control 138 | system must discover the floor plan in real time just as the physical device would. 139 | #### 2.1.2 Layout 140 | A floor plan is a grid comprised of a series of cells. Each cell is adjacent 141 | to at most 8 other cells. 142 | 143 | The Clean Sweep can move 90◦ in any direction. Consider the figure to 144 | the right. If the Clean Sweep is on cell E, it can move to cells B, D, F 145 | and H. Similarly, if it is on cell A, it can only move to cells B and D. 146 | #### 2.1.3 Surfaces 147 | The surface sensor detects the surface of the cell. This determines how 148 | much power is required to move into and clean that cell. The surface sensor can identify the 149 | following floor coverings: 150 | 1. Bare floor (e.g. wood, linoleum, etc.) 151 | 2. Low-pile carpet 152 | 3. High-pile carpet 153 | 154 | The dirt sensor can tell whether or not a given cell must be cleaned. The simulator must be able 155 | to represent how many units of dirt are at a given location. While the sensor itself cannot tell 156 | exactly how many units are present, this will allow the simulator to continuously register a cell as 157 | dirty until the appropriate number of vacuum operations have been performed. 158 | #### 2.1.4 Navigation 159 | The Clean Sweep has navigation sensors that allow it to move around the floor plan. The floor 160 | plan must identify where the Clean Sweep may navigate and when there are obstacles to its path. 161 | The floor plan must indicate whether there is a path from one cell to another or whether such 162 | navigation is blocked. Each path between two cells can contain the following: 163 | 1. Unknown. The Clean Sweep doesn’t yet know what’s in the specified direction. 164 | 2. Open. The sensor indicates that the path is open. 165 | 3. Obstacle. The sensor indicates that the path is obstructed. 166 | 4. Stairs. The sensor indicates that the path in that direction is blocked by stairs. 167 | #### 2.1.5 Charging Stations 168 | Each floor plan may contain one or more charging stations. These charging stations are used by 169 | the Clean Sweep to replenish its battery when it runs out of power. The Clean Sweep always knows 170 | about the charging station where it begins its cleaning process. It can discover others if it is within 171 | 2 cells of the charging station. The detection of the charging station happens whether or not the 172 | path to the station is actually clear. For example, it is possible that the Clean Sweep could detect 173 | a charging station in another room, but not be able to get to that station if the room’s door is closed. 174 | 175 | The control system must identify cells that contain charging stations. As the vacuum sweeps the 176 | floor, it will continually track which charging station is closest to its current position and move to 177 | that station when necessary. 178 | -------------------------------------------------------------------------------- /sensor/src/main/java/com/team4/sensor/JsonFloorBuilder.java: -------------------------------------------------------------------------------- 1 | package com.team4.sensor; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.stream.JsonReader; 5 | import com.team4.commons.*; 6 | 7 | import static com.team4.commons.Direction.*; 8 | import static com.team4.commons.FloorType.*; 9 | 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | import java.io.UnsupportedEncodingException; 13 | import java.util.HashMap; 14 | 15 | class JsonFloorBuilder implements FloorBuilder { 16 | 17 | private FloorPlan floorPlan; 18 | private HashMap tiles; 19 | private DirtGenerator dirtGenerator; 20 | 21 | JsonFloorBuilder(HashMap tiles) { 22 | if(tiles == null) { 23 | throw new RobotException("Null HashMap not allowed for tiles."); 24 | } 25 | setDirtGenerator(DirtGeneratorFactory.createDirtGenerator()); 26 | setTiles(tiles); 27 | setFloorPlan(deserialize()); 28 | } 29 | 30 | private HashMap getTiles() { 31 | return tiles; 32 | } 33 | 34 | void setTiles(HashMap tiles) { 35 | if(tiles == null) { 36 | throw new RobotException("Null tiles encountered."); 37 | } 38 | this.tiles = tiles; 39 | } 40 | 41 | DirtGenerator getDirtGenerator() { 42 | return dirtGenerator; 43 | } 44 | 45 | void setDirtGenerator(DirtGenerator dirtGenerator) { 46 | if(dirtGenerator == null) { 47 | throw new RobotException("Null dirt generator encountered."); 48 | } 49 | this.dirtGenerator = dirtGenerator; 50 | } 51 | 52 | private FloorPlan getFloorPlan() { 53 | return this.floorPlan; 54 | } 55 | 56 | private void setFloorPlan(FloorPlan floorPlan) { 57 | if(floorPlan == null) { 58 | throw new RobotException("Null floor plan encountered."); 59 | } 60 | this.floorPlan = floorPlan; 61 | Floor.WIDTH = floorPlan.getSouthEastCornerCoordinates().getX(); 62 | Floor.LENGTH = floorPlan.getSouthEastCornerCoordinates().getY(); 63 | } 64 | 65 | @Override 66 | public void buildFloor() { 67 | // build WIDTH x LENGTH tiles 68 | buildTiles(Floor.WIDTH, Floor.LENGTH); 69 | 70 | // build walls 71 | for(FloorPlan.Obstacle obstacle : getFloorPlan().getObstacles()) { 72 | buildWall(obstacle.getFrom(), obstacle.getTo()); 73 | //STAIRS are treated exactly like walls. 74 | } 75 | 76 | // open doors 77 | for(FloorPlan.Passage passage : getFloorPlan().getPassages()) { 78 | Location a = passage.getFrom(); 79 | Location b = passage.getTo(); 80 | openDoor(a, b); 81 | } 82 | 83 | // set charging stations 84 | for(Location location : getFloorPlan().getChargingStations()) { 85 | setChargingStation(location); 86 | } 87 | 88 | // load floor types 89 | for(FloorPlan.Area area : getFloorPlan().getAreas()) { 90 | setFloorType(area); 91 | } 92 | } 93 | 94 | private void buildTiles(int w, int l) { 95 | for(int i = 0; i < w; i++) { 96 | for(int j = 0; j < l; j++) { 97 | Tile tile; 98 | Location location; 99 | location = LocationFactory.createLocation(i, j); 100 | tile = TileFactory.createTile(location); 101 | tile.setFloorType(BARE); 102 | tile.setClean(false); 103 | tile.setDirtUnits(getDirtGenerator().generateDirt()); 104 | tile.setWestOpen(true); 105 | tile.setNorthOpen(true); 106 | tile.setSouthOpen(true); 107 | tile.setEastOpen(true); 108 | getTiles().put(location, tile); 109 | } 110 | } 111 | } 112 | 113 | private void buildWall(Location from, Location to) { 114 | // Only vertical or horizontal walls allowed. 115 | if(from.getX() != to.getX() && from.getY() != to.getY()) { 116 | throw new RobotException("Only vertical or horizontal walls allowed."); 117 | } 118 | // from and to have to be different. 119 | if(from.getX() == to.getX() && from.getY() == to.getY()) { 120 | throw new RobotException("The two end points of a wall cannot be the same."); 121 | } 122 | // Vertical wall 123 | if(from.getX() == to.getX()) { 124 | int x1 = from.getX(); 125 | int y1 = Utilities.min(from.getY(), to.getY()); 126 | int y2 = Utilities.max(from.getY(), to.getY()); 127 | if(x1 == 0 || x1 == Floor.WIDTH) { 128 | //outer walls 129 | Direction directionToBlock = (x1 == 0) ? WEST : EAST; 130 | int x = (x1 == 0) ? x1 : x1 - 1; 131 | for(int i = y1; i < y2; i++) { 132 | Location location = LocationFactory.createLocation(x, i); 133 | Tile tile = TileFactory.createTile(location); 134 | blockPassage(tile, directionToBlock); 135 | } 136 | } else { 137 | //inner walls 138 | int x = x1; 139 | for(int i = y1; i < y2; i++) { 140 | Location west = LocationFactory.createLocation(x - 1, i); 141 | Location east = LocationFactory.createLocation(x, i); 142 | Tile westTile = TileFactory.createTile(west); 143 | Tile eastTile = TileFactory.createTile(east); 144 | blockPassage(westTile, EAST); 145 | blockPassage(eastTile, WEST); 146 | } 147 | } 148 | return; 149 | } 150 | // Horizontal wall 151 | if(from.getY() == to.getY()) { 152 | int x1 = Utilities.min(from.getX(), to.getX()); 153 | int x2 = Utilities.max(from.getX(), to.getX()); 154 | int y1 = from.getY(); 155 | if(y1 == 0 || y1 == Floor.LENGTH) { 156 | // outer walls 157 | Direction directionToBlock = (y1 == 0) ? NORTH : SOUTH; 158 | int y = (y1 == 0) ? y1 : y1 - 1; 159 | for(int i = x1; i < x2; i++) { 160 | Location location = LocationFactory.createLocation(i, y); 161 | Tile tile = TileFactory.createTile(location); 162 | blockPassage(tile, directionToBlock); 163 | } 164 | } else { 165 | // inner walls 166 | int y = y1; 167 | for(int i = x1; i < x2; i++) { 168 | Location north = LocationFactory.createLocation(i, y - 1); 169 | Location south = LocationFactory.createLocation(i, y); 170 | Tile northTile = TileFactory.createTile(north); 171 | Tile southTile = TileFactory.createTile(south); 172 | blockPassage(northTile, SOUTH); 173 | blockPassage(southTile, NORTH); 174 | } 175 | } 176 | return; 177 | } 178 | } 179 | 180 | /** 181 | * 182 | * Open a passage between two tiles a & b 183 | * @param a 184 | * @param b 185 | */ 186 | private void openDoor(Location a, Location b) { 187 | //tiles must be different 188 | if(a.getX() == b.getX() && a.getY() == b.getY()) { 189 | throw new RobotException("Tiles must be different"); 190 | } 191 | //tiles must be on same row or on same column. 192 | if(a.getX() != b.getX() && a.getY() != b.getY()) { 193 | throw new RobotException("Tiles must be on the same row or on the same column."); 194 | } 195 | //tiles must be adjacent 196 | if( (a.getX() == b.getX() && Math.abs(a.getY() - b.getY()) > 1) || (a.getY() == b.getY() && Math.abs(a.getX() - b.getX()) > 1) ) { 197 | throw new RobotException("Tiles must be adjacent"); 198 | } 199 | //tiles on the same row 200 | if(a.getY() == b.getY()) { 201 | int x1 = Utilities.min(a.getX(), b.getX()); 202 | int x2 = Utilities.max(a.getX(), b.getX()); 203 | int y = a.getY(); 204 | Location west = LocationFactory.createLocation(x1, y); 205 | Location east = LocationFactory.createLocation(x2, y); 206 | Tile westTile = TileFactory.createTile(west); 207 | Tile eastTile = TileFactory.createTile(east); 208 | openPassage(westTile, EAST); 209 | openPassage(eastTile, WEST); 210 | } 211 | //tiles in same column 212 | if(a.getX() == b.getX()) { 213 | int x = a.getX(); 214 | int y1 = Utilities.min(a.getY(), b.getY()); 215 | int y2 = Utilities.max(a.getY(), b.getY()); 216 | Location north = LocationFactory.createLocation(x, y1); 217 | Location south = LocationFactory.createLocation(x, y2); 218 | Tile northTile = TileFactory.createTile(north); 219 | Tile southTile = TileFactory.createTile(south); 220 | openPassage(northTile, SOUTH); 221 | openPassage(southTile, NORTH); 222 | } 223 | } 224 | 225 | /** 226 | * Marks a location as a charging station 227 | * @param location 228 | */ 229 | private void setChargingStation(Location location) { 230 | if(location == null) { 231 | throw new RobotException("Null tile encountered."); 232 | } 233 | TileFactory.createTile(location).setChargingStation(true); 234 | } 235 | 236 | private void setFloorType(FloorPlan.Area area) { 237 | if(area == null) { 238 | throw new RobotException("Null area object encountered in JsonFloorBuilder.setFloorType() method."); 239 | } 240 | Location topLeft = area.getTopLeft(); 241 | Location bottomRight = area.getBottomRight(); 242 | FloorType floorType = toFloorType(area.getFloorType()); 243 | int x1 = topLeft.getX(); 244 | int y1 = topLeft.getY(); 245 | int x2 = bottomRight.getX(); 246 | int y2 = bottomRight.getY(); 247 | //validate input locations here!! 248 | for(int i = x1; i <= x2; i++) { 249 | for (int j = y1; j <= y2; j++) { 250 | TileFactory.createTile(LocationFactory.createLocation(i, j)).setFloorType(floorType); 251 | } 252 | } 253 | } 254 | 255 | private FloorType toFloorType(String floorTypeString) { 256 | switch (floorTypeString) { 257 | case "BARE": 258 | return BARE; 259 | case "LOW_PILE": 260 | return LOW_PILE; 261 | case "HIGH_PILE": 262 | return HIGH_PILE; 263 | default: 264 | throw new RobotException("Invalid floor type encountered."); 265 | } 266 | } 267 | 268 | /** 269 | * Helper method to close passage in tile toward specified direction 270 | * @param tile 271 | * @param direction 272 | */ 273 | private void blockPassage(Tile tile, Direction direction) { 274 | if(tile == null) { 275 | throw new RobotException("Null tile encountered in JsonFloorBuilder.blockPassage() method."); 276 | } 277 | if(direction == null) { 278 | throw new RobotException("Null direction encountered in JsonFloorBuilder.blockPassage() method."); 279 | } 280 | switch(direction) { 281 | case NORTH: 282 | tile.setNorthOpen(false); 283 | return; 284 | case SOUTH: 285 | tile.setSouthOpen(false); 286 | return; 287 | case EAST: 288 | tile.setEastOpen(false); 289 | return; 290 | case WEST: 291 | tile.setWestOpen(false); 292 | return; 293 | default: 294 | throw new RobotException("Impossible direction."); 295 | } 296 | } 297 | 298 | /** 299 | * Helper method to open passage in tile toward specified direction 300 | * @param tile 301 | * @param direction 302 | */ 303 | private void openPassage(Tile tile, Direction direction) { 304 | if(tile == null) { 305 | throw new RobotException("Null tile encountered in JsonFloorBuilder.openPassage() method."); 306 | } 307 | if(direction == null) { 308 | throw new RobotException("Null direction encountered in JsonFloorBuilder.openPassage() method."); 309 | } 310 | switch(direction) { 311 | case NORTH: 312 | tile.setNorthOpen(true); 313 | return; 314 | case SOUTH: 315 | tile.setSouthOpen(true); 316 | return; 317 | case EAST: 318 | tile.setEastOpen(true); 319 | return; 320 | case WEST: 321 | tile.setWestOpen(true); 322 | return; 323 | default: 324 | throw new RobotException("Impossible direction."); 325 | } 326 | } 327 | 328 | public FloorPlan deserialize() { 329 | String floorPlanJson = ConfigManager.getConfiguration("floorPlan"); 330 | InputStream inputStream = getClass().getClassLoader().getResourceAsStream(floorPlanJson); 331 | try { 332 | JsonReader reader = new JsonReader(new InputStreamReader(inputStream, "UTF-8")); 333 | Gson gson = new Gson(); 334 | return gson.fromJson(reader, FloorPlan.class); 335 | } catch (UnsupportedEncodingException uee) { 336 | throw new RobotException("Unsupported encoding exception encountered"); 337 | } 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /commons/src/main/java/com/team4/commons/Utilities.java: -------------------------------------------------------------------------------- 1 | package com.team4.commons; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | public class Utilities { 8 | /** 9 | * Extracts the first entry of a tuple of form (x, y) 10 | * The parentheses and the comma are important. Otherwise, an 11 | * Exception should be thrown. 12 | * 13 | * @param tuple 14 | * @return int 15 | */ 16 | public static int xFromTupleString(String tuple) { 17 | if(!isValidTuple(tuple)) { 18 | throw new RobotException(tuple + " is not a valid pair of coordinates."); 19 | } 20 | String parenthesisLeftRemoved = tuple.split("\\(")[1]; 21 | String parenthesesRemoved = parenthesisLeftRemoved.split("\\)")[0]; 22 | int x = Integer.parseInt(parenthesesRemoved.split(",")[0].replaceAll(" ", "")); 23 | return x; 24 | } 25 | public static int yFromTupleString(String tuple) { 26 | if(!isValidTuple(tuple)) { 27 | throw new RobotException(tuple + " is not a valid pair of coordinates."); 28 | } 29 | String parenthesisLeftRemoved = tuple.split("\\(")[1]; 30 | String parenthesesRemoved = parenthesisLeftRemoved.split("\\)")[0]; 31 | int y = Integer.parseInt(parenthesesRemoved.split(",")[1].replaceAll(" ", "")); 32 | return y; 33 | } 34 | 35 | /** 36 | * Verify tuple has correct tuple pattern (x, y) 37 | * @param tuple 38 | * @return boolean 39 | */ 40 | public static boolean isValidTuple(String tuple) { 41 | return true; 42 | } 43 | 44 | /** 45 | * Converts a pair of comma separated non-negative integers 46 | * into a string. 47 | * @param x 48 | * @param y 49 | * @return "(x,y)" 50 | */ 51 | public static String tupleToString(int x, int y) { 52 | if(x < 0 || y < 0) { 53 | throw new RobotException("Negative coordinates not allowed in tuple to string conversion."); 54 | } 55 | StringBuilder sb = new StringBuilder("("); 56 | sb.append(x); 57 | sb.append(","); 58 | sb.append(y); 59 | sb.append(")"); 60 | return sb.toString(); 61 | } 62 | 63 | public static String arrayToString(T [] elements) { 64 | StringBuilder sb = new StringBuilder("{ "); 65 | for(T element : elements) { 66 | sb.append(element.toString()); 67 | sb.append(", "); 68 | } 69 | if(sb.length() > 2) { 70 | sb.replace(sb.length() - 2, sb.length(), ""); 71 | } 72 | sb.append(" }"); 73 | return sb.toString(); 74 | } 75 | 76 | public static String padSpacesToFront(String str, int fixedSize) { 77 | if(str.length() >= fixedSize) { 78 | return str; 79 | } 80 | StringBuilder sb = new StringBuilder(); 81 | for(int i = 1; i <= fixedSize - str.length(); i++) { 82 | sb.append(" "); 83 | } 84 | sb.append(str); 85 | return sb.toString(); 86 | } 87 | 88 | public static int max(int a, int b) { 89 | return (a <= b) ? b : a; 90 | } 91 | 92 | public static int min(int a, int b) { 93 | return (a > b) ? b : a; 94 | } 95 | 96 | public static void printFormattedHeader(WorkingMode mode) { 97 | if(mode == WorkingMode.DEPLOYED) { 98 | System.out.println("-------------- --------- -------- --------- --------- ---------- -------------- ------------- ---------- ----------------------------\t------------------------"); 99 | System.out.println(" CLOCK DIRECTION LOCATION BEFORE AFTER FLOOR TYPE BATTERY BEFORE BATTERY AFTER DIRT LEVEL OPEN DIRECTIONS\tCHARGING STATIONS NEARBY"); 100 | System.out.println("-------------- --------- -------- --------- --------- ---------- -------------- ------------- ---------- ----------------------------\t------------------------"); 101 | } 102 | } 103 | 104 | public static void doTimeDelay(long millis) { 105 | try { 106 | Thread.sleep(millis); 107 | } catch (InterruptedException ie) { 108 | ie.printStackTrace(); 109 | } 110 | } 111 | public static void doLoopedTimeDelay(String msg, long millis, long zeroTime) { 112 | int numberOfLoops = (int) (millis / 1000L); 113 | try { 114 | for(int i = 1; i <= numberOfLoops; i++){ 115 | LogManager.print(msg, zeroTime); 116 | Thread.sleep(1000L); 117 | } 118 | } catch (InterruptedException ie) { 119 | ie.printStackTrace(); 120 | } 121 | } 122 | 123 | public static void printLogo() { 124 | System.out.println(); 125 | System.out.println(" +=======================================================+"); 126 | System.out.println(" | CLEAN SWEEP ROBOT |"); 127 | System.out.println(" +=======================================================+"); 128 | System.out.println(); 129 | } 130 | public static void printConfiguration() { 131 | String initLocation = ConfigManager.getConfiguration("initLocation"); 132 | String maxBatteryLevel = ConfigManager.getConfiguration("maxBatteryLevel"); 133 | String dirtCapacity = ConfigManager.getConfiguration("dirtCapacity"); 134 | String chargingStationDetectionRadius = ConfigManager.getConfiguration("chargingStationDetectionRadius"); 135 | String navigator = ConfigManager.getConfiguration("navigator"); 136 | String scheduledWait = ConfigManager.getConfiguration("scheduledWait"); 137 | String timeInTile = ConfigManager.getConfiguration("timeInTile"); 138 | String timeToCharge = ConfigManager.getConfiguration("timeToCharge"); 139 | String floorBuilderType = ConfigManager.getConfiguration("floorBuilderType"); 140 | String floorPlan = ConfigManager.getConfiguration("floorPlan"); 141 | String dirtGeneratorType = ConfigManager.getConfiguration("dirtGeneratorType"); 142 | String logsHome = ConfigManager.getConfiguration("logsHome"); 143 | String summaryLogFile = ConfigManager.getConfiguration("summaryLogFile"); 144 | String unityLogFile = ConfigManager.getConfiguration("unityLogFile"); 145 | 146 | System.out.println(); 147 | System.out.println(" +==================== CONFIGURATION ====================+"); 148 | System.out.println(" | Initial Location: " + initLocation + nSpaces(19 - initLocation.length()) + "|"); 149 | System.out.println(" | Maximum Battery Level: " + maxBatteryLevel + nSpaces(19 - maxBatteryLevel.length()) + "|"); 150 | System.out.println(" | Dirt Carrying Capacity: " + dirtCapacity + nSpaces(19 - dirtCapacity.length()) + "|"); 151 | System.out.println(" | Charging Station Detection Radius: " + chargingStationDetectionRadius + nSpaces(19 - chargingStationDetectionRadius.length()) + "|"); 152 | System.out.println(" | Navigator Version: " + navigator + nSpaces(19 - navigator.length()) + "|"); 153 | System.out.println(" | Scheduled Wait (milli seconds): " + scheduledWait + nSpaces(19 - scheduledWait.length()) + "|"); 154 | System.out.println(" | Time in Tile (milli seconds): " + timeInTile + nSpaces(19 - timeInTile.length()) + "|"); 155 | System.out.println(" | Time to Charge (milli seconds): " + timeToCharge + nSpaces(19 - timeToCharge.length()) + "|"); 156 | System.out.println(" | Floor Builder Type: " + floorBuilderType + nSpaces(19 - floorBuilderType.length()) + "|"); 157 | System.out.println(" | Floor Plan File: " + floorPlan + nSpaces(19 - floorPlan.length()) + "|"); 158 | System.out.println(" | Dirt Generation Algorithm: " + dirtGeneratorType + nSpaces(19 - dirtGeneratorType.length()) + "|"); 159 | System.out.println(" | Logs Home Environment Variable: " + logsHome + nSpaces(19 - logsHome.length()) + "|"); 160 | System.out.println(" | Summary Log File: " + summaryLogFile + nSpaces(19 - summaryLogFile.length()) + "|"); 161 | System.out.println(" | Unity Log File: " + unityLogFile + nSpaces(19 - unityLogFile.length()) + "|"); 162 | System.out.println(" +=======================================================+"); 163 | System.out.println(); 164 | } 165 | public static void printSummary(long zeroTime, long currentMillis, int numberOfRuns, String initLocation, String finalLocation, double percentDone) { 166 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss"); 167 | Date startTime = new Date(zeroTime); 168 | String start = formatter.format(startTime); 169 | Date finishTime = new Date(currentMillis); 170 | String end = formatter.format(finishTime); 171 | long duration = currentMillis - zeroTime; 172 | String percentage = percentDone + "%"; 173 | System.out.println(); 174 | System.out.println(" +======================= SUMMARY =======================+"); 175 | System.out.println(" | Initial Location: " + initLocation + nSpaces(22 - initLocation.length()) + "|"); 176 | System.out.println(" | Final Location: " + finalLocation + nSpaces(22 - finalLocation.length()) + "|"); 177 | System.out.println(" | Run Number: " + numberOfRuns + nSpaces(22 - (numberOfRuns + "").length()) + "|"); 178 | System.out.println(" | Start Date Time: " + start + nSpaces(22 - start.length()) + "|"); 179 | System.out.println(" | Finish Date Time: " + end + nSpaces(22 - end.length()) + "|"); 180 | System.out.println(" | Time Taken (milli seconds): " + duration + nSpaces(22 - Long.toString(duration).length()) + "|"); 181 | System.out.println(" | Percentage Done: " + percentage + nSpaces(22 - percentage.length()) + "|"); 182 | System.out.println(" +=======================================================+"); 183 | System.out.println(); 184 | } 185 | public static void printStateTransition(String state, long zeroTime) { 186 | StringBuilder sb = new StringBuilder(); 187 | for(int i = 0; i < 11 - state.length(); i++) { 188 | sb.append("-"); 189 | } 190 | System.out.println(); 191 | LogManager.print("[" + state + "] " + sb.toString() + "---- --------- --------- ---------- -------------- ------------- ---------- ----------------------------\t------------------------", zeroTime); 192 | System.out.println(); 193 | } 194 | public static void printDonePercentage(double percentage, long zeroTime) { 195 | System.out.println("Percentage of Done Tiles = " + percentage + "%"); 196 | } 197 | public static String formatElapsedTime(long milliTime, long zeroTime) { 198 | long elapsedTime = milliTime - zeroTime; 199 | long elapsedSeconds = TimeUnit.SECONDS.convert(elapsedTime, TimeUnit.MILLISECONDS); 200 | long s = elapsedSeconds % 60; 201 | long m = ((elapsedSeconds - s) % 3600) / 60; 202 | long h = (elapsedSeconds - (elapsedSeconds - s) % 3600) / 60; 203 | //show milliseconds... 204 | long decimals = (elapsedTime - 3600000 * h - 60000 * m - 1000 * s); 205 | return String.format("[%02d:%02d:%02d.%03d] ", h, m, s, decimals); 206 | //do not show milli seconds 207 | //return String.format("[%02d:%02d:%02d] ", h, m, s); 208 | } 209 | public static boolean isPrime(int n) { 210 | for(int i = 2; i <= Math.round(Math.sqrt(n)); i++) { 211 | if(n % i == 0) { 212 | return false; 213 | } 214 | } 215 | return true; 216 | } 217 | 218 | public static Location getNeighbor(Location location, Direction direction) { 219 | int x = location.getX(); 220 | int y = location.getY(); 221 | switch (direction) { 222 | case NORTH: return LocationFactory.createLocation(x, y - 1); 223 | case SOUTH: return LocationFactory.createLocation(x, y + 1); 224 | case EAST: return LocationFactory.createLocation(x + 1, y); 225 | case WEST: return LocationFactory.createLocation(x - 1, y); 226 | default: throw new RobotException("Impossible direction. Only N, S, E, W directions are available."); 227 | } 228 | } 229 | public static String nSpaces(int n) { 230 | if(n < 0) { 231 | throw new RobotException("Invalid number of white spaces: " + n); 232 | } 233 | StringBuilder sb = new StringBuilder(); 234 | for(int i = 0; i < n; i++) { 235 | sb.append(" "); 236 | } 237 | return sb.toString(); 238 | } 239 | } -------------------------------------------------------------------------------- /robot/src/main/java/com/team4/robot/RobotCleanSweep.java: -------------------------------------------------------------------------------- 1 | package com.team4.robot; 2 | 3 | import com.team4.commons.*; 4 | import com.team4.sensor.SensorSimulator; 5 | import com.team4.sensor.FloorDao; 6 | import static com.team4.commons.State.*; 7 | import static com.team4.commons.WorkingMode.*; 8 | 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.LinkedList; 12 | import java.util.List; 13 | import java.util.Stack; 14 | 15 | public class RobotCleanSweep implements Robot { 16 | 17 | private static long zeroTime; 18 | private static int numberOfRuns; 19 | //To suppress logging during the test phase. 20 | public static WorkingMode workingMode; 21 | 22 | private State state; 23 | private Location location; 24 | 25 | private Navigator navigator; 26 | private VacuumCleaner vacuumCleaner; 27 | private PowerManager powerManager; 28 | 29 | //Robot's memory of visited cells 30 | private HashMap visited = new HashMap<>(); 31 | //unvisited cells 32 | private Stack unvisited = new Stack<>(); 33 | //graph built as robot progresses 34 | private HashMap> graph = new HashMap<>(); 35 | 36 | private Location lastLocation; 37 | private LinkedList lastLocationList = new LinkedList<>(); 38 | 39 | //Charging stations locations 40 | private ArrayList chargingStations = new ArrayList<>(); 41 | private Location currentChargingStation = null; 42 | 43 | private static RobotCleanSweep robotCleanSweep = null; 44 | 45 | private RobotCleanSweep() { 46 | setZeroTime(System.currentTimeMillis()); 47 | numberOfRuns = 0; 48 | workingMode = DEPLOYED; 49 | setState(OFF); 50 | String locationTuple = ConfigManager.getConfiguration("initLocation"); 51 | int x = Utilities.xFromTupleString(locationTuple); 52 | int y = Utilities.yFromTupleString(locationTuple); 53 | setLocation(LocationFactory.createLocation(x, y)); 54 | setNavigator(NavigatorFactory.createNavigator()); 55 | setPowerManager(new PowerUnit()); 56 | setVacuumCleaner(new DirtManager()); 57 | } 58 | 59 | /** 60 | * Robot has to be a singleton since there should only be one robot. 61 | * 62 | * @return Robot 63 | */ 64 | public static RobotCleanSweep getInstance() { 65 | if(robotCleanSweep == null) { 66 | synchronized (RobotCleanSweep.class) { 67 | if(robotCleanSweep == null) { 68 | robotCleanSweep = new RobotCleanSweep(); 69 | } 70 | } 71 | } 72 | return robotCleanSweep; 73 | } 74 | 75 | @Override 76 | public void turnOn() { 77 | setState(STANDBY); 78 | waitForScheduledCleaningTime(); 79 | work(); 80 | } 81 | 82 | @Override 83 | public void turnOff() throws RobotException { 84 | setState(OFF); 85 | Utilities.printSummary( 86 | getZeroTime(), 87 | System.currentTimeMillis(), 88 | numberOfRuns, 89 | ConfigManager.getConfiguration("initLocation"), 90 | getLocation().toString(), 91 | SensorSimulator.getInstance().getDonePercentage()); 92 | } 93 | 94 | private void work() { 95 | /** //check if there is enough battery to make it to the nearest charging station 96 | * //if(there is enough battery to make it to the nearest charging station) { 97 | * // continue working... ? 98 | * //} else { 99 | * // set state low battery... etc.. 100 | * //} 101 | * while(mode is STANDBY && there are tiles not yet visited) { 102 | * if(dirt tank is full) { 103 | * save current tile as the last tile not done 104 | * change mode to FULL_TANK 105 | * wait until owner empties tank (infinite loop?) 106 | * if(tank emptied) { 107 | * change mode to STANDBY 108 | * } 109 | * } else { 110 | * if(there is undone tile saved) { 111 | * get saved undone tile and make it my next tile 112 | * } else { 113 | * decide where to go next <- the traversal algorithm picks which Tile is next. (navigator) 114 | * } 115 | * go to next tile (move) 116 | * ask sensor simulator information about current tile and save info 117 | * if(tile is not clean) { 118 | * check if there is enough battery to clean tile 119 | * if(there is not enough battery) { 120 | * save current tile as the last tile not done 121 | * change mode to CHARGING 122 | * go to nearest charging station 123 | * charge to 100% 124 | * go back to last tile 125 | * } else { 126 | * clean tile 127 | * } 128 | * } 129 | * report battery usage to power manager 130 | * save tile to done list 131 | * } 132 | * } 133 | */ 134 | if (getState() != OFF) { 135 | setState(WORKING); 136 | Utilities.printFormattedHeader(workingMode); 137 | while(getState() == WORKING) { 138 | Long timeInTile = Long.parseLong(ConfigManager.getConfiguration("timeInTile")); 139 | if(workingMode != DEPLOYED) { 140 | timeInTile = 0L; 141 | } 142 | Utilities.doTimeDelay(timeInTile); 143 | FloorDao floorDaoBefore = SensorSimulator.getInstance().getLocationInfo(getLocation()); 144 | if(floorDaoBefore.isClean) { 145 | SensorSimulator.getInstance().setTileDone(location); 146 | } 147 | if(floorDaoBefore.chargingStations.length == 1) { 148 | setCurrentChargingStation(floorDaoBefore.chargingStations[0]); 149 | } 150 | createLocations(floorDaoBefore.openPassages); 151 | buildGraph(getLocation(), floorDaoBefore.openPassages); 152 | double cost = floorDaoBefore.floorType.getCost(); 153 | 154 | Direction direction = getNavigator().traverseFloor(floorDaoBefore.openPassages); 155 | 156 | if(direction != null ) { 157 | double batteryLevelBefore = getPowerManager().getBatteryLevel(); 158 | while(!(SensorSimulator.getInstance().getLocationInfo(getLocation())).isClean && getState() != LOW_BATTERY) { 159 | getVacuumCleaner().clean(cost); 160 | } 161 | int dirtLevelAfter = getVacuumCleaner().getDirtLevel(); 162 | double batteryLevelAfter = getPowerManager().getBatteryLevel(); 163 | FloorDao floorDaoAfter = SensorSimulator.getInstance().getLocationInfo(getLocation()); 164 | for(Location chargingStation: floorDaoAfter.chargingStations) { 165 | if(!getChargingStations().contains(chargingStation)) { 166 | getChargingStations().add(chargingStation); 167 | } 168 | } 169 | if(workingMode == DEPLOYED) { 170 | logTileInfo(floorDaoBefore, floorDaoAfter, batteryLevelBefore, batteryLevelAfter, dirtLevelAfter, direction); 171 | } 172 | move(direction, cost); 173 | } 174 | else { 175 | //duplicate logic. find a way to refactor this. 176 | double batteryLevelBefore = getPowerManager().getBatteryLevel(); 177 | while(!(SensorSimulator.getInstance().getLocationInfo(getLocation())).isClean && getState()!= LOW_BATTERY) { 178 | getVacuumCleaner().clean(cost); 179 | } 180 | int dirtLevelAfter = getVacuumCleaner().getDirtLevel(); 181 | double batteryLevelAfter = getPowerManager().getBatteryLevel(); 182 | FloorDao floorDaoAfter = SensorSimulator.getInstance().getLocationInfo(getLocation()); 183 | if(workingMode == DEPLOYED) { 184 | logTileInfo(floorDaoBefore, floorDaoAfter, batteryLevelBefore, batteryLevelAfter, dirtLevelAfter, direction); 185 | } 186 | setState(STANDBY); 187 | } 188 | 189 | if(getState() == LOW_BATTERY) { 190 | FloorDao floorDao = SensorSimulator.getInstance().getLocationInfo(getLocation()); 191 | buildGraph(getLocation(), floorDao.openPassages); 192 | getLastLocationList().add(getLocation()); 193 | move(backToCharge(), floorDao.floorType.getCost()); 194 | } 195 | 196 | if(getState() == MOVING_BACK) { 197 | FloorDao floorDao = SensorSimulator.getInstance().getLocationInfo(getLocation()); 198 | getLastLocationList().add(getLocation()); 199 | buildGraph(getLocation(), floorDao.openPassages); 200 | move(movingBack(), floorDao.floorType.getCost()); 201 | } 202 | } 203 | if(SensorSimulator.getInstance().getDonePercentage() == 100.0) { 204 | homeToNearestChargingStation(); 205 | } 206 | } 207 | } 208 | 209 | private void move(Direction direction, double cost) { 210 | 211 | if(direction == null) { 212 | /** 213 | * Do not consume power without moving. 214 | */ 215 | if(getLocation() == getCurrentChargingStation() || getLocation() == getLastLocation()) { 216 | cost = 0.0; 217 | } 218 | getPowerManager().updateBatteryLevel(cost); 219 | return; 220 | } 221 | 222 | if(!SensorSimulator.getInstance().getLocationInfo(getLocation()).isClean) { 223 | putDirty(getLocation()); 224 | } 225 | 226 | setLocation(Utilities.getNeighbor(getLocation(), direction)); 227 | 228 | FloorDao floorDao = SensorSimulator.getInstance().getLocationInfo(getLocation()); 229 | cost += floorDao.floorType.getCost(); 230 | cost = cost / 2.0; 231 | buildGraph(getLocation(), floorDao.openPassages); 232 | getPowerManager().updateBatteryLevel(cost); 233 | if(getState() == LOW_BATTERY) { 234 | getLastLocationList().add(getLocation()); 235 | move(backToCharge(), floorDao.floorType.getCost()); 236 | } 237 | if(getState() == MOVING_BACK) { 238 | move(movingBack(), floorDao.floorType.getCost()); 239 | } 240 | } 241 | 242 | long getZeroTime() { 243 | return zeroTime; 244 | } 245 | 246 | static int getNumberOfRuns() { 247 | return numberOfRuns; 248 | } 249 | 250 | static void updateNumberOfRuns() { 251 | RobotCleanSweep.numberOfRuns += 1; 252 | } 253 | 254 | private static void setZeroTime(long zeroTime) { 255 | if(zeroTime < 0) { 256 | throw new RobotException("Negative time millis is not allowed."); 257 | } 258 | RobotCleanSweep.zeroTime = zeroTime; 259 | } 260 | 261 | State getState() { 262 | return state; 263 | } 264 | 265 | void setState(State state) { 266 | if(state == null) { 267 | throw new RobotException("Null state is not allowed."); 268 | } 269 | this.state = state; 270 | if(workingMode == DEPLOYED) { 271 | Utilities.printStateTransition(getState().toString(), getZeroTime()); 272 | } 273 | } 274 | Location getLastLocation() { 275 | return this.lastLocation; 276 | } 277 | void setLastLocation(Location l_location) { 278 | this.lastLocation = l_location; 279 | } 280 | 281 | Location getLocation() { 282 | return location; 283 | } 284 | 285 | void setLocation(Location location) { 286 | if(location == null) { 287 | throw new RobotException("Null location object not allowed."); 288 | } 289 | String key = Utilities.tupleToString(location.getX(), location.getY()); 290 | this.location = location; 291 | this.unvisited.remove(location); 292 | this.visited.put(key, location); 293 | } 294 | 295 | boolean visitedLocation(Location location) { 296 | int x = location.getX(); 297 | int y = location.getY(); 298 | String key = Utilities.tupleToString(x,y); 299 | return this.visited.containsKey(key); 300 | } 301 | 302 | boolean putUnvisited(Location location) { 303 | 304 | int x = location.getX(); 305 | int y = location.getY(); 306 | 307 | String key = Utilities.tupleToString(x,y); 308 | if(this.visited.containsKey(key)) { 309 | 310 | return true; 311 | } 312 | else { 313 | if(!this.unvisited.contains(location)) { 314 | 315 | this.unvisited.push(location); 316 | } 317 | return false; 318 | } 319 | } 320 | 321 | void putDirty(Location location) { 322 | this.unvisited.add(location); 323 | } 324 | 325 | boolean visitedAll() { 326 | return this.unvisited.isEmpty(); 327 | } 328 | Navigator getNavigator() { 329 | return navigator; 330 | } 331 | Location lastUnvisited() { 332 | return this.unvisited.peek(); 333 | } 334 | 335 | private void setNavigator(Navigator navigator) { 336 | if(navigator == null) { 337 | throw new RobotException("Null navigator object not allowed."); 338 | } 339 | this.navigator = navigator; 340 | } 341 | 342 | PowerManager getPowerManager() { 343 | return powerManager; 344 | } 345 | 346 | private void setPowerManager(PowerManager powerManager) { 347 | if(powerManager == null) { 348 | throw new RobotException("Null power manager is not allowed."); 349 | } 350 | this.powerManager = powerManager; 351 | } 352 | 353 | VacuumCleaner getVacuumCleaner() { 354 | return vacuumCleaner; 355 | } 356 | 357 | private void setVacuumCleaner(VacuumCleaner vacuumCleaner) { 358 | if (vacuumCleaner == null) { 359 | throw new RobotException("Null vacuum cleaner is not allowed."); 360 | } 361 | this.vacuumCleaner = vacuumCleaner; 362 | } 363 | 364 | HashMap> getGraph() { 365 | return graph; 366 | } 367 | 368 | private void createLocations(Direction [] directions) { 369 | for(int i = 0; i < directions.length; i++) { 370 | putUnvisited(Utilities.getNeighbor(getLocation(), directions[i])); 371 | } 372 | } 373 | 374 | /** 375 | * The robot builds a graph of locations it has been to 376 | * and locations it knows it can go to. 377 | * @param location 378 | * @param directions 379 | */ 380 | private void buildGraph(Location location, Direction [] directions) { 381 | List neighbors = new ArrayList<>(); 382 | for(Direction direction : directions) { 383 | neighbors.add(Utilities.getNeighbor(location, direction)); 384 | } 385 | getGraph().put(location, neighbors); 386 | } 387 | 388 | Direction backToCharge() { 389 | if(getLocation().equals(getCurrentChargingStation())){ 390 | String dirtLevel = Integer.toString(getVacuumCleaner().getDirtLevel()); 391 | String batteryLevel = Double.toString(getPowerManager().getBatteryLevel()); 392 | 393 | for(int i = 0; i <= 4 ; i++) { 394 | LogManager.logForUnity(getLocation(), "CHARGING", batteryLevel , dirtLevel); 395 | } 396 | getPowerManager().recharge(); 397 | batteryLevel = Double.toString(getPowerManager().getBatteryLevel()); 398 | LogManager.logForUnity(getLocation(), "CHARGED",batteryLevel , dirtLevel); 399 | getLocBeforeCharge(); 400 | setState(MOVING_BACK); 401 | return null; 402 | } 403 | AStar aStar = new AStar(getGraph(), getLocation(), getCurrentChargingStation() ,2); 404 | Direction direction = null; 405 | if(aStar.search() != null && !aStar.search().empty()) { 406 | direction = aStar.search().pop(); 407 | } 408 | return direction; 409 | } 410 | 411 | Location getLocBeforeCharge() { 412 | setLastLocation(getLastLocationList().removeFirst()); 413 | getLastLocationList().clear(); 414 | return getLastLocation(); 415 | } 416 | 417 | Direction movingBack() { 418 | if(getLocation().equals( getLastLocation())) { 419 | String dirtLevel = Integer.toString(getVacuumCleaner().getDirtLevel()); 420 | String batteryLevel = Double.toString(getPowerManager().getBatteryLevel()); 421 | LogManager.logForUnity(getLocation(), "RESUME",batteryLevel , dirtLevel); 422 | setState(WORKING); 423 | return null; 424 | } 425 | AStar aStar = new AStar(getGraph(), getLocation(), getLastLocation() ,2); 426 | String dirtLevel = Integer.toString(getVacuumCleaner().getDirtLevel()); 427 | String batteryLevel = Double.toString(getPowerManager().getBatteryLevel()); 428 | LogManager.logForUnity(getLocation(), "GO_LAST",batteryLevel , dirtLevel); 429 | FloorDao floorDao = SensorSimulator.getInstance().getLocationInfo(RobotCleanSweep.getInstance().getLocation()); 430 | if(workingMode == DEPLOYED) { 431 | Long timeInTile = Long.parseLong(ConfigManager.getConfiguration("timeInTile")); 432 | Utilities.doTimeDelay(timeInTile); 433 | RobotCleanSweep.getInstance().logTileInfo( 434 | floorDao, 435 | floorDao, 436 | getPowerManager().getBatteryLevel(), 437 | getVacuumCleaner().getDirtLevel(), 438 | getVacuumCleaner().getDirtLevel(), 439 | null); 440 | } 441 | return aStar.search().pop(); 442 | } 443 | 444 | /** 445 | * Homing feature. Return to the nearest charging station after completing work. 446 | */ 447 | private void homeToNearestChargingStation() { 448 | while(!(getLocation().equals(getCurrentChargingStation()))) { 449 | Long timeInTile = Long.parseLong(ConfigManager.getConfiguration("timeInTile")); 450 | if(workingMode != DEPLOYED) { 451 | timeInTile = 0L; 452 | } 453 | Utilities.doTimeDelay(timeInTile); 454 | FloorDao floorDaoBefore = SensorSimulator.getInstance().getLocationInfo(getLocation()); 455 | double batteryLevelBefore = getPowerManager().getBatteryLevel(); 456 | move(backToCharge(), floorDaoBefore.floorType.getCost()); 457 | FloorDao floorDaoAfter = SensorSimulator.getInstance().getLocationInfo(getLocation()); 458 | int dirtLevelAfter = getVacuumCleaner().getDirtLevel(); 459 | double batteryLevelAfter = getPowerManager().getBatteryLevel(); 460 | if(workingMode == DEPLOYED) { 461 | logTileInfo(floorDaoBefore, floorDaoAfter, batteryLevelBefore, batteryLevelAfter, dirtLevelAfter, null); 462 | } 463 | } 464 | LogManager.print("Cleaning Completed", getZeroTime()); 465 | LogManager.logCurrentRun(getZeroTime(), System.currentTimeMillis(), ConfigManager.getConfiguration("initLocation"),getLocation().toString(), SensorSimulator.getInstance().getDonePercentage()); 466 | String dirtLevel = Integer.toString(getVacuumCleaner().getDirtLevel()); 467 | String batteryLevel = Double.toString(getPowerManager().getBatteryLevel()); 468 | 469 | for(int i = 0; i <= 4 ; i++) { 470 | LogManager.logForUnity(getLocation(), "CHARGING", batteryLevel , dirtLevel); 471 | } 472 | 473 | getPowerManager().recharge(); 474 | batteryLevel = Double.toString(getPowerManager().getBatteryLevel()); 475 | if(workingMode == DEPLOYED) { 476 | LogManager.print("Battery recharged. Battery level: " + getPowerManager().getBatteryLevel(), getZeroTime()); 477 | } 478 | LogManager.logForUnity(getLocation(), "CHARGED", batteryLevel, dirtLevel); 479 | updateNumberOfRuns(); 480 | } 481 | 482 | private void waitForScheduledCleaningTime() { 483 | long scheduledWait = Long.parseLong(ConfigManager.getConfiguration("scheduledWait")); 484 | LogManager.print("Waiting for cleaning schedule at " + getLocation(), getZeroTime()); 485 | Utilities.doTimeDelay(scheduledWait); 486 | } 487 | 488 | void logTileInfo(FloorDao floorDaoBefore, FloorDao floorDaoAfter, double batteryLevelBefore, double batteryLevelAfter, int dirtLevelAfter, Direction direction) { 489 | StringBuilder simple = new StringBuilder(); 490 | simple.append(Utilities.padSpacesToFront("pos" + "(" + getLocation().getX() + ", " + getLocation().getY() + ")", 8)); 491 | simple.append(" "); 492 | simple.append(Utilities.padSpacesToFront((floorDaoBefore.isClean) ? "act[ALREADY_CLEAN] " : "", 9)); 493 | simple.append(" "); 494 | simple.append(Utilities.padSpacesToFront((floorDaoAfter.isClean && floorDaoBefore.isClean) ? "act[CLEAN]" : "", 9)); 495 | LogManager.logForUnity(getLocation(), floorDaoBefore.isClean, floorDaoAfter.isClean,Double.toString(batteryLevelAfter), Integer.toString(dirtLevelAfter)); 496 | 497 | StringBuilder sb = new StringBuilder(); 498 | sb.append(Utilities.padSpacesToFront((direction == null) ? "" : direction.toString(), 9)); 499 | sb.append(" "); 500 | sb.append(Utilities.padSpacesToFront("(" + getLocation().getX() + ", " + getLocation().getY() + ")", 8)); 501 | sb.append(" "); 502 | sb.append(Utilities.padSpacesToFront((floorDaoBefore.isClean) ? "CLEAN" : "NOT CLEAN", 9)); 503 | sb.append(" "); 504 | sb.append(Utilities.padSpacesToFront((floorDaoAfter.isClean) ? "CLEAN" : "NOT CLEAN", 9)); 505 | sb.append(" "); 506 | sb.append(Utilities.padSpacesToFront(floorDaoBefore.floorType.toString(), 10)); 507 | sb.append(" "); 508 | sb.append(Utilities.padSpacesToFront(Double.toString(batteryLevelBefore), 14)); 509 | sb.append(" "); 510 | sb.append(Utilities.padSpacesToFront(Double.toString(batteryLevelAfter), 13)); 511 | sb.append(" "); 512 | sb.append(Utilities.padSpacesToFront(Integer.toString(dirtLevelAfter), 10)); 513 | sb.append(" "); 514 | sb.append(Utilities.padSpacesToFront(Utilities.arrayToString(floorDaoBefore.openPassages), 28)); 515 | sb.append("\t"); 516 | sb.append(Utilities.arrayToString(floorDaoBefore.chargingStations)); 517 | LogManager.print(sb.toString(), getZeroTime()); 518 | } 519 | 520 | /** 521 | * For testing purposes. 522 | */ 523 | void dryRun() { 524 | workingMode = TESTING; 525 | work(); 526 | } 527 | 528 | public LinkedList getLastLocationList() { 529 | return lastLocationList; 530 | } 531 | 532 | public ArrayList getChargingStations() { 533 | return chargingStations; 534 | } 535 | 536 | public Location getCurrentChargingStation() { 537 | return currentChargingStation; 538 | } 539 | 540 | public void setCurrentChargingStation(Location currentChargingStation) { 541 | this.currentChargingStation = currentChargingStation; 542 | } 543 | } 544 | --------------------------------------------------------------------------------