using System; using System.IO; using System.Drawing; using System.Windows.Forms; using System.Collections.Generic; 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 is how I handle the data from an input file. Right now the only requriements of the file are that the numbers must be separated with a space NUMBER MEANINGS 0 = EMPTY SPACE 1 = WALL 2 = ROBOT 3 = TARGET BLOCK 4 = DESTINATION/GOAL LOCATION */ class FloorMap { /* EVENTS */ public event EventHandler OpenDocument; public event EventHandler UpdateCaption; public event EventHandler moveRobot; public event EventHandler moveTarget; public event EventHandler redrawGoal; public event EventHandler targetReachedGoal; public event EventHandler allTargetsReachedGoal; public event EventHandler multipleRobotsDetectedInFile; private const string EMPTY = "0"; private const string WALL = "1"; private const string ROBOT = "2"; private const string TARGET = "3"; private const string GOAL = "4"; //setup List floorMap; int numRows = 0; int maxRowLength = 0; int robotRow = -1; int robotColumn = -1; int targetNewRow = -1; int targetNewColumn = -1; int goalRow= -1; int goalColumn = -1; int numTargets = 0; bool robotIsOnGoal = false; bool finished = true; bool multipleRobotMessageSent = false; //other variables private string fileName = ""; //setters/getters public int NumRows { get { return numRows; } } public int NumColumns { get { return maxRowLength; } } public List FloorLayout { get { return floorMap; } } public int RobotRow { get { return robotRow; } } public int RobotColumn { get { return robotColumn; } } public int TargetNewRow { get { return targetNewRow; } } public int TargetNewColumn { get { return targetNewColumn; } } public int GoalRow { get { return goalRow; } } public int GoalColumn { get { return goalColumn; } } public int NumTargets { get { return numTargets; } } public string GetFileName { get { return fileName; } } /* Functions */ public bool moveRobotNorth(bool canMoveTarget = true) { /* This checks if the location the robot is moving to is valid, if a wall lies in the spot if a target lies there and finally if it is moving over a goal spot. There are four total variations of this function for moving North, South, East or West or Up, Down, Right and Left on the monitor. */ int newspace = robotRow - 1; if (!isValidLocation((newspace), robotColumn)) return false;//not valid move if (isWallOccupyingSpace(newspace, robotColumn)) return false;//not valid move if (isMovingTarget(newspace, robotColumn)) { if (!canMoveTarget) return false; if (!moveTargetNewLocation(newspace - 1, robotColumn)) return false;//failed and this is an invalid move } bool redrawGoal = moveRobotOffSpaceCheckGoalRedrawCall(); robotRow--; completeRobotMoveAction(redrawGoal); return true; } public bool moveRobotSouth(bool canMoveTarget = true) { /* This checks if the location the robot is moving to is valid, if a wall lies in the spot if a target lies there and finally if it is moving over a goal spot. There are four total variations of this function for moving North, South, East or West or Up, Down, Right and Left on the monitor. */ int newspace = robotRow + 1; if (!isValidLocation(newspace, robotColumn)) return false;//not valid move if (isWallOccupyingSpace(newspace, robotColumn)) return false;//not valid move if (isMovingTarget(newspace, robotColumn)) { if (!canMoveTarget) return false; if (!moveTargetNewLocation(newspace + 1, robotColumn)) return false;//failed and this is an invalid move } bool redrawGoal = moveRobotOffSpaceCheckGoalRedrawCall(); robotRow++; completeRobotMoveAction(redrawGoal); return true; } public bool moveRobotEast(bool canMoveTarget = true) { /* This checks if the location the robot is moving to is valid, if a wall lies in the spot if a target lies there and finally if it is moving over a goal spot. There are four total variations of this function for moving North, South, East or West or Up, Down, Right and Left on the monitor. */ int newspace = robotColumn + 1; if (!isValidLocation(robotRow, newspace)) return false;//not valid move if (isWallOccupyingSpace(robotRow, newspace)) return false;//not valid move if (isMovingTarget(robotRow, newspace)) { if (!canMoveTarget) return false; if (!moveTargetNewLocation(robotRow, newspace + 1)) return false;//failed and this is an invalid move } bool redrawGoal = moveRobotOffSpaceCheckGoalRedrawCall(); robotColumn++; completeRobotMoveAction(redrawGoal); return true; } public bool moveRobotWest(bool canMoveTarget = true) { /* This checks if the location the robot is moving to is valid, if a wall lies in the spot if a target lies there and finally if it is moving over a goal spot. There are four total variations of this function for moving North, South, East or West or Up, Down, Right and Left on the monitor. */ int newspace = robotColumn - 1; if (!isValidLocation(robotRow, newspace)) return false;//not valid move if (isWallOccupyingSpace(robotRow, newspace)) return false;//not valid move if (isMovingTarget(robotRow, newspace)) { if (!canMoveTarget) return false; if (!moveTargetNewLocation(robotRow, newspace - 1)) return false;//failed and this is an invalid move } bool redrawGoal = moveRobotOffSpaceCheckGoalRedrawCall(); robotColumn--; completeRobotMoveAction(redrawGoal); return true; } private void completeRobotMoveAction(bool redrawGoal) { /*Handles updating all of the robot move actions. One problem I had to deal with was what would happen if the robot was on a goal space if that occured I need to keep track of the fact that it is on a goal space and reset it upon leaving that space. */ if (isGoalOccupyingSpace(robotRow, robotColumn)) robotIsOnGoal = true; floorMap[robotRow][robotColumn] = ROBOT; if (this.moveRobot != null) this.moveRobot(this, EventArgs.Empty); checkFinished(); if (redrawGoal) makeRedrawGoalCall(); } private void checkFinished() { //Checks if all of the robots have reached a goal location. Sends an event if true. //Currently in main it should show a messagebox when all of the targets reach a goal if (!finished && numTargets == 0) { //All targets reached goal finished = true; if (this.allTargetsReachedGoal != null) this.allTargetsReachedGoal(this, EventArgs.Empty); } } private bool moveTargetNewLocation(int row, int column) { /*recieves location to move a target to doesn't need to worry about cleanup because robot will occupy that space now*/ if(!isValidLocation(row,column)) return false;//invalid move if (isWallOccupyingSpace(row, column)) return false;//invalid move if (isMovingTarget(row, column)) return false;//can't move another target block if (isGoalOccupyingSpace(row, column)) { numTargets--; if (this.targetReachedGoal != null) this.targetReachedGoal(this, EventArgs.Empty); return true; //may need to change this depending on how I implement the AI } floorMap[row][column] = TARGET; targetNewRow = row; targetNewColumn = column; if (this.moveTarget != null) this.moveTarget(this, EventArgs.Empty); return true; } private bool moveRobotOffSpaceCheckGoalRedrawCall() { /*checks if the robot is covering a goal spot. if it is make sure goal is redrawn and added back to the array If it wasn't on a goal spot just fill the previous location with empty space*/ if (robotIsOnGoal) { floorMap[robotRow][robotColumn] = GOAL; goalRow = robotRow; goalColumn = robotColumn; robotIsOnGoal = false; return true; } else { floorMap[robotRow][robotColumn] = EMPTY; return false; } } private void makeRedrawGoalCall() { /*Simply makes a redraw event call*/ if (this.redrawGoal != null) this.redrawGoal(this, EventArgs.Empty); } private bool isWallOccupyingSpace(int rowIndex, int columnIndex) { /* checks if the space is a wall. returns true if the space given is a wall block*/ if (floorMap[rowIndex][columnIndex] == WALL) { return true; } return false; } private bool isGoalOccupyingSpace(int rowIndex,int columnIndex) { /*returns true if space given is a goal space*/ return (floorMap[rowIndex][columnIndex] == GOAL); } public bool isValidLocation(int rowIndex, int columnIndex) { //displays if the given location exists. If outside bounds of array returns false else returns true if (rowIndex >= numRows|| rowIndex < 0) { return false; } else if (floorMap[rowIndex].Length <= columnIndex || columnIndex < 0) return false; else return true; } public bool isMovingTarget(int row, int column) { /*returns true if the robot is going to be moving the target. In other words if the robot is required to push a target. Returns true if target block ocupies the space given as parameter*/ if (floorMap[row][column] == TARGET) return true; return false; } public bool Open(string newFileName = "") { //from Dr. McVey's DocView Class Lab 6 of cs 350 //input file must be a .txt file with numbers between 0 and 4 //TODO add check for if the file was empty //check if filename was given if so if (string.IsNullOrEmpty(newFileName)) { OpenFileDialog dlg = new System.Windows.Forms.OpenFileDialog(); dlg.InitialDirectory = Application.StartupPath; DialogResult res = dlg.ShowDialog(); if (res != DialogResult.OK) return false; newFileName = dlg.FileName; multipleRobotMessageSent = false; } try { using (StreamReader sr = new StreamReader(newFileName)) { string data; char[] delim = { ' ','\n' }; floorMap = new List(); resetDataOnOpen(); int tempRowLength = 0; while (true) { //read through the file. Each line is an array and each line is added to a list data = sr.ReadLine(); if (data == null) break; //RemoveEmptyEntries resolves problem with empty char at the end of a line floorMap.Add(data.Split(delim,StringSplitOptions.RemoveEmptyEntries)); tempRowLength = floorMap[numRows].Length; if (maxRowLength < tempRowLength) maxRowLength = tempRowLength; numRows++; } findRobotAndCountTargets(); //update robotRow and robotColumn } } catch(Exception e) { MessageBox.Show("Can't open file: " + newFileName + "\r\n" + e.Message, Application.ProductName); return false; } fileName = newFileName; //Success if (this.OpenDocument != null) this.OpenDocument(this, EventArgs.Empty); if (this.UpdateCaption != null) this.UpdateCaption(this, EventArgs.Empty); return true; }//end bool Open private void resetDataOnOpen() { //resets any data upon opening a file to the default values //reset robot row and column robotRow = -1; robotColumn = -1; numRows = 0; maxRowLength = 0; robotIsOnGoal = false; finished = true; } private void findRobotAndCountTargets() { /*Finds the robot (only counts first robot it finds) and counts the amount of targets in the file If there are multiple robot blocks in the file it sends an event so main can display a message. */ bool robotFound = false; numTargets = 0; for (int row = 0; row < numRows; row++) { for (int column = 0; column < NumColumns; column++) { if (isValidLocation(row, column)) { if (Convert.ToInt32(floorMap[row][column]) < 0 || Convert.ToInt32(floorMap[row][column]) > 4) floorMap[row][column] = WALL; if (floorMap[row][column] == ROBOT) { //only accepts the first robot it finds. Ingore any others in file. if (robotFound) { //if a robot has already been found make this robot a wall floorMap[row][column] = WALL; if (!multipleRobotMessageSent) { multipleRobotMessageSent = true; sendMultipleRobotMessage(); } } else { //if this is the first robot that's found keep track of it robotRow = row; robotColumn = column; robotFound = true; } } if (floorMap[row][column] == TARGET) { numTargets++; } } } } if (numTargets > 0) finished = false; } private void sendMultipleRobotMessage() { if (this.multipleRobotsDetectedInFile != null) this.multipleRobotsDetectedInFile(this, EventArgs.Empty); } private void showOutputArrayData() { //this is just for testing and it will display the data in the file string test = ""; ; for (int row = 0; row < numRows; row++) { for (int col = 0; col < 10; col++) { test += floorMap[row][col]; } test += '\n'; } MessageBox.Show("MaxRowLength = " + maxRowLength + "\nThe file contains the following\n" + test); } } }