Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Python MITx edx 6.00.2x python 2 code, need to fix a bug

  1. Apr 17, 2016 #1


    User Avatar
    Gold Member

    Hi people,
    I've "enrolled" into the edx MITx 6.00.2x course on python and data science, but I don't plan to get a certificate and I started so late that all the problems I'm facing aren't due anymore so I think this gives me the right to seek help here.
    Basically the task is to simulate some different kinds of robots that clean an mxn room and perform some statistics to gather information on how fast they are at cleaning the room, in order to pick the best strategy. That is, each kind of bot has a different cleaning strategy.
    The whole code comes with a skeletton and we must fill in the blank. So far my code runs without error (I got inspired/helped a lot by other's code that I found on github), but I noticed a bug.

    Basically the bots have a speed. If I increase the speed say from 1.0 to 5.0, the bots indeed move farther away within 1 time_counter, as it should. The problem is that with my current code, the bots only clean their current tile (i.e. position in room) and the tile they land on. With speed = 1.0 this causes no problem, but with speed 5.0 this means that the bots don't clean all the intermediary tiles between their current position and the one they will land on within the time_counter! I don't really see an easy fix to my code but I'm sure there are some... Any help is appreciated. Here's the code:
    Code (Python):

    # 6.00.2x Problem Set 2: Simulating robots

    import math
    import random

    import ps2_visualize
    import pylab

    # For Python 2.7:
    from ps2_verify_movement27 import testRobotMovement

    # If you get a "Bad magic number" ImportError, you are not using
    # Python 2.7 and using most likely Python 2.6:

    # === Provided class Position
    class Position(object):
      A Position represents a location in a two-dimensional room.

      def __init__(self, x, y):
      Initializes a position with coordinates (x, y).

      self.x = x
      self.y = y
      def getX(self):
      return self.x
      def getY(self):
      return self.y
      def getNewPosition(self, angle, speed):
      Computes and returns the new Position after a single clock-tick has
      passed, with this object as the current position, and with the
      specified angle and speed.

      Does NOT test whether the returned position fits inside the room.

      angle: number representing angle in degrees, 0 <= angle < 360
      speed: positive float representing speed

      Returns: a Position object representing the new position.

      old_x, old_y = self.getX(), self.getY()
      angle = float(angle)
      # Compute the change in position
      delta_y = speed * math.cos(math.radians(angle))
      delta_x = speed * math.sin(math.radians(angle))
      # Add that to the existing position
      new_x = old_x + delta_x
      new_y = old_y + delta_y
      return Position(new_x, new_y)

      def __str__(self):  
      return "(%0.2f, %0.2f)" % (self.x, self.y)

    # === Problem 1
    class RectangularRoom(object):
      A RectangularRoom represents a rectangular region containing clean or dirty

      A room has a width and a height and contains (width * height) tiles. At any
      particular time, each of these tiles is either clean or dirty.

      DIRTY = 0
      CLEAN = 1
      def __init__(self, width, height):
      Initializes a rectangular room with the specified width and height.

      Initially, no tiles in the room have been cleaned.

      width: an integer > 0
      height: an integer > 0

      self.width = width
      self.height = height
      self.room = [[RectangularRoom.DIRTY for horizontal in range(height)] for vertical in range(width)]
      # Or:
      #self.room = []
      #for vertical in range(height):
      #  inner_list = []
      #  for horizontal in range(width):
      #  inner_list.append(RectangularRoom.DIRTY)
      #  self.room.append(inner_list)

      def cleanTileAtPosition(self, pos):
      Mark the tile under the position POS as cleaned.

      Assumes that POS represents a valid position inside this room.

      pos: a Position

      x = int(pos.x)
      y = int(pos.y)
      self.room[x][y] = RectangularRoom.CLEAN

      def isTileCleaned(self, m, n):
      Return True if the tile (m, n) has been cleaned.

      Assumes that (m, n) represents a valid tile inside the room.

      m: an integer
      n: an integer
      returns: True if (m, n) is cleaned, False otherwise

      if self.room[m][n] == RectangularRoom.DIRTY:
      return False
      return True
      def getNumTiles(self):
      Return the total number of tiles in the room.

      returns: an integer

      return self.width * self.height

      def getNumCleanedTiles(self):
      Return the total number of clean tiles in the room.

      returns: an integer

      CleanTileCounter = 0
      for horizontal in self.room:
      for h in horizontal:
      if h == RectangularRoom.CLEAN:
      CleanTileCounter += 1
      return CleanTileCounter

      def getRandomPosition(self):
      Return a random position inside the room.

      returns: a Position object.

      x = random.randint(0, self.width - 1)
      y = random.randint(0, self.height - 1)
      return Position(x,y)

      def isPositionInRoom(self, pos):
      Return True if pos is inside the room.

      pos: a Position object.
      returns: True if pos is in the room, False otherwise.

      x = pos.getX()
      y = pos.getY()
      if x >= 0 and x < self.width and y >= 0 and y < self.height:
      return True
      return False

    class Robot(object):
      Represents a robot cleaning a particular room.

      At all times the robot has a particular position and direction in the room.
      The robot also has a fixed speed.

      Subclasses of Robot should provide movement strategies by implementing
      updatePositionAndClean(), which simulates a single time-step.

      def __init__(self, room, speed):
      Initializes a Robot with the given speed in the specified room. The
      robot initially has a random direction and a random position in the
      room. The robot cleans the tile it is on.

      room:  a RectangularRoom object.
      speed: a float (speed > 0)

      self.room = room
      self.speed = speed
      self.position = room.getRandomPosition()
      self.direction = random.randint(0,360-1)

      def getRobotPosition(self):
      Return the position of the robot.

      returns: a Position object giving the robot's position.

      return self.position # I don't understand why this doesn't return a random position, by the way it's defined above
      def getRobotDirection(self):
      Return the direction of the robot.

      returns: an integer d giving the direction of the robot as an angle in
      degrees, 0 <= d < 360.

      return self.direction  # Idem than above

      def setRobotPosition(self, position):
      Set the position of the robot to POSITION.

      position: a Position object.

      self.position = position  # I don't understand why it's not POSITION = self.position

      def setRobotDirection(self, direction):
      Set the direction of the robot to DIRECTION.

      direction: integer representing an angle in degrees

      self.direction = direction

      def updatePositionAndClean(self):
      Simulate the raise passage of a single time-step.

      Move the robot to a new position and mark the tile it is on as having
      been cleaned.

      raise NotImplementedError # don't change this!

    # === Problem 2
    class StandardRobot(Robot):
      A StandardRobot is a Robot with the standard movement strategy.

      At each time-step, a StandardRobot attempts to move in its current
      direction; when it would hit a wall, it *instead* chooses a new direction

      def updatePositionAndClean(self):
      Simulate the raise passage of a single time-step.

      Move the robot to a new position and mark the tile it is on as having
      been cleaned.

      newPosition = self.position.getNewPosition(self.direction , self.speed)
      #i If the robot hits the wall and newPosition is outside the room, we choose a random direction and get new position until it fits in the room
      while not self.room.isPositionInRoom(newPosition):
      self.direction = random.randint(1, 360 - 1)
      newPosition = self.position.getNewPosition(self.direction , self.speed)
      #The robot is now inside the room, so we pick that new position as valid and we clean the tile

    # Uncomment this line to see your implementation of StandardRobot in action!
    #testRobotMovement(StandardRobot, RectangularRoom)

    # === Problem 3
    def runSimulation(num_robots, speed, width, height, min_coverage, num_trials,
      Runs NUM_TRIALS trials of the simulation and returns the mean number of
      time-steps needed to clean the fraction MIN_COVERAGE of the room.

      The simulation is run with NUM_ROBOTS robots of type ROBOT_TYPE, each with
      speed SPEED, in a room of dimensions WIDTH x HEIGHT.

      num_robots: an int (num_robots > 0)
      speed: a float (speed > 0)
      width: an int (width > 0)
      height: an int (height > 0)
      min_coverage: a float (0 <= min_coverage <= 1.0)
      num_trials: an int (num_trials > 0)
      robot_type: class of robot to be instantiated (e.g. StandardRobot or

      results = []
      for trial in range(num_trials):  
      anim = ps2_visualize.RobotVisualization(num_robots, width, height)  # Animation
      room = RectangularRoom(width, height)
      coverage = room.getNumCleanedTiles() / float(room.getNumTiles())
      robots = [robot_type(room, speed) for robot in range(num_robots)]
      while coverage < min_coverage:
      anim.update(room, robots)  # Animation
      for robot in robots:
      coverage = float(room.getNumCleanedTiles()) / float(room.getNumTiles())
      time_counter +=1
      if coverage == min_coverage:
      # Now I make the average of that list, to get a feeling on how good/bad is the bot
      anim.done()  #Animation
      return sum(results) / float(len(results))
    # Uncomment this line to see how much your simulation takes on average
    print  runSimulation(1, 3.0, 10, 10, 0.9, 100, StandardRobot)
  2. jcsd
  3. Apr 18, 2016 #2


    Staff: Mentor

    why not run the code with the 1 unit counter scheme and then show stuff every mod 5 == 0 of the counter.

    like having a fine counter and a coarse counter.
  4. Apr 24, 2016 #3


    User Avatar
    Gold Member

    That would have been a good idea, but I have been told that it's a feature and not a bug. And that I should think about speed as "step size rather than velocity"...
Share this great discussion with others via Reddit, Google+, Twitter, or Facebook

Have something to add?
Draft saved Draft deleted