Package maze.ai

Source Code of maze.ai.Floodfill

package maze.ai;

import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Vector;

import maze.model.Direction;
import maze.model.MazeCell;
import maze.model.MazeModel;
import maze.model.RobotModel;

/**
* A maze solving algorithm that does a best path search after each step.
*/
public class Floodfill extends RobotBase
{
   private int[][] distance;
   private boolean[][] explored;
   private ArrayList<RobotStep> moveQueue = new ArrayList<RobotStep>();
   private boolean turbo = false;
   private static final int USELESS = 1024;
   private MazeModel maze = new MazeModel();
   private boolean goal;
   private static final boolean TO_CENTER = true;
   private static final boolean TO_START = false;
   private boolean speedRunCapable;

   /**
    * Returns a string describing this class
    */
   @Override
   public String toString()
   {
      return "Flood Fill";
   }

   /**
    * Initializes the algorithm for use. This should be called before any run is
    * started.
    */
   public void initialize()
   {
      super.initialize();
      moveQueue.clear();
      maze.setSize(robotLocation.getMazeSize());
      maze.clearMaze();
      Dimension size = maze.getSize();
      if (distance == null)
      {
         distance = new int[size.width][size.height];
         explored = new boolean[size.width][size.height];
      }
      for (int i = 0; i < size.getWidth(); i++)
      {
         for (int j = 0; j < size.getHeight(); j++)
         {
            distance[i][j] = USELESS;
            explored[i][j] = false;
         }
      }
      goal = TO_CENTER;
      speedRunCapable = false;
   }

   /**
    * Returns the next step that the robot should take. This is called by the
    * controller.
    */
   @Override
   public RobotStep nextStep()
   {
      RobotStep next;
      Direction nextDirection;
      Direction currentDirection = robotLocation.getDirection();
      if (moveQueue.isEmpty())
      {
         if (getExplored() == false)
         {
            checkWalls();
            setExplored();
         }
         if (atGoal() == true)
         {
            if ( (goal == TO_CENTER) && (speedRunCapable == false))
            {
               speedRunCapable = true;
               blockOutCenter();
            }
            goal = !goal;
            floodfill();
         }
         nextDirection = getBestDirection();
         turbo = getNeighborExplored(nextDirection);
         if (nextDirection == currentDirection)
         {
            next = RobotStep.MoveForward;
         }
         else if (nextDirection == currentDirection.getLeft())
         {
            next = RobotStep.RotateLeft;
            moveQueue.add(RobotStep.MoveForward);
         }
         else if (nextDirection == currentDirection.getRight())
         {
            next = RobotStep.RotateRight;
            moveQueue.add(RobotStep.MoveForward);
         }
         else
         // if(nextDirection == currentDirection.getOpposite())
         {
            next = RobotStep.MoveBackward;
         }
      }
      else
      {
         next = moveQueue.get(0);
         moveQueue.remove(0);
      }
      return next;
   }

   /**
    * Returns the explored flag of the current location's neighbor in the given
    * direction.
    */
   private boolean getNeighborExplored(Direction direction)
   {
      MazeCell neighbor;
      MazeCell here = robotLocation.getCurrentLocation();
      Dimension size = maze.getSize();
      if ( (direction == Direction.North) && (here.getY() != 1))
      {
         neighbor = MazeCell.valueOf(here.getX(), here.getY() - 1);
      }
      else if ( (direction == Direction.South) && (here.getY() != size.getHeight()))
      {
         neighbor = MazeCell.valueOf(here.getX(), here.getY() + 1);
      }
      else if ( (direction == Direction.East) && (here.getX() != size.getWidth()))
      {
         neighbor = MazeCell.valueOf(here.getX() + 1, here.getY());
      }
      else if ( (direction == Direction.West) && (here.getX() != 1))
      {
         neighbor = MazeCell.valueOf(here.getX() - 1, here.getY());
      }
      else
      {
         return false;
      }

      return getExplored(neighbor);
   }

