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

  • #1
fluidistic
Gold Member
3,749
133
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:
# 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
  tiles.

  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
  else:
  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
  else:
  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)
  self.room.cleanTileAtPosition(self.position)

  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
  randomly.
  """
  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
  self.setRobotPosition(newPosition)
  self.room.cleanTileAtPosition(newPosition)

# 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,
  robot_type):
  """
  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
  RandomWalkRobot)
  """
  results = []
  for trial in range(num_trials):   
  anim = ps2_visualize.RobotVisualization(num_robots, width, height)  # Animation
  time_counter=0   
  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:
  robot.updatePositionAndClean()
  coverage = float(room.getNumCleanedTiles()) / float(room.getNumTiles())
  time_counter +=1
  if coverage == min_coverage:
  break
  results.append(time_counter)
  # 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)
 

Answers and Replies

  • #2
12,499
6,288
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.
 
  • Like
Likes fluidistic
  • #3
fluidistic
Gold Member
3,749
133
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.
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"...
 

Related Threads on MITx edx 6.00.2x python 2 code, need to fix a bug

  • Last Post
Replies
11
Views
305
  • Last Post
Replies
3
Views
1K
Replies
2
Views
1K
  • Last Post
Replies
11
Views
1K
Replies
2
Views
5K
Replies
4
Views
1K
Replies
6
Views
1K
Replies
14
Views
2K
Replies
3
Views
2K
Replies
1
Views
2K
Top