Guest User

Problem Set 6

a guest
Jul 11th, 2012
564
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.51 KB | None | 0 0
  1. # Problem Set 6: Simulating robots
  2. # Name: Niki Castle
  3. # Collaborators: none
  4. # Time: ~8hrs + testing time
  5.  
  6. import math
  7. import random
  8.  
  9. import ps6_visualize
  10. import pylab
  11.  
  12. # === Provided classes
  13.  
  14. class Position(object):
  15.     """
  16.    A Position represents a location in a two-dimensional room.
  17.    """
  18.     def __init__(self, x, y):
  19.         """
  20.        Initializes a position with coordinates (x, y).
  21.        """
  22.         self.x = x
  23.         self.y = y
  24.     def getX(self):
  25.         return self.x
  26.     def getY(self):
  27.         return self.y
  28.     def getNewPosition(self, angle, speed):
  29.         """
  30.        Computes and returns the new Position after a single clock-tick has
  31.        passed, with this object as the current position, and with the
  32.        specified angle and speed.
  33.  
  34.        Does NOT test whether the returned position fits inside the room.
  35.  
  36.        angle: float representing angle in degrees, 0 <= angle < 360
  37.        speed: positive float representing speed
  38.  
  39.        Returns: a Position object representing the new position.
  40.        """
  41.         old_x, old_y = self.getX(), self.getY()
  42.         # Compute the change in position
  43.         delta_y = speed * math.cos(math.radians(angle))
  44.         delta_x = speed * math.sin(math.radians(angle))
  45.         # Add that to the existing position
  46.         new_x = old_x + delta_x
  47.         new_y = old_y + delta_y
  48.         return Position(new_x, new_y)
  49.  
  50. # === Problems 1
  51.  
  52. class RectangularRoom(object):
  53.     """
  54.    A RectangularRoom represents a rectangular region containing clean or dirty
  55.    tiles.
  56.  
  57.    A room has a width and a height and contains (width * height) tiles. At any
  58.    particular time, each of these tiles is either clean or dirty.
  59.    """
  60.     def __init__(self, width, height):
  61.         """
  62.        Initializes a rectangular room with the specified width and height.
  63.  
  64.        Initially, no tiles in the room have been cleaned.
  65.  
  66.        width: an integer > 0
  67.        height: an integer > 0
  68.        """
  69.  
  70.         self.width = width
  71.         self.height = height
  72.         #create an empty list for clean tiles in this room
  73.         self.cleanTiles = []
  74.    
  75.     def cleanTileAtPosition(self, pos):
  76.         """
  77.        Mark the tile under the position POS as cleaned.
  78.  
  79.        Assumes that POS represents a valid position inside this room.
  80.  
  81.        pos: a Position
  82.        """
  83.         #add tile (as a tuple of integers) to the list of cleaned ones
  84.         #use int instead of round because that's how the tiles are numbered
  85.         self.cleanTiles.append((int(pos.getX()), int(pos.getY())))
  86.  
  87.     def isTileCleaned(self, m, n):
  88.         """
  89.        Return True if the tile (m, n) has been cleaned.
  90.  
  91.        Assumes that (m, n) represents a valid tile inside the room.
  92.  
  93.        m: an integer
  94.        n: an integer
  95.        returns: True if (m, n) is cleaned, False otherwise
  96.        """
  97.         #check the list of clean tiles
  98.         if (m, n) in self.cleanTiles:
  99.             return True
  100.         else:
  101.             return False
  102.    
  103.     def getNumTiles(self):
  104.         """
  105.        Return the total number of tiles in the room.
  106.  
  107.        returns: an integer
  108.        """
  109.         return self.width*self.height
  110.  
  111.     def getNumCleanedTiles(self):
  112.         """
  113.        Return the total number of clean tiles in the room.
  114.  
  115.        returns: an integer
  116.        """
  117.         cleanTiles = 0
  118.         #check all tiles in room
  119.         for i in range(self.width):
  120.             for j in range(self.height):
  121.                 if self.isTileCleaned(i, j):
  122.                     #tally clean tiles
  123.                     cleanTiles += 1
  124.         return cleanTiles
  125.  
  126.     def getRandomPosition(self):
  127.         """
  128.        Return a random position inside the room.
  129.  
  130.        returns: a Position object.
  131.        """
  132.         #because we're taking integer positions (e.g. on a tile), range for the
  133.         #randint function must be 0 to (measurement-1)
  134.         return Position(random.randint(0,self.width-1), random.randint(0,self.height-1))
  135.  
  136.     def isPositionInRoom(self, pos):
  137.         """
  138.        Return True if pos is inside the room.
  139.  
  140.        pos: a Position object.
  141.        returns: True if pos is in the room, False otherwise.
  142.        """
  143.         if (pos.getX() <= width) and (pos.getY() <= height):
  144.             return True
  145.         else:
  146.             return False
  147.        
  148.  
  149. class Robot(object):
  150.     """
  151.    Represents a robot cleaning a particular room.
  152.  
  153.    At all times the robot has a particular position and direction in the room.
  154.    The robot also has a fixed speed.
  155.  
  156.    Subclasses of Robot should provide movement strategies by implementing
  157.    updatePositionAndClean(), which simulates a single time-step.
  158.    """
  159.     def __init__(self, room, speed):
  160.         """
  161.        Initializes a Robot with the given speed in the specified room. The
  162.        robot initially has a random direction and a random position in the
  163.        room. The robot cleans the tile it is on.
  164.  
  165.        room:  a RectangularRoom object.
  166.        speed: a float (speed > 0)
  167.        """
  168.         self.room = room
  169.         self.speed = speed
  170.  
  171.     def getRobotPosition(self):
  172.         """
  173.        Return the position of the robot.
  174.  
  175.        returns: a Position object giving the robot's position.
  176.        """
  177.         return self.position
  178.    
  179.     def getRobotDirection(self):
  180.         """
  181.        Return the direction of the robot.
  182.  
  183.        returns: an integer d giving the direction of the robot as an angle in
  184.        degrees, 0 <= d < 360.
  185.        """
  186.         return self.dir
  187.  
  188.     def setRobotPosition(self, position):
  189.         """
  190.        Set the position of the robot to POSITION.
  191.  
  192.        position: a Position object.
  193.        """
  194.         self.position = position
  195.  
  196.     def setRobotDirection(self, direction):
  197.         """
  198.        Set the direction of the robot to DIRECTION.
  199.  
  200.        direction: integer representing an angle in degrees
  201.        """
  202.         self.dir = direction
  203.  
  204.     def updatePositionAndClean(self):
  205.         """
  206.        Simulate the raise passage of a single time-step.
  207.  
  208.        Move the robot to a new position and mark the tile it is on as having
  209.        been cleaned.
  210.        """
  211.         raise NotImplementedError
  212.  
  213.  
  214. # === Problem 2
  215. class StandardRobot(Robot):
  216.     """
  217.    A StandardRobot is a Robot with the standard movement strategy.
  218.  
  219.    At each time-step, a StandardRobot attempts to move in its current direction; when
  220.    it hits a wall, it chooses a new direction randomly.
  221.    """
  222.     def __init__(self ,room, speed):
  223.         Robot.__init__(self, room, speed)
  224.    
  225.     def updatePositionAndClean(self):
  226.         """
  227.        Simulate the passage of a single time-step.
  228.  
  229.        Move the robot to a new position and mark the tile it is on as having
  230.        been cleaned.
  231.        """
  232.         #NOTE:
  233.         #based on the coordinates of the example given at the beginning of
  234.         #the problem set, it seems that the robot cleans a tile each time it
  235.         #stops, not every tile it moves over; this reflects that,
  236.         #as does the code for RandomWalkRobot.
  237.  
  238.         #get original position, mark tile cleaned
  239.         pos1 = self.getRobotPosition()
  240.         self.room.cleanTileAtPosition(pos1)
  241.         pos2 = pos1.getNewPosition(self.dir, self.speed)
  242.         #test whether projected position is within room before moving
  243.         if pos2.getX() <= self.room.width and pos2.getX() >= 0 and \
  244.            pos2.getY() <= self.room.height and pos2.getY() >= 0:
  245.             self.setRobotPosition(pos2)
  246.         #if not, set a new random direction and try again
  247.         else:
  248.             self.setRobotDirection(random.randint(0,360))
  249.             self.updatePositionAndClean
  250.  
  251. # === Problem 3
  252.  
  253. def runSimulation(num_robots, speed, width, height, min_coverage, num_trials,
  254.                   robot_type):
  255.     """
  256.    Runs NUM_TRIALS trials of the simulation and returns the mean number of
  257.    time-steps needed to clean the fraction MIN_COVERAGE of the room.
  258.  
  259.    The simulation is run with NUM_ROBOTS robots of type ROBOT_TYPE, each with
  260.    speed SPEED, in a room of dimensions WIDTH x HEIGHT.
  261.  
  262.    num_robots: an int (num_robots > 0)
  263.    speed: a float (speed > 0)
  264.    width: an int (width > 0)
  265.    height: an int (height > 0)
  266.    min_coverage: a float (0 <= min_coverage <= 1.0)
  267.    num_trials: an int (num_trials > 0)
  268.    robot_type: class of robot to be instantiated (e.g. Robot or
  269.                RandomWalkRobot)
  270.    """
  271.    
  272.  
  273.     timeStepsNeeded = 0
  274.     times = []
  275.     for trial in xrange(0,num_trials):
  276.         #create new set of robots for each trial
  277.         robotlist = []
  278.         ###anim = ps6_visualize.RobotVisualization(num_robots, width, height)
  279.         #create new room for each trial
  280.         room = RectangularRoom(width, height)
  281.         #create num_robots robots, add to list
  282.         for i in xrange(num_robots):
  283.             robot = robot_type(room, speed)
  284.             robot.setRobotPosition(room.getRandomPosition())
  285.             robot.setRobotDirection(random.randint(0,360))
  286.             robotlist.append(robot)
  287.         #go until minimum coverage is reached
  288.         while (float(room.getNumCleanedTiles())/room.getNumTiles() < min_coverage):
  289.             #move each robot
  290.             for i in xrange(len(robotlist)):
  291.                 robotlist[i].updatePositionAndClean()
  292.             #increment total time needed; doesn't need to be reset
  293.             timeStepsNeeded += 1
  294.             ###anim.update(room, robotlist)
  295.     #return average
  296.     return (timeStepsNeeded/num_trials)
  297.  
  298.  
  299.            
  300.  
  301. # === Problem 4
  302. #
  303. # 1) How long does it take 1, 2, 3, 4, and 5 robots to clean 80% of a
  304. #    20x20 room?
  305. #
  306. # 2) Howlong does it take two robots to clean 80% of rooms with
  307. #    dimensions 20x20, 25x16, 50x8, 80x5, and 100x4?
  308.  
  309. def showPlot1():
  310.     """
  311.    Produces a plot showing dependence of cleaning time on number of robots.
  312.    """
  313.     #make lists for average times and number of robots
  314.     times = []
  315.     num_robots = [1,2,3,4,5,6,7,8,9,10]
  316.     #find the average time for each number of robots
  317.     for number in num_robots:
  318.         average = runSimulation(number, 1.0, 20, 20, 0.8, 10, StandardRobot)
  319.         times.append(average)
  320.     #plot times with regard to number of robots
  321.     pylab.plot(num_robots, times)
  322.     pylab.title('Time to Clean 80% of a 20x20 Room')
  323.     pylab.xlabel('Number of Robots')
  324.     pylab.ylabel('Average Cleaning Time')
  325.     pylab.show()
  326.  
  327. def showPlot2():
  328.     """
  329.    Produces a plot showing dependence of cleaning time on room shape.
  330.    """
  331.     #make lists for average times, dimensions of rooms, and width:length ratios
  332.     times = []
  333.     widths = [20, 25, 40, 50, 80, 100]
  334.     lengths = [20, 16, 10, 8, 5, 4]
  335.     ratios = []
  336.     #run simulation on each room
  337.     for i in xrange(len(widths)):
  338.         average = runSimulation(2, 1.0, widths[i], lengths[i], 0.8, 10, StandardRobot)
  339.         times.append(average)
  340.         ratios.append(float(widths[i])/lengths[i])
  341.     #plot times with regard to width:length ratios
  342.     pylab.plot(ratios, times)
  343.     pylab.title('Time for 2 Robots to Clean 80% of a Room (area 400 units)')
  344.     pylab.xlabel('Ratio of Width to Length')
  345.     pylab.ylabel('Average Cleaning Time')
  346.     pylab.show()
  347.  
  348.  
  349. # === Problem 5
  350.  
  351. class RandomWalkRobot(Robot):
  352.     """
  353.    A RandomWalkRobot is a robot with the "random walk" movement strategy: it
  354.    chooses a new direction at random after each time-step.
  355.    """
  356.     def __init__(self, room, speed):
  357.         Robot.__init__(self, room, speed)
  358.    
  359.     def updatePositionAndClean(self):
  360.         #get original position, mark tile cleaned
  361.         pos1 = self.getRobotPosition()
  362.         #get a new direction
  363.         self.setRobotDirection(random.randint(0,360))
  364.         self.room.cleanTileAtPosition(pos1)
  365.         pos2 = pos1.getNewPosition(self.dir, self.speed)
  366.         #test whether projected position is within room before moving
  367.         if pos2.getX() <= self.room.width and pos2.getX() >= 0 and \
  368.            pos2.getY() <= self.room.height and pos2.getY() >= 0:
  369.             self.setRobotPosition(pos2)
  370.         #if not, set a new random direction and try again
  371.         else:
  372.             self.setRobotDirection(random.randint(0,360))
  373.             self.updatePositionAndClean
  374.  
  375.  
  376. # === Problem 6
  377.  
  378. # For the parameters tested below (cleaning 80% of a 20x20 square room),
  379. # RandomWalkRobots take approximately twice as long to clean the same room as
  380. # StandardRobots do.
  381. def showPlot3():
  382.     """
  383.    Produces a plot comparing the two robot strategies.
  384.    """
  385.     #make lists for average times and number of robots
  386.     StandardTimes = []
  387.     RandomWalkTimes = []
  388.     num_robots = [1,2,3,4,5,6,7,8,9,10]
  389.     #find the average time for each number of robots
  390.     for number in num_robots:
  391.         average = runSimulation(number, 1.0, 20, 20, 0.8, 10, StandardRobot)
  392.         StandardTimes.append(average)
  393.         average = runSimulation(number, 1.0, 20, 20, 0.8, 10, RandomWalkRobot)
  394.         RandomWalkTimes.append(average)
  395.     #plot times with regard to number of robots
  396.     pylab.plot(num_robots, StandardTimes)
  397.     pylab.plot(num_robots, RandomWalkTimes)
  398.     pylab.title('RandomWalkRobot vs. StandardRobot')
  399.     pylab.xlabel('Number of Robots')
  400.     pylab.ylabel('Time to Clean 20x20 Room')
  401.     pylab.legend('StandardRobot', 'RandomWalkRobot')
  402.     pylab.show()
  403.  
  404.     #Unsurprisingly, the RandomWalkRobots consistently take significantly longer
  405.     #to satisfactorily clean the room than do the StandardRobots.
  406.     #At lower values of min_coverage, the gap between times is not as large;
  407.     #this is probably also true for less square rooms.
Advertisement
Add Comment
Please, Sign In to add comment