   /**
    * Returns the explored flag of the given location's neighbor.
    */
   private boolean getNeighborExplored(MazeCell here, Direction direction)
   {
      MazeCell neighbor;
      Dimension size = maze.getSize();
      if ( (direction == Direction.North) && (here.getY() != 1))
      {
         neighbor = MazeCell.valueOf(here.getX(), here.getY() - 1);
      }
      else if ( (direction == Direction.South) && (here.getY() != size.getHeight()))
      {
         neighbor = MazeCell.valueOf(here.getX(), here.getY() + 1);
      }
      else if ( (direction == Direction.East) && (here.getX() != size.getWidth()))
      {
         neighbor = MazeCell.valueOf(here.getX() + 1, here.getY());
      }
      else if ( (direction == Direction.West) && (here.getX() != 1))
      {
         neighbor = MazeCell.valueOf(here.getX() - 1, here.getY());
      }
      else
      {
         return false;
      }

      return getExplored(neighbor);
   }

   /**
    * Returns the explored flag of the given location.
    */
   private boolean getExplored(MazeCell cell)
   {
      return explored[cell.getX() - 1][cell.getY() - 1];
   }

   /**
    * Returns the explored flag of the current location.
    */
   private boolean getExplored()
   {
      return getExplored(robotLocation.getCurrentLocation());
   }

   /**
    * Sets the explored flag of the current location to true.
    */
   private void setExplored()
   {
      explored[robotLocation.getCurrentLocation().getX() - 1][robotLocation.getCurrentLocation().getY() - 1] = true;
   }

   /**
    * Returns the best direction to go, according to Floodfill, from the current
    * MazeCell. This algorithm biases in the following order: Straight, North,
    * East, West, South
    */
   private Direction getBestDirection()
   {
      MazeCell here = robotLocation.getCurrentLocation();
      int bestDistance = getDistance(here);
      Direction bestDirection = null;

      if ( (bestDistance > getNeighborDistance(here, robotLocation.getDirection())) &&
          (robotLocation.isWallFront() == false))
      {
         bestDirection = robotLocation.getDirection();
         bestDistance = getNeighborDistance(here, bestDirection);
      }

      if ( (bestDistance > getNeighborDistance(here, Direction.North)) &&
          (maze.getWall(here, Direction.North).isSet() == false))
      {
         bestDirection = Direction.North;
         bestDistance = getNeighborDistance(here, bestDirection);
      }
      if ( (bestDistance > getNeighborDistance(here, Direction.East)) &&
          (maze.getWall(here, Direction.East).isSet() == false))
      {
         bestDirection = Direction.East;
         bestDistance = getNeighborDistance(here, bestDirection);
      }
      if ( (bestDistance > getNeighborDistance(here, Direction.West)) &&
          (maze.getWall(here, Direction.West).isSet() == false))
      {
         bestDirection = Direction.West;
         bestDistance = getNeighborDistance(here, bestDirection);
      }
      if ( (bestDistance > getNeighborDistance(here, Direction.South)) &&
          (maze.getWall(here, Direction.South).isSet() == false))
      {
         bestDirection = Direction.South;
         bestDistance = getNeighborDistance(here, bestDirection);
      }

      if (bestDirection == null)
      {
         floodfill();
         return getBestDirection();
      }
      else
      {
         return bestDirection;
      }
   }

   /**
    * Returns the distance of the MazeCell adjacent to the passed MazeCell and
    * is adjacent in the specified direction.
    */
   private int getNeighborDistance(MazeCell here, Direction direction)
   {
      MazeCell neighbor;
      Dimension size = maze.getSize();
      if ( (direction == Direction.North) && (here.getY() != 1))
      {
         neighbor = MazeCell.valueOf(here.getX(), here.getY() - 1);
      }
      else if ( (direction == Direction.South) && (here.getY() != size.getHeight()))
      {
         neighbor = MazeCell.valueOf(here.getX(), here.getY() + 1);
      }
      else if ( (direction == Direction.East) && (here.getX() != size.getWidth()))
      {
         neighbor = MazeCell.valueOf(here.getX() + 1, here.getY());
      }
      else if ( (direction == Direction.West) && (here.getX() != 1))
      {
         neighbor = MazeCell.valueOf(here.getX() - 1, here.getY());
      }
      else
      {
         return USELESS;
      }

      return getDistance(neighbor);
   }

