using System; using System.Collections.Generic; using System.Windows.Forms; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BryanKellyCapstone2017 {/* WRITTEN BY BRYAN KELLY AS PART OF THE 2017 CAPSTONE. SPECIFICATIONS GIVEN BY DR. DAVID PANKRATZ & DR. MCVEY This contains the state space searches of my program. It contains both depth first search for just the robot and for integrating the robot pushing target blocks to their goal locations. It also includes an iterative deepening depth limited search. I apologize if some of the terminology gets confusing. I typically use DFS to stand for Depth First Search and then I use DLS for Depth Limited Search. Which is the iterative deepening depth limited search. One of the more confusing parts of this program is the interaction between the target searches and the robot searches. The basic idea is run a search on a target block to get to the goal. This is done by requesting the robot to reach a location oposite of the direction of motion. Say the target search attempts to move the target North (or up) it must first position the robot block one space to the south (or down one). If the robot could get to the requested position it will attempt to execute the move. All of the actual movement of the blocks is handled in the FloorMap.cs file. Another thing that may be confusing is the way I make a copy of the floor map (the list of arrays). I do this because I want to keep a copy where I mark spaces as visited in the searches. It doesn't matter to the program whether the copy actually has the walls or not it is only concerned with marking spaces as visited or not I have left some code in here that may not be used in the final product but it was used as either part of the debugging progess or testing new functionality. One of the functions was used as a way to attempt to just move the robot block and get the display to update in Main.cs it isn't actually called from Main.cs anymore but if you would like to try it you could add a button to Main.cs and call it. It will move the robot to the goal but it will get stuck if the path is too complex. */ class AITestFunctions { public event EventHandler errorTryingToMoveRobot; public event EventHandler robotReachedDestination; public event EventHandler movingOnVisitedSpace; public event EventHandler DFSContinue; public event EventHandler DFSReverse; public event EventHandler enableShowVisitedRobot; public event EventHandler enableShowVisitedTarget; public event EventHandler disableShowVisitedRobot; public event EventHandler disableShowVisitedTarget; public event EventHandler eventTargetReachedDestination; public event EventHandler eventTargetError; public event EventHandler eventResetRobotVisited; private Stack stackTargetRow; private Stack stackTargetCol; //private Stack stackRobotMovesFinal; private Stack stackTargetMovesFinal; private Stack stackTargetRequstRobotRow; private Stack stackTargetRequestRobotColumn; //private Queue stackRobotMovesTemp; private FloorMap floorMap; private List stateArrayRobot; private List stateArrayTarget; private int depthOfSearch; private int sleepAmount = 0; private bool returningRobotToState = false; private const string EMPTY = "0"; private const string WALL = "1"; private const string ROBOT = "2"; private const string TARGET = "3"; private const string GOAL = "G"; private const string VISITED = "V"; private const char NORTH = 'N'; private const char SOUTH = 'S'; private const char EAST = 'E'; private const char WEST = 'W'; private bool abort = false; private bool testDLS = false; public int SleepCount { //Use this to set the amount to wait based on user's input from Main.cs set { sleepAmount = value; } get { return sleepAmount; } } public bool AbortThread { //use this to exit recursive functions if requested by the user set { abort = value; if (abort == true) sendDisableShowVisitedRobot(); } get { return abort; } } public void depthLimiteSearchRobotTest(FloorMap tempFloorMap, int goalRow, int goalColumn) { /*This is a function that only runs the iterative deepening depth first search on the robot block. It is called by Main.cs and will only run until it reaches a goal location.*/ testDLS = true; floorMap = tempFloorMap; depthOfSearch = 0; stateArrayRobot = new List(); for (int i = 0; i < floorMap.NumRows; i++) { stateArrayRobot.Add(new string[floorMap.FloorLayout[i].Length]); for (int j = 0; j < floorMap.FloorLayout[i].Length; j++) { stateArrayRobot[i][j] = floorMap.FloorLayout[i][j]; } } stateArrayRobot[goalRow][goalColumn] = GOAL; sendEnableShowVisitedRobot(); for (int limit = 0; limit < floorMap.NumColumns * floorMap.NumRows; limit++) { //loop that starts at 0 and increases by one until a depth limit if (abort) return; if (progressDLSRobot(limit)) { sendEventRobotReachedDestination(); sendDisableShowVisitedRobot(); return; } else { sendEventErrorTryingToRemoveRobot(); resetDLSVisitedRobot(goalRow,goalColumn); } } sendDisableShowVisitedRobot(); } public bool depthLimitedSearchRobot( int goalRow, int goalColumn) { /*This is the DLS for the robot that is called from the DLS search on the target blocks TODO: properly update a stack of sucessful moves so that it can be used by the Target DLS to return to the previously working state. */ testDLS = false; depthOfSearch = 0; stateArrayRobot = new List(); for (int i = 0; i < floorMap.NumRows; i++) { stateArrayRobot.Add(new string[floorMap.FloorLayout[i].Length]); for (int j = 0; j < floorMap.FloorLayout[i].Length; j++) { stateArrayRobot[i][j] = floorMap.FloorLayout[i][j]; } } stateArrayRobot[goalRow][goalColumn] = GOAL; sendEnableShowVisitedRobot(); for (int limit = 0; limit < floorMap.NumColumns * floorMap.NumRows; limit++) { if (abort) return false; if (progressDLSRobot(limit)) { sendEventRobotReachedDestination(); sendDisableShowVisitedRobot(); return true; } else { sendEventErrorTryingToRemoveRobot(); resetDLSVisitedRobot(goalRow, goalColumn); } } sendDisableShowVisitedRobot(); return false; } private void resetDLSVisitedRobot(int goalRow, int goalColumn) { //resets the VISTED spaces to EMPTY for the robot sendEventResetRobotVisited(); sendEnableShowVisitedRobot(); for (int i = 0; i < floorMap.NumRows; i++) { for (int j = 0; j < floorMap.FloorLayout[i].Length; j++) { stateArrayRobot[i][j] = floorMap.FloorLayout[i][j]; } } stateArrayRobot[goalRow][goalColumn] = GOAL; } private void resetDLSVisitedTarget() { //Resets the VISITED spaces to EMPTY for the target for (int i = 0; i < floorMap.NumRows; i++) { for (int j = 0; j < floorMap.FloorLayout[i].Length; j++) { stateArrayTarget[i][j] = floorMap.FloorLayout[i][j]; } } } private bool isDLSRobotAtGoalLocation() { if (stateArrayRobot[floorMap.RobotRow][floorMap.RobotColumn] == GOAL) return true; if (testDLS) { if (stateArrayRobot[floorMap.RobotRow][floorMap.RobotColumn] == "4") { return true; } } return false; } private bool progressDLSRobot(int limit) { /*Progresses the DLS on the robot. Basically checks if at goal location. Else attempt to move North, West, South, East until finished or at depth limit. TODO: I want to use a stack to track the sucessful moves that are made by the robot in order to reset when the target DLS fails. Currently not working. */ if (abort) return false; if (isDLSRobotAtGoalLocation()) return true; else { if (limit < 0) return false; stateArrayRobot[floorMap.RobotRow][floorMap.RobotColumn] = VISITED; //move North if (floorMap.isValidLocation(floorMap.RobotRow - 1, floorMap.RobotColumn) && isEmptyOrGoalSpaceRobot(floorMap.RobotRow - 1, floorMap.RobotColumn)) { sendEnableShowVisitedRobot(); System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotNorth(false)) { if (progressDLSRobot(limit -1 )) return true; else { sendDisableShowVisitedRobot(); stateArrayRobot[floorMap.RobotRow][floorMap.RobotColumn] = EMPTY; System.Threading.Thread.Sleep(sleepAmount); floorMap.moveRobotSouth(); } } } //move West if (floorMap.isValidLocation(floorMap.RobotRow, floorMap.RobotColumn - 1) && isEmptyOrGoalSpaceRobot(floorMap.RobotRow, floorMap.RobotColumn - 1)) { sendEnableShowVisitedRobot(); System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotWest(false)) { if (progressDLSRobot(limit - 1)) return true; else { sendDisableShowVisitedRobot(); stateArrayRobot[floorMap.RobotRow][floorMap.RobotColumn] = EMPTY; System.Threading.Thread.Sleep(sleepAmount); floorMap.moveRobotEast(); } } } //move South if (floorMap.isValidLocation(floorMap.RobotRow + 1, floorMap.RobotColumn) && isEmptyOrGoalSpaceRobot(floorMap.RobotRow + 1, floorMap.RobotColumn)) { sendEnableShowVisitedRobot(); System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotSouth(false)) { if (progressDLSRobot(limit - 1)) return true; else { sendDisableShowVisitedRobot(); stateArrayRobot[floorMap.RobotRow][floorMap.RobotColumn] = EMPTY; System.Threading.Thread.Sleep(sleepAmount); floorMap.moveRobotNorth(); } } } //move East if (floorMap.isValidLocation(floorMap.RobotRow, floorMap.RobotColumn + 1) && isEmptyOrGoalSpaceRobot(floorMap.RobotRow, floorMap.RobotColumn + 1)) { sendEnableShowVisitedRobot(); System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotEast(false)) { if (progressDLSRobot(limit -1)) return true; else { sendDisableShowVisitedRobot(); stateArrayRobot[floorMap.RobotRow][floorMap.RobotColumn] = EMPTY; System.Threading.Thread.Sleep(sleepAmount); floorMap.moveRobotWest(); } } } return false; } } public void depthLimitedSearchTarget(FloorMap tempFloorMap) { /*Sets up the Iterative deepening depth limited search for the target blocks. It will search through the floormap and find all of the targets and add them to a stack. Then it will loop until this stack is empty running a iterative deepening depth limited search on them until finished. */ floorMap = tempFloorMap; stateArrayTarget = new List(); stackTargetMovesFinal = new Stack(); //store the order of the path direction for execution. stackTargetRequestRobotColumn = new Stack(); //use these to store the location that the target attempts to move the robot to before a push. stackTargetRequstRobotRow = new Stack(); //Use these stacks to keep track of the target blocks that are on the map. stackTargetRow = new Stack(); stackTargetCol = new Stack(); for (int i = 0; i < floorMap.NumRows; i++) { stateArrayTarget.Add(new string[floorMap.FloorLayout[i].Length]); for (int j = 0; j < floorMap.FloorLayout[i].Length; j++) { stateArrayTarget[i][j] = floorMap.FloorLayout[i][j]; if (stateArrayTarget[i][j] == "4") stateArrayTarget[i][j] = GOAL; if (stateArrayTarget[i][j] == TARGET) { stackTargetRow.Push(i); stackTargetCol.Push(j); } } } sendEnableShowVisitedTarget(); int tempRow, tempCol; while (stackTargetRow.Count > 0) {//this loop runs until all of the targets have been taken off of the stack. if (abort) return; tempRow = stackTargetRow.Pop(); tempCol = stackTargetCol.Pop(); for (int limit = 0; limit < (floorMap.NumColumns * floorMap.NumRows); limit++) { //this for loop makes this an iterative deepening DLS. It increases the limit of the //DLS from 0 to find an optimal path (multiple can exist) if(abort) return; if (progressDLSTarget(tempRow, tempCol, limit)) { resetTargetVisitedFlags(); sendEventTargetReachedDestination(); break; } else { sendEventTargetError(); resetDLSVisitedTarget(); } } } sendDisableShowVisitedTarget(); } private bool progressDLSTarget(int targetRow, int targetColumn, int limit) { /*Recursive function of the iterative deepening depth limited search. Checks if at he goal location otherwise it checks in the following order. North West South East. If none of those work it returns false. If it reaches the depth limit it also returns false. TODO: Fix the stack keeping track of the sucessful paths. Currently uses the same stragegy as the DFS which works but isn't true to the DLS search because it uses an inefficient path to return the robot to the previously working state. I was having some trouble thinking of how to properly keep track of the successful paths. */ if (abort) return false; if (stateArrayTarget[targetRow][targetColumn] == GOAL) return true; else { stateArrayTarget[targetRow][targetColumn] = VISITED; //Move North if (isTargetMovableNorthSouth(targetRow, targetColumn) && isEmptyOrGoalSpaceTarget(targetRow - 1, targetColumn)) { sendEventResetRobotVisited(); if (depthLimitedSearchRobot(targetRow + 1, targetColumn)) { //see if the robot can get into position System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotNorth()) { //robot is in position, see if it cam push the target north stackTargetMovesFinal.Push(NORTH); stackTargetRequstRobotRow.Push(targetRow + 1); stackTargetRequestRobotColumn.Push(targetColumn); if (progressDLSTarget(targetRow - 1, targetColumn, limit-1)) { //progress the Depth First Search for the target return true; } else { stackTargetMovesFinal.Pop(); stackTargetRequstRobotRow.Pop(); stackTargetRequestRobotColumn.Pop(); System.Threading.Thread.Sleep(sleepAmount); returnToPreviousState(); } } } } //Move West if (isTargetMovableEastWest(targetRow, targetColumn) && isEmptyOrGoalSpaceTarget(targetRow, targetColumn - 1)) { sendEventResetRobotVisited(); if (depthLimitedSearchRobot(targetRow, targetColumn + 1)) { //see if the robot can get into position System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotWest()) { //robot is in position, see if it cam push the target West stackTargetMovesFinal.Push(WEST); stackTargetRequstRobotRow.Push(targetRow); stackTargetRequestRobotColumn.Push(targetColumn + 1); if (progressDLSTarget(targetRow, targetColumn - 1, limit - 1)) { //progress the Depth First Search for the target return true; } else { stackTargetMovesFinal.Pop(); stackTargetRequstRobotRow.Pop(); stackTargetRequestRobotColumn.Pop(); System.Threading.Thread.Sleep(sleepAmount); returnToPreviousState(); } } } } //Move South if (isTargetMovableNorthSouth(targetRow, targetColumn) && isEmptyOrGoalSpaceTarget(targetRow + 1, targetColumn)) { sendEventResetRobotVisited(); if (depthLimitedSearchRobot(targetRow - 1, targetColumn)) { //see if the robot can get into position System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotSouth()) { //robot is in position, see if it cam push the target South stackTargetMovesFinal.Push(SOUTH); stackTargetRequstRobotRow.Push(targetRow - 1); stackTargetRequestRobotColumn.Push(targetColumn); if (progressDLSTarget(targetRow + 1, targetColumn, limit - 1)) { //progress the Depth First Search for the target return true; } else { stackTargetMovesFinal.Pop(); stackTargetRequstRobotRow.Pop(); stackTargetRequestRobotColumn.Pop(); System.Threading.Thread.Sleep(sleepAmount); returnToPreviousState(); } } } } //Move East if (isTargetMovableEastWest(targetRow, targetColumn) && isEmptyOrGoalSpaceTarget(targetRow, targetColumn + 1)) { sendEventResetRobotVisited(); if (depthLimitedSearchRobot(targetRow, targetColumn - 1)) { //see if the robot can get into position System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotEast()) { //robot is in position, see if it cam push the target East stackTargetMovesFinal.Push(EAST); stackTargetRequstRobotRow.Push(targetRow); stackTargetRequestRobotColumn.Push(targetColumn - 1); if (progressDLSTarget(targetRow, targetColumn + 1, limit - 1)) { //progress the Depth First Search for the target return true; } else { stackTargetMovesFinal.Pop(); stackTargetRequstRobotRow.Pop(); stackTargetRequestRobotColumn.Pop(); System.Threading.Thread.Sleep(sleepAmount); returnToPreviousState(); } } } } } return false; } public void depthFirstSearchTarget(FloorMap tempFloorMap) { /*Setup function to start the depth first search for the target blocks. This searches for all of the targets in the floorMap and then adds them to a stack. It will then run a loop until this stack is empty to run a DFS on the targets */ floorMap = tempFloorMap; stateArrayTarget = new List(); stackTargetMovesFinal = new Stack(); //store the order of the path direction for execution. stackTargetRequestRobotColumn = new Stack(); //use these to store the location that the target attempts to move the robot to before a push. stackTargetRequstRobotRow = new Stack(); //Use these stacks to keep track of the target blocks that are on the map. stackTargetRow = new Stack(); stackTargetCol = new Stack(); for (int i = 0; i < floorMap.NumRows; i++) { stateArrayTarget.Add(new string[floorMap.FloorLayout[i].Length]); for (int j = 0; j < floorMap.FloorLayout[i].Length; j++) { stateArrayTarget[i][j] = floorMap.FloorLayout[i][j]; if (stateArrayTarget[i][j] == "4") stateArrayTarget[i][j] = GOAL; if (stateArrayTarget[i][j] == TARGET) { stackTargetRow.Push(i); stackTargetCol.Push(j); } } } sendEnableShowVisitedTarget(); while (stackTargetRow.Count > 0) { if (progressDFSTarget(stackTargetRow.Pop(), stackTargetCol.Pop())) { resetTargetVisitedFlags(); sendEventTargetReachedDestination(); } else { sendEventTargetError(); } } sendDisableShowVisitedTarget(); } private void resetTargetVisitedFlags() { /*This goes through the stateArrayTarget array and sets all of the values to what the floormap has. Purpose of this is to set the VISTITED spaces to EMPTY*/ for (int i = 0; i < floorMap.NumRows; i++) { for (int j = 0; j < floorMap.FloorLayout[i].Length; j++) { stateArrayTarget[i][j] = floorMap.FloorLayout[i][j]; if (stateArrayTarget[i][j] == "4") stateArrayTarget[i][j] = GOAL; } } } public bool depthFirstSearchRobot( int goalRow, int goalColumn) {/*This is the depth first search setup function for the robot. It sets up the state array, establishes the goal location and then starts the recursive call to progress the DFS on the robot. I attempted to put a limit on the depth of search so that it wouldn't go on too far but it doesn't work the way I planned. The DLS searches properly implement a depth of serach limit. */ depthOfSearch = 0; stateArrayRobot = new List(); for (int i = 0; i < floorMap.NumRows; i++) { stateArrayRobot.Add(new string[floorMap.FloorLayout[i].Length]); for (int j = 0; j < floorMap.FloorLayout[i].Length; j++) { stateArrayRobot[i][j] = floorMap.FloorLayout[i][j]; } } stateArrayRobot[goalRow][goalColumn] = GOAL; if(!returningRobotToState) sendEnableShowVisitedRobot(); if (progressDFSRobot()) { sendEventRobotReachedDestination(); sendDisableShowVisitedRobot(); return true; } else { sendEventErrorTryingToRemoveRobot(); sendDisableShowVisitedRobot(); return false; } } public void depthFirstSearchRobotTest(FloorMap tempFloorMap,int goalRow,int goalColumn) { /*used to setup. I used this to test that the DFS works. It is not usable with the DFSTarget function. It is used to show just the robot perform it's DFS. Called from Main.cs*/ floorMap = tempFloorMap; depthOfSearch = 0; stateArrayRobot = new List(); for (int i = 0; i < floorMap.NumRows; i++) { stateArrayRobot.Add(new string[floorMap.FloorLayout[i].Length]); for(int j = 0; j < floorMap.FloorLayout[i].Length; j++) { stateArrayRobot[i][j] = floorMap.FloorLayout[i][j]; } } stateArrayRobot[goalRow][goalColumn] = GOAL; sendEnableShowVisitedRobot(); if (progressDFSRobot()) sendEventRobotReachedDestination(); else sendEventErrorTryingToRemoveRobot(); sendDisableShowVisitedRobot(); } private bool progressDFSTarget(int targetRow, int targetColumn) { /*This function is the recursive DFS for the Target block. The general structure of this is check if the goal state is reached. If so then return true. Else continue the recursive calls until no move can be made. The interesting part of this function is that it must perform a DFS on the robot to move the robot into position to move this target block in a direction. So if attempting to move north(up) it will need to postion the robot one block to the south(down). Then it can call the robot move north function. A few stacks are used to track what moves were successful in order to return to a previously working state if the target reaches a state where it is stuck. requesting to sleep the thread before each robot move is necessary to keep the robot movement looking consistent and slow enough for the user to follow the execution of the program. */ if (abort) return false; if (stateArrayTarget[targetRow][targetColumn] == GOAL) return true; else { stateArrayTarget[targetRow][targetColumn] = VISITED; //Move North if (isTargetMovableNorthSouth(targetRow, targetColumn) && isEmptyOrGoalSpaceTarget(targetRow - 1, targetColumn)) { sendEventResetRobotVisited(); if(depthFirstSearchRobot(targetRow+1,targetColumn)) { //see if the robot can get into position System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotNorth()) { //robot is in position, see if it can push the target north stackTargetMovesFinal.Push(NORTH); stackTargetRequstRobotRow.Push(targetRow + 1); stackTargetRequestRobotColumn.Push(targetColumn); if(progressDFSTarget(targetRow - 1, targetColumn)) { //progress the Depth First Search for the target return true; } else { stackTargetMovesFinal.Pop(); stackTargetRequstRobotRow.Pop(); stackTargetRequestRobotColumn.Pop(); System.Threading.Thread.Sleep(sleepAmount); returnToPreviousState(); } } } } //Move West if (isTargetMovableEastWest(targetRow, targetColumn) && isEmptyOrGoalSpaceTarget(targetRow, targetColumn-1)) { sendEventResetRobotVisited(); if (depthFirstSearchRobot(targetRow, targetColumn + 1)) { //see if the robot can get into position System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotWest()) { //robot is in position, see if it can push the target West stackTargetMovesFinal.Push(WEST); stackTargetRequstRobotRow.Push(targetRow); stackTargetRequestRobotColumn.Push(targetColumn + 1); if (progressDFSTarget(targetRow, targetColumn - 1)) { //progress the Depth First Search for the target return true; } else { stackTargetMovesFinal.Pop(); stackTargetRequstRobotRow.Pop(); stackTargetRequestRobotColumn.Pop(); System.Threading.Thread.Sleep(sleepAmount); returnToPreviousState(); } } } } //Move South if (isTargetMovableNorthSouth(targetRow, targetColumn) && isEmptyOrGoalSpaceTarget(targetRow + 1, targetColumn)) { sendEventResetRobotVisited(); if (depthFirstSearchRobot(targetRow - 1, targetColumn)) { //see if the robot can get into position System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotSouth()) { //robot is in position, see if it can push the target South stackTargetMovesFinal.Push(SOUTH); stackTargetRequstRobotRow.Push(targetRow - 1); stackTargetRequestRobotColumn.Push(targetColumn); if (progressDFSTarget(targetRow + 1, targetColumn)) { //progress the Depth First Search for the target return true; } else { stackTargetMovesFinal.Pop(); stackTargetRequstRobotRow.Pop(); stackTargetRequestRobotColumn.Pop(); System.Threading.Thread.Sleep(sleepAmount); returnToPreviousState(); } } } } //Move East if (isTargetMovableEastWest(targetRow, targetColumn) && isEmptyOrGoalSpaceTarget(targetRow, targetColumn+1)) { sendEventResetRobotVisited(); if (depthFirstSearchRobot(targetRow, targetColumn - 1)) { //see if the robot can get into position System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotEast()) { //robot is in position, see if it can push the target East stackTargetMovesFinal.Push(EAST); stackTargetRequstRobotRow.Push(targetRow); stackTargetRequestRobotColumn.Push(targetColumn - 1); if (progressDFSTarget(targetRow, targetColumn+1)) { //progress the Depth First Search for the target return true; } else { stackTargetMovesFinal.Pop(); stackTargetRequstRobotRow.Pop(); stackTargetRequestRobotColumn.Pop(); System.Threading.Thread.Sleep(sleepAmount); returnToPreviousState(); } } } } } return false; } private void returnToPreviousState() { //This needs to be used to return the DFS target search to the previous state it was in. //this will need to use the stack of robot moves minus the most previous state. Stack tempStackDirection = new Stack(); Stack tempStackRow = new Stack(); Stack tempStackColumn = new Stack(); string fileName = floorMap.GetFileName; while (stackTargetMovesFinal.Count > 0) { //reverse the stack order so that first move is at top of stack tempStackDirection.Push(stackTargetMovesFinal.Pop()); tempStackRow.Push(stackTargetRequstRobotRow.Pop()); tempStackColumn.Push(stackTargetRequestRobotColumn.Pop()); } floorMap.Open(fileName); //reset the data while (tempStackDirection.Count > 0) { returnRobotToPreviousState(tempStackRow.Peek(), tempStackColumn.Peek(), tempStackDirection.Peek()); stackTargetMovesFinal.Push(tempStackDirection.Pop()); stackTargetRequstRobotRow.Push(tempStackRow.Pop()); stackTargetRequestRobotColumn.Push(tempStackColumn.Pop()); } } private void returnRobotToPreviousState(int row, int column, char direction) { /*This function is called by returnToPreviousState() which used a stack of moves that worked correctly. This will run a DFS on the robot to return it to a goal state that should be next to a target. It then moves the target in the requested direction. Some special functionality of this function is that it disables showing what is visited and the sleep time is cut in half. I do this to try to communicate to the user that it isn't trying a new search but performing a search to reset to a state that worked. */ int tempSleepAmount = sleepAmount; sendDisableShowVisitedRobot(); returningRobotToState = true; sleepAmount = sleepAmount/2; depthFirstSearchRobot(row, column); System.Threading.Thread.Sleep(sleepAmount); switch (direction) { case NORTH: floorMap.moveRobotNorth(); break; case SOUTH: floorMap.moveRobotSouth(); break; case EAST: floorMap.moveRobotEast(); break; case WEST: floorMap.moveRobotWest(); break; } sleepAmount = tempSleepAmount; returningRobotToState = false; sendEnableShowVisitedRobot(); } private bool progressDFSRobot() { /*This function is recursive and will progress the depth first search on the robot. It starts off checking if the user requested to abort the search. If so it will return false until it exits the recursion. The structure of this function is to check if the goal state is reached. If so return true. Otherwise do the following: Try to move North (up) if can move, make recursive call to progress DFSRobot() Try to move West (left) if can move, make recursive call to progressDFSRobot() Try to move South (down) if can move, call progressDFSRobot() Try to move East (right) if can move, call progressDFSRobot() If none of these work return false. I included a number of sleep statements to slow down executution so the user can follow the execution of the search */ // TODO: Fix the depthOfSearch. This isn't working as anticipated. Probably should put it as a parameter instead of as a global variable. if (abort) return false; depthOfSearch++; if (depthOfSearch > (floorMap.NumRows * floorMap.NumColumns)) { return false; } if (stateArrayRobot[floorMap.RobotRow][floorMap.RobotColumn] == GOAL) return true; else { stateArrayRobot[floorMap.RobotRow][floorMap.RobotColumn] = VISITED; if (floorMap.isValidLocation(floorMap.RobotRow - 1, floorMap.RobotColumn) && isEmptyOrGoalSpaceRobot(floorMap.RobotRow - 1, floorMap.RobotColumn)) { System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotNorth(false)) { if (progressDFSRobot()) return true; else { System.Threading.Thread.Sleep(sleepAmount); floorMap.moveRobotSouth(); } } } if(floorMap.isValidLocation(floorMap.RobotRow, floorMap.RobotColumn-1) && isEmptyOrGoalSpaceRobot(floorMap.RobotRow, floorMap.RobotColumn-1)) { System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotWest(false)) { if (progressDFSRobot()) return true; else { System.Threading.Thread.Sleep(sleepAmount); floorMap.moveRobotEast(); } } } if (floorMap.isValidLocation(floorMap.RobotRow + 1, floorMap.RobotColumn) && isEmptyOrGoalSpaceRobot(floorMap.RobotRow + 1, floorMap.RobotColumn)) { System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotSouth(false)) { if (progressDFSRobot()) return true; else { System.Threading.Thread.Sleep(sleepAmount); floorMap.moveRobotNorth(); } } } if (floorMap.isValidLocation(floorMap.RobotRow, floorMap.RobotColumn+1) && isEmptyOrGoalSpaceRobot(floorMap.RobotRow, floorMap.RobotColumn+1)) { System.Threading.Thread.Sleep(sleepAmount); if (floorMap.moveRobotEast(false)) { if (progressDFSRobot()) return true; else { System.Threading.Thread.Sleep(sleepAmount); floorMap.moveRobotWest(); } } } return false; } } private bool isTargetMovableNorthSouth(int targetRow, int targetCol) { /*Checks if the Target has empty spaces both to the north and south. If so it is possible to move either to the north or south once space. If either there is a wall or the edge of the map this will return false. Otherwise the target is able to be moved either north or south*/ if (!floorMap.isValidLocation(targetRow + 1, targetCol) || !floorMap.isValidLocation(targetRow - 1, targetCol)) return false; if (stateArrayTarget[targetRow + 1][targetCol] == WALL) return false; if (stateArrayTarget[targetRow - 1][targetCol] == WALL) return false; return true; } private bool isTargetMovableEastWest(int targetRow, int targetCol) { /*Checks if there is a wall or the edge of the map to the east or west. If the spaces are empty then the target can be pushed either east or west (assuming the robot can reach that location)*/ if (!floorMap.isValidLocation(targetRow, targetCol + 1) || !floorMap.isValidLocation(targetRow, targetCol - 1)) return false; if (stateArrayTarget[targetRow][targetCol + 1] == WALL) return false; if (stateArrayTarget[targetRow][targetCol - 1] == WALL) return false; return true; } private bool isEmptyOrGoalSpaceRobot(int row,int col) { /*checks if the robot is moving to a goal or empty space. I created this mainly to make the if statements more readable throughout the search functions. */ return (stateArrayRobot[row][col] == EMPTY || stateArrayRobot[row][col] == GOAL || stateArrayRobot[row][col] == "4"); } private bool isEmptyOrGoalSpaceTarget(int row, int col) { /*this returns if this is a goal or empty space for a target. This was written to make if statements in the seraches more readable. */ return (stateArrayTarget[row][col] == EMPTY || stateArrayTarget[row][col] == GOAL) || stateArrayTarget[row][col] == ROBOT; } //The following functions raise event handlers. I separated them for readablilty. private void sendEventResetRobotVisited() { if (this.eventResetRobotVisited != null) this.eventResetRobotVisited(this, EventArgs.Empty); } private void sendDisableShowVisitedRobot() { if (this.disableShowVisitedRobot != null) this.disableShowVisitedRobot(this, EventArgs.Empty); } private void sendDisableShowVisitedTarget() { if (this.disableShowVisitedTarget != null) this.disableShowVisitedTarget(this, EventArgs.Empty); } private void sendEventTargetReachedDestination() { if (this.eventTargetReachedDestination != null) this.eventTargetReachedDestination(this, EventArgs.Empty); } private void sendEventTargetError() { if (this.eventTargetError != null) this.eventTargetError(this, EventArgs.Empty); } private void sendEnableShowVisitedTarget() { if (this.enableShowVisitedTarget != null) this.enableShowVisitedTarget(this, EventArgs.Empty); } private void sendEnableShowVisitedRobot() { if (this.enableShowVisitedRobot != null) this.enableShowVisitedRobot(this, EventArgs.Empty); } private void sendVisitedMessage() { if (this.movingOnVisitedSpace != null) this.movingOnVisitedSpace(this, EventArgs.Empty); } private void sendDFSContinueMessage() { if (this.DFSContinue != null) this.DFSContinue(this, EventArgs.Empty); } private void sendDFSReverse() { if (this.DFSReverse != null) this.DFSReverse(this, EventArgs.Empty); } private void sendEventErrorTryingToRemoveRobot() { if (this.errorTryingToMoveRobot != null) this.errorTryingToMoveRobot(this, EventArgs.Empty); } private void sendEventRobotReachedDestination() { if (this.robotReachedDestination != null) this.robotReachedDestination(this, EventArgs.Empty); } //The following are functions I used early on in the development process to make sure the log in Main.cs and the movement of the //blocks would work as intended. I these aren't currently implemented in Main.cs public void moveRobotToGoal(FloorMap tempFloorMap, int goalRow, int goalColumn) { /*This is just a test function. I used it to test if I could get communication between FloorMap.cs AITestFunctions.cs and the Main.cs. This helped me realize I needed to use threading.*/ floorMap = tempFloorMap; int row, column; while (!isRobotAtGoal(goalRow, goalColumn)) { System.Threading.Thread.Sleep(sleepAmount); row = floorMap.RobotRow; column = floorMap.RobotColumn; if (row < 0 || column < 0) return; if (goalRow == row) { //already at goal row, now move closer to goal column if (!moveRobotToCloserColumn(goalColumn)) { sendEventErrorTryingToRemoveRobot(); return; } } else if (goalColumn == column) { //already at goal column, now move closer to goal row if (!moveRobotToCloserRow(goalRow)) { sendEventErrorTryingToRemoveRobot(); return; } } else { //not at either goal row or goal column, find which is closer if (Math.Abs(goalRow - floorMap.RobotRow) < Math.Abs(goalColumn - floorMap.RobotColumn)) { //closer to goal row than goal column if (!moveRobotToCloserRow(goalRow)) { //If it can't move up or down try left or right if (!moveRobotToCloserColumn(goalColumn)) { sendEventErrorTryingToRemoveRobot(); return; } } } else { if (!moveRobotToCloserColumn(goalColumn)) { //if it can't move left or right try up or down if (!moveRobotToCloserRow(goalRow)) { sendEventErrorTryingToRemoveRobot(); return; } } } } } sendEventRobotReachedDestination(); } private bool moveRobotToCloserRow(int goalRow) { //attempts to move the robot closer to the goal on the row. if ((goalRow - floorMap.RobotRow) > 0) { if (floorMap.moveRobotSouth()) return true; else return false; } else { if (floorMap.moveRobotNorth()) return true; else return false; } } private bool moveRobotToCloserColumn(int goalColumn) { //atempts to mvoe the robot closer on the column if ((goalColumn - floorMap.RobotColumn) > 0) { if (floorMap.moveRobotEast()) return true; else return false; } else { if (floorMap.moveRobotWest()) return true; else return false; } } private bool isRobotAtGoal(int goalRow, int goalColumn) { //checks if the robot is at the goal. return (floorMap.RobotRow == goalRow && floorMap.RobotColumn == goalColumn); } } }