   /**
    * Returns the distance of the desired MazeCell
    */
   private int getDistance(MazeCell here)
   {
      return distance[here.getX() - 1][here.getY() - 1];
   }

   /**
    * Sets the distance of the desired MazeCell to the value provided
    */
   private void setDistance(MazeCell here, int value)
   {
      distance[here.getX() - 1][here.getY() - 1] = value;
   }

   private void floodfill()
   {
      Dimension size = maze.getSize();
      Vector<MazeCell> queue = new Vector<MazeCell>();
      MazeCell cell;
      int currentDistance;
      boolean speedy;

      for (int i = 1; i <= size.width; i++)
      {
         for (int j = 1; j <= size.height; j++)
         {
            setDistance(MazeCell.valueOf(i, j), USELESS);
         }
      }

      if (goal == TO_START)
      {
         cell = MazeCell.valueOf(1, size.height);
         setDistance(cell, 0);
         queue.add(cell);
         speedy = false;
      }
      else
      {
         int targetX = size.width / 2;
         int targetY = size.height / 2;
         cell = MazeCell.valueOf(targetX, targetY);
         setDistance(cell, 0);
         queue.add(cell);
         cell = MazeCell.valueOf(targetX + 1, targetY);
         setDistance(cell, 0);
         queue.add(cell);
         cell = MazeCell.valueOf(targetX, targetY + 1);
         setDistance(cell, 0);
         queue.add(cell);
         cell = MazeCell.valueOf(targetX + 1, targetY + 1);
         setDistance(cell, 0);
         queue.add(cell);
         if ( (speedRun == true) && (speedRunCapable == true))
         {
            speedy = true;
         }
         else
         {
            speedy = false;
         }
      }

      while (queue.isEmpty() == false)
      {
         cell = queue.get(0);
         queue.remove(0);
         currentDistance = getDistance(cell);

         //Check to see if accessible
         if (maze.getWall(cell, Direction.North).isSet() == false)
         { //Check to see if it should be added to queue
            if ( ( (currentDistance + 1) < getNeighborDistance(cell, Direction.North)) &&
                ( (speedy == false) || (getNeighborExplored(cell, Direction.North) == true)))
            {
               queue.add(cell.plusY(-1));
               setDistance(cell.plusY(-1), currentDistance + 1);
            }
         }

         //Check to see if accessible
         if (maze.getWall(cell, Direction.South).isSet() == false)
         { //Check to see if it should be added to queue
            if ( ( (currentDistance + 1) < getNeighborDistance(cell, Direction.South)) &&
                ( (speedy == false) || (getNeighborExplored(cell, Direction.South))))
            {
               queue.add(cell.plusY(1));
               setDistance(cell.plusY(1), currentDistance + 1);
            }
         }

         //Check to see if accessible
         if (maze.getWall(cell, Direction.West).isSet() == false)
         { //Check to see if it should be added to queue
            if ( ( (currentDistance + 1) < getNeighborDistance(cell, Direction.West)) &&
                ( (speedy == false) || (getNeighborExplored(cell, Direction.West))))
            {
               queue.add(cell.plusX(-1));
               setDistance(cell.plusX(-1), currentDistance + 1);
            }
         }

         //Check to see if accessible
         if (maze.getWall(cell, Direction.East).isSet() == false)
         { //Check to see if it should be added to queue
            if ( ( (currentDistance + 1) < getNeighborDistance(cell, Direction.East)) &&
                ( (speedy == false) || (getNeighborExplored(cell, Direction.East))))
            {
               queue.add(cell.plusX(1));
               setDistance(cell.plusX(1), currentDistance + 1);
            }
         }

      }

      MazeCell here = robotLocation.getCurrentLocation();
      if (getDistance(here) == USELESS)
      {
         //System.out.println("Purging Knowledge");
         maze.clearMaze();
         speedRunCapable = false;
         for (int i = 0; i < size.width; i++)
         {
            for (int j = 0; j < size.height; j++)
            {
               explored[i][j] = false;
            }
         }
         explored[here.getX() - 1][here.getY() - 1] = true;
         checkWalls();
         floodfill();
      }
   }

   /**
    * This helper function should only be called when first entering the center
    * goal. Sets the maze knowledge of the algorithm to think that there are no
    * other ways out of the center because according the American rules there is
    * only one entrance/exit associated with the goal.
    */
   private void blockOutCenter()
   {
      Dimension size = maze.getSize();
      MazeCell cell1 = MazeCell.valueOf(size.width / 2, size.height / 2);
      MazeCell cell2 = MazeCell.valueOf(size.width / 2 + 1, size.height / 2);
      MazeCell cell3 = MazeCell.valueOf(size.width / 2, size.height / 2 + 1);
      MazeCell cell4 = MazeCell.valueOf(size.width / 2 + 1, size.height / 2 + 1);
      MazeCell current = robotLocation.getCurrentLocation();

      if (cell1.equals(current) == false)
      {
         maze.setWall(cell1.getX(), cell1.getY(), Direction.North.getIndex());
         maze.setWall(cell1.getX(), cell1.getY(), Direction.West.getIndex());
      }
      if (cell2.equals(current) == false)
      {
         maze.setWall(cell2.getX(), cell2.getY(), Direction.North.getIndex());
         maze.setWall(cell2.getX(), cell2.getY(), Direction.East.getIndex());
      }
      if (cell3.equals(current) == false)
      {
         maze.setWall(cell3.getX(), cell3.getY(), Direction.South.getIndex());
         maze.setWall(cell3.getX(), cell3.getY(), Direction.West.getIndex());
      }
      if (cell4.equals(current) == false)
      {
         maze.setWall(cell4.getX(), cell4.getY(), Direction.South.getIndex());
         maze.setWall(cell4.getX(), cell4.getY(), Direction.East.getIndex());
      }
   }

   /**
    * Returns true if the current location of the robot is its current
    * destination.
    */
   private boolean atGoal()
   {
      MazeCell cell = robotLocation.getCurrentLocation();
      Dimension size = maze.getSize();
      if ( (goal == TO_START) && (cell.getX() == 1) && (cell.getY() == size.height))
      {
         return true;
      }
      if ( (goal == TO_CENTER) &&
          ( (cell.getY() == size.height / 2) || (cell.getY() == (size.height / 2 + 1))) &&
          ( (cell.getX() == size.width / 2) || (cell.getX() == (size.width / 2 + 1))))
      {
         return true;
      }
      return false;
   }

   /**
    * Updates the algorithm's memory of the maze based upon the walls in the
    * current cell.
    */
   private void checkWalls()
   {
      MazeCell cell = robotLocation.getCurrentLocation();
      Direction direction = robotLocation.getDirection();
      if (robotLocation.isWallFront())
      {
         maze.setWall(cell.getX(), cell.getY(), direction.getIndex());
      }
      if (robotLocation.isWallLeft())
      {
         maze.setWall(cell.getX(), cell.getY(), direction.getLeft().getIndex());
      }
      if (robotLocation.isWallRight())
      {
         maze.setWall(cell.getX(), cell.getY(), direction.getRight().getIndex());
      }
      if (robotLocation.isWallBack())
      {
         maze.setWall(cell.getX(), cell.getY(), direction.getOpposite().getIndex());
      }
   }

   /**
    * Returns the "turbo" state of the algorithm. Will be true when areas are
    * being traversed after they have been explored before.
    */
   public boolean isInTurboMode()
   {
      return turbo;
   }

   /**
    * Returns the instance of how the algorithm understands the maze. For
    * Floodfill this is an integer for each cell representing the expected
    * distance from the center.
    */
   public int[][] getUnderstandingInt()
   {
      return distance;
   }

   @Override
   /**
    * Sets the instance of the robot model to use.
    */
   public void setRobotLocation(RobotModel model)
   {
      this.robotLocation = model;
      distance = null;
   }
}
TOP

Related Classes of maze.ai.Floodfill

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.