Stop learning alone!

Learn faster and stay on-track by joining this free class with other self-learners.

Register for MIT OpenCourseWare 6.00 Introduction to Computer Science and Programming now.

MIT OpenCourseWare 6.00 Introduction to Computer Science and Programming

Class length: 24 weeks. Start anytime.

Creator: duallain

Status: Established

Join this class!

Lesson 19: Assignment 1: PS 11

Homework Submissions

8 total

tuckertuck (Self-grade: Pretty good)
Submitted 8 months ago | Permalink | Time spent: 3 hours
# Problem Set 11: Simulating robots
# Name:
# Collaborators:
# Time:

import math, random, pylab
import ps11_visualize

# === Provided classes

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).

        x: a real number indicating the x-coordinate
        y: a real number indicating the y-coordinate
        """
        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: integer 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()
        # 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)


# === Problems 1 and 2

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.
    """
    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 = int(width)
        self.height = int(height)
        self.tiles = []
        self.cleaned = []
        for h in range(0,height):
            for w in range(0,width):
                self.tiles.append((w,h))
    
    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
        """
        self.cleaned.append((int(pos.getX()),int(pos.getY())))
    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
        """
        return (m,n) in self.cleaned
    def getNumTiles(self):
        """
        Return the total number of tiles in the room.

        returns: an integer
        """
        return len(self.tiles)
    def getNumCleanedTiles(self):
        """
        Return the total number of clean tiles in the room.

        returns: an integer
        """
        return len(self.cleaned)
    def getRandomPosition(self):
        """
        Return a random position inside the room.

        returns: a Position object.
        """
        return Position(random.randint(0,self.width),random.randint(0,self.height))
    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.
        """
        return 0 <= pos.getX() < self.width and 0 <= pos.getY() < self.height


class BaseRobot(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 BaseRobot 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 d and a
        random position p in the room.

        The direction d is an integer satisfying 0 <= d < 360; it
        specifies an angle in degrees.

        p is a Position object giving the robot's position.

        room:  a RectangularRoom object.
        speed: a float (speed > 0)
        """
        self.room = room
        self.speed = float(speed)
        self.p = self.room.getRandomPosition()
        self.d = random.randint(0,359)
    def getRobotPosition(self):
        """
        Return the position of the robot.

        returns: a Position object giving the robot's position.
        """
        return self.p
    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.d
    def setRobotPosition(self, position):
        """
        Set the position of the robot to POSITION.

        position: a Position object.
        """
        self.p = position
    def setRobotDirection(self, direction):
        """
        Set the direction of the robot to DIRECTION.

        direction: integer representing an angle in degrees
        """
        self.d = direction


class Robot(BaseRobot):
    """
    A Robot is a BaseRobot with the standard movement strategy.

    At each time-step, a Robot attempts to move in its current
    direction; when it hits a wall, it chooses a new direction
    randomly.
    """
    def updatePositionAndClean(self):
        """
        Simulate the passage of a single time-step.

        Move the robot to a new position and mark the tile it is on as having
        been cleaned.
        """
        moving = True
        while moving:
            new_p = self.p.getNewPosition(self.d,self.speed)
            if self.room.isPositionInRoom(new_p):
                self.setRobotPosition(new_p)
                #print new_p.getX(), new_p.getY()
                if not self.room.isTileCleaned(int(new_p.getX()),int(new_p.getY())):
                    self.room.cleanTileAtPosition(new_p)
                    #print 'cleaned'
                moving = False
            else:
                #print 'new direction'
                self.setRobotDirection(random.randint(0,359))
                continue

# Testing Problem 1 and 2

##room = RectangularRoom(5,5)
##robby = Robot(room,1)
##robby.updatePositionAndClean()
##robby.updatePositionAndClean()
##robby.updatePositionAndClean()
##print room.getNumCleanedTiles()/float(room.getNumTiles())
            
# === Problem 3

def runSimulation(num_robots, speed, width, height, min_coverage, num_trials,
                  robot_type, visualize):
    """
    Runs NUM_TRIALS trials of the simulation and returns a list of
    lists, one per trial. The list for a trial has an element for each
    timestep of that trial, the value of which is the percentage of
    the room that is clean after that timestep. Each trial stops when
    MIN_COVERAGE of the room is clean.

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

    Visualization is turned on when boolean VISUALIZE is set to True.

    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. Robot or
                RandomWalkRobot)
    visualize: a boolean (True to turn on visualization)
    """
    results = []
    
    for i in xrange(0,num_trials):
        coverage = 0
        trial = []
        robots = []
        room = RectangularRoom(width,height)
        for n in xrange(0,num_robots):
            robots.append(robot_type(room, speed))

        if visualize:
            anim = ps11_visualize.RobotVisualization(num_robots, width, height)
        # Simulate a trial
        while coverage < min_coverage:
            for robot in robots:
                robot.updatePositionAndClean()
            if visualize:
                anim.update(room, robots)
            coverage = room.getNumCleanedTiles()/float(room.getNumTiles())
            trial.append(coverage)
            
        if visualize:
            anim.done()
        results.append(trial)
    return results    
    
# Testing Problem 3
            
#avg = runSimulation(1, 1.0, 5, 5, 1.0, 5, Robot, True)

# === Provided function
def computeMeans(list_of_lists):
    """
    Returns a list as long as the longest list in LIST_OF_LISTS, where
    the value at index i is the average of the values at index i in
    all of LIST_OF_LISTS' lists.

    Lists shorter than the longest list are padded with their final
    value to be the same length.
    """
    # Find length of longest list
    longest = 0
    for lst in list_of_lists:
        if len(lst) > longest:
           longest = len(lst)
    # Get totals
    tots = [0]*(longest)
    for lst in list_of_lists:
        for i in range(longest):
            if i < len(lst):
                tots[i] += lst[i]
            else:
                tots[i] += lst[-1]
    # Convert tots to an array to make averaging across each index easier
    tots = pylab.array(tots)
    # Compute means
    means = tots/float(len(list_of_lists))
    return means

# === Problem 4
def showPlot1():
    """
    Produces a plot showing dependence of cleaning time on room size.
    """
    num_robots = 1
    speed = 1.0
    min_coverage = 0.75
    num_trials = 100
    robot_type = Robot
    visualize = False
    rooms = [5,10,15,20,25]
    means = []
    area = []
    for i in range(len(rooms)):
        width = rooms[i]
        height = rooms[i]
        sim = runSimulation(num_robots, speed, width, height, min_coverage, num_trials, robot_type, visualize)
        avg = plotHelper(sim)
        area.append(width*height)
        means.append(avg)

    pylab.figure()
    pylab.plot(area, means)
    pylab.ylabel('Timesteps')
    pylab.xlabel('Room area')
    pylab.title('Time to clean %d percent of a square room of various areas with %d robots (%d trials)' % (min_coverage*100, num_robots, num_trials))
    
def showPlot2():
    """
    Produces a plot showing dependence of cleaning time on number of robots.
    """
    num_robots = range(1,11)
    speed = 1.0
    min_coverage = 0.75
    num_trials = 100
    robot_type = Robot
    visualize = False
    width, height = (25, 25)
    means = []
    for bot in num_robots:        
        sim = runSimulation(bot, speed, width, height, min_coverage, num_trials, robot_type, visualize)
        avg = plotHelper(sim)
        means.append(avg)

    pylab.figure()
    pylab.plot(num_robots, means)
    pylab.ylabel('Timesteps')
    pylab.xlabel('Number of Robots')
    pylab.title('Time to clean %d percent of a 25x25 room with multiple robots (%d trials)' % (min_coverage*100, num_trials))
    
def showPlot3():
    """
    Produces a plot showing dependence of cleaning time on room shape.
    """
    num_robots = 2
    speed = 1.0
    min_coverage = 0.75
    num_trials = 100
    robot_type = Robot
    visualize = False
    rooms = [(20,20),(25,16),(40,10),(50,8),(80,5),(100,4)]
    means = []
    ratio = []
    for i in range(len(rooms)):
        width, height = rooms[i]
        sim = runSimulation(num_robots, speed, width, height, min_coverage, num_trials, robot_type, visualize)
        avg = plotHelper(sim)
        ratio.append(width/float(height))
        means.append(avg)
    pylab.figure()
    pylab.plot(ratio, means)
    pylab.axis([0,25,0, 400])
    pylab.ylabel('Timesteps')
    pylab.xlabel('Ratio of Width to Height')
    pylab.title('Time to clean %d percent of a rectangular room with %d robots (%d trials)' % (min_coverage*100, num_robots, num_trials))
    

def showPlot4():
    """
    Produces a plot showing cleaning time vs. percentage cleaned, for
    each of 1-5 robots.
    """
    num_robots = range(1,6)
    speed = 1.0
    min_coverage = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]
    num_trials = 10
    robot_type = Robot
    visualize = False
    width, height = (25, 25)
    
    pylab.figure()
    
    for bot in num_robots:        
        means = []
        for percent in min_coverage:
            sim = runSimulation(bot, speed, width, height, percent, num_trials, robot_type, visualize)
            avg = plotHelper(sim)
            means.append(avg)
   
        pylab.plot(min_coverage, means, label='%d robots' % bot)
    pylab.legend(loc=2)
    pylab.ylabel('Timesteps')
    pylab.xlabel('Minimum Coverage')
    pylab.title('Time to clean a 25x25 room with 1 to 5 robots (%d trials)' % num_trials)
    
# Testing Problem 4

def plotHelper(list_of_lists):
    """
    Returns the average length of all the lists of lists.
    """
    listCounter = 0
    for list in list_of_lists:
        listCounter += len(list)
    return listCounter/float(len(list_of_lists))

showPlot1()
showPlot2()
showPlot3()
showPlot4()
pylab.show()
    

# === Problem 5

class RandomWalkRobot(BaseRobot):
    """
    A RandomWalkRobot is a robot with the "random walk" movement
    strategy: it chooses a new direction at random after each
    time-step.
    """
    def updatePositionAndClean(self):
        """
        Simulate the passage of a single time-step.

        Move the robot to a new position and mark the tile it is on as having
        been cleaned.
        """
        moving = True
        while moving:
            new_p = self.p.getNewPosition(self.d,self.speed)
            if self.room.isPositionInRoom(new_p):
                self.setRobotPosition(new_p)
                if not self.room.isTileCleaned(int(new_p.getX()),int(new_p.getY())):
                    self.room.cleanTileAtPosition(new_p)
                moving = False
            self.setRobotDirection(random.randint(0,359))


# Testing Problem 5
#avg = runSimulation(3, 1.0, 5, 5, 0.8, 1, RandomWalkRobot, True)


# === Problem 6

def showPlot5():
    """
    Produces a plot comparing the two robot strategies.
    """
    num_robots = 3
    speed = 1.0
    min_coverage = 0.75
    num_trials = 100
    robot_type = [Robot, RandomWalkRobot]
    visualize = False
    rooms = [5,10,15,20,25]
    pylab.figure()
    
    for botType in robot_type:
        means = []
        area = []
        for i in range(len(rooms)):
            width = rooms[i]
            height = rooms[i]
            sim = runSimulation(num_robots, speed, width, height, min_coverage, num_trials, botType, visualize)
            avg = plotHelper(sim)
            area.append(width*height)
            means.append(avg)
        if botType is Robot:
            label = 'Robot'
        else: label = 'RandomWalkRobot'
        pylab.plot(area, means, label=label)
    pylab.legend(loc=2)
    pylab.ylabel('Timesteps')
    pylab.xlabel('Area')
    pylab.title('Performance comparison of Robot and RandomWalkRobot clearing %d percent of various size rooms (%d trials)' % (min_coverage*100, num_trials))
    pylab.show()

#Testing Problem 6
#showPlot5()

# Observations for Problem 6:
# RobotWalkRandom() is always slower than Robot(), the difference in performance is more obvious
# with 1 robot and a large room and high minimum coverage.
cmlilley (Self-grade: Pretty good)
Submitted 9 months ago | Permalink | Time spent: 1 minute
# Problem Set 11: Simulating robots
# Name:
# Collaborators:
# Time:

import math, random, pylab

import ps11_visualize

# === Provided classes

class Position(object):
    """
    A Position represents a location in a two-dimensional room.
    """
    def __init__(self, x, y):
        self.x = x
        self.y = y
        
    def __str__(self):
        return "x-coord=" + str(self.x) + " & y-coord=" + str(self.y)
        
    def getX(self):
        return self.x
        
    def getY(self):
        return self.y
        
    def convertPosToTile(self):
        tileX = int(self.x)
        tileY = int(self.y)
        self.tile = (tileX, tileY)
        
        return self.tile
        
    def getNewPosition(self, angle, speed):
        old_x, old_y = self.getX(), self.getY()
        # 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)


# === Problems 1 and 2

class RectangularRoom(object):
    def __init__(self, width, height):
        self.w = width
        self.h = height
        
        self.cleantiles = []
        self.dirtytiles = []
        for w in range (width):
            for h in range (height):
                self.dirtytiles.append ((w,h))

    def __str__(self):
        return "This RectangularRoom contains the following dirty tiles: \n" + str(self.dirtytiles) + "\n And also the following clean tiles: \n" + str(self.cleantiles)
        
    def cleanTileAtPosition(self, tile):        
        if tile[0] >= self.w or tile[1] >= self.h:
            raise ValueError ("RectangularRoom.cleanTileAtPosition called with a position not in the room.")
        
        self.cleantiles.append (tile)
        self.dirtytiles.remove (tile)
        
    def isTileCleaned(self, m,n):
        tile = (m,n)
        return tile in self.cleantiles
        
    def getNumTiles(self):
        return int(self.w*self.h)
        
    def getNumCleanedTiles(self):
        return len(self.cleantiles)
        
    def getRandomPosition(self):
        return Position(random.triangular(0, self.w), random.triangular(0, self.h))
        
    def isPositionInRoom(self, pos):
        return (0 <= pos.x < (self.w)) and (0<= pos.y < (self.h))


class BaseRobot(object):
    def __init__(self, room, speed):
        self.speed = speed
        self.room = room
        self.d = random.randint(0,359)
        self.p = self.room.getRandomPosition()
        
        
    def getRobotPosition(self):
        return self.p
        
    def getRobotDirection(self):
        return self.d
        
    def setRobotPosition(self, position):
        self.p = position
        
    def setRobotDirection(self, direction):
        self.d = direction


class Robot(BaseRobot):
    def updatePositionAndClean(self):
        thinking = True
        while thinking:
            self.testp = self.p.getNewPosition(self.d, self.speed)
            if self.room.isPositionInRoom(self.testp) == True:
                thinking = False
            else: 
                self.d = random.randint(0,359)
        
        self.p = self.testp
        tile = self.testp.convertPosToTile()
        if not self.room.isTileCleaned(tile[0], tile[1]):
            self.room.cleanTileAtPosition(tile)
        
# === Problem 5

class RandomWalkRobot(BaseRobot):

    def updatePositionAndClean(self):

        thinking = True
        while thinking:
            self.d = random.randint(0,359)
            self.testp = self.p.getNewPosition(self.d, self.speed)
            if self.room.isPositionInRoom(self.testp) == True:
                thinking = False
        
        self.p = self.testp
        tile = self.testp.convertPosToTile()
        if not self.room.isTileCleaned(tile[0], tile[1]):
            self.room.cleanTileAtPosition(tile)


# === Problem 3

def runSimulation(num_robots, speed, width, height, min_coverage, num_trials,robot_type, visualize):
    iTrial = 0											#index of active trial, starts at 0
    trialsList = []
    incomplete = True
    
    while incomplete:
    	trialsList.append (runTrial (num_robots, speed, width, height, min_coverage, robot_type, visualize))
    	if iTrial+1 == num_trials:
    		incomplete = False
    	else:	
    		iTrial += 1
    return trialsList
        
def runTrial (num_robots, speed, width, height, min_coverage, robot_type, visualize):

    trialRoom = RectangularRoom(width, height)
    trialRoomSize = trialRoom.getNumTiles()
    trialSteps = []
    allRobots = []
    for i in range(num_robots):
        allRobots.append(robot_type(trialRoom, speed))
    dirty = True
    # Visualization Code:
    if visualize:
        anim = ps11_visualize.RobotVisualization(num_robots, width, height, .01)

    while dirty:
    	iRobot = 0	
        while iRobot != len(allRobots):
			allRobots[iRobot].updatePositionAndClean()
			iRobot += 1
        if visualize:
            anim.update(trialRoom, allRobots)
        numCleaned = trialRoom.getNumCleanedTiles()
        pctCleaned = float(numCleaned)/float(trialRoomSize)
        trialSteps.append(pctCleaned)
        if pctCleaned >= min_coverage:
            dirty = False
    if visualize:
        anim.done()
    return trialSteps
    

# === Provided function ===
def computeMeans(list_of_lists):
    """
    Returns a list as long as the longest list in LIST_OF_LISTS, where
    the value at index i is the average of the values at index i in
    all of LIST_OF_LISTS' lists.

    Lists shorter than the longest list are padded with their final
    value to be the same length.
    """
    # Find length of longest list
    longest = 0
    for lst in list_of_lists:
        if len(lst) > longest:
           longest = len(lst)
    # Get totals
    tots = [0]*(longest)
    for lst in list_of_lists:
        for i in range(longest):
            if i < len(lst):
                tots[i] += lst[i]
            else:
                tots[i] += lst[-1]
    # Convert tots to an array to make averaging across each index easier
    tots = pylab.array(tots)
    # Compute means
    means = tots/float(len(list_of_lists))
    return means

# === My Own Helper Function ===
def computeAvgLens(listList):
	"""Given a list of lists, returns the average length of the lists (as a proxy for time elapsed)"""
	tot = 0
	for i in range(len(listList)):
		tot += len(listList[i])
	avg = tot/len(listList)
	return avg


# === Problem 4
def showPlot1():
    """
    Produces a plot showing dependence of cleaning time on room size.
    """
    widths = [5, 10, 15, 20, 25]
    areas = [5**2, 10**2, 15**2, 20**2, 25**2]
    times = []
    for i in widths:
        times.append(computeAvgLens(runSimulation (1, 1.0, i, i, 0.75, 30, RandomWalkRobot, False)))
    
    
    pylab.figure()
    pylab.plot(areas, times, '-ro')
    pylab.axis([0, 650, 0, 3000])  #RandomWalkRobot needs 3 times as long
    pylab.ylabel ('time to clean')
    pylab.xlabel ('areas of room')
    pylab.title ('Room Area vs. 1 Robot Time To Clean')
    
    pylab.show()
# showPlot1()

def showPlot2():
    """
    Produces a plot showing dependence of cleaning time on number of robots.
    """
    number = [1,2,3,4,5,6,7,8,9,10]
    times = []
    for i in number:
        times.append(computeAvgLens(runSimulation (i, 1.0, 25, 25, 0.75, 30, Robot, False)))
    pylab.figure()
    pylab.plot(number, times, '-ro')
    pylab.axis([0, 11, 0, 1000]) 
    pylab.ylabel ('time to clean')
    pylab.xlabel ('number of robots')
    pylab.title ('# of Robots vs. Time To Clean 75% of 25x25 rm')
    
    pylab.show()
    
# showPlot2()

def showPlot3():
    """
    Produces a plot showing dependence of cleaning time on room shape.
    """
    widths = [20, 25, 40, 50, 80, 100]
    heights = [20, 16, 10, 8, 5, 4]
    ratios = [float(widths[i])/float(heights[i]) for i in range(len(widths))]
    times = []
    for i in range (len(widths)):
        times.append(computeAvgLens(runSimulation (1, 1.0, widths[i], heights[i], 0.75, 30, Robot, False)))
    pylab.figure()
    pylab.plot(ratios, times, '-ro')
    pylab.axis([0, 30, 580, 800]) 
    pylab.ylabel ('time to clean')
    pylab.xlabel ('ratio')
    pylab.title ('ratio of width to height vs. Time To Clean 75% of rm')
    
    pylab.show()
# showPlot3()

def showPlot4A():
    """
    Produces a plot showing dependence of cleaning time on required coverage.
    """
    coverages = [.50, .60, .70, .80, .90, .97, .98, .99, 1.0]
    times = []
    for i in coverages:
        times.append(computeAvgLens(runSimulation (5, 1.0, 25,25, i, 30, Robot, False)))
    pylab.figure()
    pylab.plot(coverages, times, '-ro')
    pylab.axis([0.45, 1.05, 0, 6000]) 
    pylab.ylabel ('time to clean (ticks)')
    pylab.xlabel ('required coverage (%)')
    pylab.title ('Coverage Requirements vs. Time To Clean for 1 robot')
    
    pylab.show()
# showPlot4A()


def showPlot4():
    """
    Produces a plot showing cleaning time vs. percentage cleaned, for
    each of 1-5 robots.
    """
    numRobots = [1,2,3,4,5]
    pcts = []
    for i in range (1,6):
        pcts.append (computeMeans(runSimulation (i, 1.0, 25, 25, 1.0, 10, Robot, False)))

    pylab.figure()
    pylab.plot(pcts[0], '-r', label='1 Robot')
    pylab.plot(pcts[1], '-b', label='2 Robots')
    pylab.plot(pcts[2], '-g', label='3 Robots')
    pylab.plot(pcts[3], '-c', label='4 Robots')
    pylab.plot(pcts[4], '-m', label='5 Robots')
    
    pylab.axis ([0, 6300, 0, 1.05]) 
    pylab.xlabel ('time to clean (ticks)')
    pylab.ylabel ('% of floor cleaned')
    pylab.title ('Completed Coverage vs. Time for 1-5 robots')
    pylab.legend() 
    
    pylab.show()
showPlot4()


# === Problem 6

def showPlot5():
    """
    Produces a plot comparing the two robot strategies.
    """
    
    pcts = computeMeans(runSimulation (1, 1.0, 15, 15, 1.0, 30, Robot, False))
    randomWalkPcts = computeMeans(runSimulation (1, 1.0, 15, 15, 1.0, 30, RandomWalkRobot, False))
    
    pylab.figure()
    pylab.plot(pcts, '-r')
    pylab.plot(randomWalkPcts, '-b')

    
    pylab.axis ([0, 7500, 0, 1.05]) 
    pylab.xlabel ('time to clean (ticks)')
    pylab.ylabel ('% of floor cleaned')
    pylab.title ('Coverage vs. Time, for Regular (red) vs RandomWalk (blue) robots')
    
    pylab.show()
# showPlot5()

### Final Comments: RandomWalk Robot averages about 2.5-3 times longer for most tasks, with high volatility between trials. 
conwayblue (Self-grade: Pretty good)
Submitted 1 year ago | Permalink | Time spent: 1 day

This was more fun that the previous problems.

# Problem Set 11: Simulating robots
# Name:conwayblue

import math
import pylab
import random
import ps11_visualize


# === Provided classes

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).

        x: a real number indicating the x-coordinate
        y: a real number indicating the y-coordinate
        """
        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: integer 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()
        # 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 __eq__(self, other):
        return self.getX() == other.getX() and self.getY() == other.getY()


# === Problems 1 and 2

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.
    """
    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.clean = []
        
        
    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
        """
        point = (int(pos.getX()), int(pos.getY()))
        if point not in self.clean:
            self.clean.append(point)
        
    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
        """
        return (m,n) in self.clean
        
    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
        """
        return len(self.clean)
        
    def getRandomPosition(self):
        """
        Return a random position inside the room.

        returns: a Position object.
        """
        return Position(random.randint(0,self.width),
                        random.randint(0,self.height))

        
    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.
        """

        return (pos.getX() <= self.width and
                pos.getX() >- 0 and
                pos.getY() <= self.height and
                pos.getY() >= 0)


class BaseRobot(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 BaseRobot 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 d and a
        random position p in the room.

        The direction d is an integer satisfying 0 <= d < 360; it
        specifies an angle in degrees.

        p is a Position object giving the robot's position.

        room:  a RectangularRoom object.
        speed: a float (speed > 0)
        """
        # TODO: Your code goes here
        self.room = room
        self.speed = speed
        self.d = random.randint(0,360)
        self.p = room.getRandomPosition() 
    def getRobotPosition(self):
        """
        Return the position of the robot.

        returns: a Position object giving the robot's position.
        """
        # TODO: Your code goes here
        return self.p
    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.
        """
        # TODO: Your code goes here
        return self.d
    def setRobotPosition(self, position):
        """
        Set the position of the robot to POSITION.

        position: a Position object.
        """
        # TODO: Your code goes here
        self.p = position
    def setRobotDirection(self, direction):
        """
        Set the direction of the robot to DIRECTION.

        direction: integer representing an angle in degrees
        """
        # TODO: Your code goes here
        self.d = direction


class Robot(BaseRobot):
    """
    A Robot is a BaseRobot with the standard movement strategy.

    At each time-step, a Robot attempts to move in its current
    direction; when it hits a wall, it chooses a new direction
    randomly.
    """
    def updatePositionAndClean(self):
        """
        Simulate the passage of a single time-step.

        Move the robot to a new position and mark the tile it is on as having
        been cleaned.
        """
        # TODO: Your code goes here
        
        inroom = False
        while not inroom:
            newpos = self.p.getNewPosition(self.d,self.speed)
            if self.room.isPositionInRoom(newpos):
                inroom = True
            else:
                self.setRobotDirection(random.randint(0,360))
        self.setRobotPosition(newpos)
        self.setRobotDirection(self.d)
        self.room.cleanTileAtPosition(newpos)
    


# === Problem 3

def runSimulation(num_robots, speed, width, height, min_coverage, num_trials,
                  robot_type=Robot, visualize=False):
    """
    Runs NUM_TRIALS trials of the simulation and returns a list of
    lists, one per trial. The list for a trial has an element for each
    timestep of that trial, the value of which is the percentage of
    the room that is clean after that timestep. Each trial stops when
    MIN_COVERAGE of the room is clean.

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

    Visualization is turned on when boolean VISUALIZE is set to True.

    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. Robot or
                RandomWalkRobot)
    visualize: a boolean (True to turn on visualization)
    """
    # TODO: Your code goes
    
    results = []    
    for i in range(num_trials):
        room = RectangularRoom(width, height)
        robots = [robot_type(room,speed) for i in range(num_robots)]
        clean = 0
        tick = 0
        trial = []
        
        if visualize:
            anim = ps11_visualize.RobotVisualization(num_robots,width,height)
        
        while clean < min_coverage:
            clean = float(room.getNumCleanedTiles()) / float(room.getNumTiles())
            trial.append(clean)
            for bot in robots:
                tick += 1
                bot.updatePositionAndClean()
                if visualize:
                    anim.update(room,robots)
        #print tick
        if visualize:
            anim.done()
        results.append(trial)
    
    return results

    
    

# === Provided function
def computeMeans(list_of_lists):
    """
    Returns a list as long as the longest list in LIST_OF_LISTS, where
    the value at index i is the average of the values at index i in
    all of LIST_OF_LISTS' lists.

    Lists shorter than the longest list are padded with their final
    value to be the same length.
    """
    # Find length of longest list
    longest = 0
    for lst in list_of_lists:
        if len(lst) > longest:
           longest = len(lst)
    # Get totals
    tots = [0]*(longest)
    for lst in list_of_lists:
        for i in range(longest):
            if i < len(lst):
                tots[i] += lst[i]
            else:
                tots[i] += lst[-1]
    # Convert tots to an array to make averaging across each index easier
    tots = pylab.array(tots)
    # Compute means
    means = tots/float(len(list_of_lists))
    return means
    
def avgTime(list_of_lists):
    total = 0
    for i in list_of_lists:
        total += len(i)
    avg = float(total) / float(len(list_of_lists))
    return avg  

# === Problem 4
def showPlot1(typebot=Robot):
    """
    Produces a plot showing dependence of cleaning time on room size.
    """
    # TODO: Your code goes here
    avgTimes = []
    sizes = [5,10,15,20,25]
    areas = [25,100,225,400,625]
    clean = 0.75
    sims = 1
    bots = 1
    speed = 1
    #typebot = Robot
    #typebot = RandomWalkRobot
    visualize = False
    
    for side in sizes:
        avgTimes.append(int(avgTime(runSimulation(
                                    bots,speed,side,side,
                                    clean,sims,typebot,visualize))))
    
    pylab.figure()
    pylab.plot(areas,avgTimes)
    pylab.ylabel('Number of steps')
    pylab.xlabel('Room Area')
    pylab.title('Avg time to clean 75% of given area with 1 robot at speed 1')
    pylab.show() 
    

def showPlot2(typebot=Robot):
    """
    Produces a plot showing dependence of cleaning time on number of robots.
    """
    # TODO: Your code goes here
    avgTimes = []
    side = 25
    clean = 0.75
    sims = 30
    speed = 1
    #typebot = Robot
    #typebot = RandomWalkRobot
    visualize = False
    
    for i in range(1,11):
        avgTimes.append(avgTime(runSimulation(i,speed,side,side,clean,
                                              sims,typebot,visualize)))
    
    pylab.figure()
    pylab.plot(range(1,11),avgTimes)
    pylab.xlabel('Number of robots')
    pylab.ylabel('Number of steps')
    pylab.title('Avg time for x number of robots to clean 75% of 25 x 25 room')
    pylab.show()

def showPlot3(typebot=Robot):
    """
    Produces a plot showing dependence of cleaning time on room shape.
    """
    # TODO: Your code goes here
    rooms = [(20,20),(25,16),(40,10),(50,8),(80,5),(100,4)]
    clean = 0.75
    sims = 30
    speed = 1
    bots = 2
    #typebot = Robot
    #typebot = RandomWalkRobot
    visualize = False
    avgTimes = []
    ratios = []
    
    for room in rooms:
        ratios.append(float(room[0]) / room[1])
        avgTimes.append(avgTime(runSimulation(bots,speed,room[0],room[1],
                                              clean,sims,typebot,visualize)))
    pylab.figure()
    pylab.plot(ratios,avgTimes)
    pylab.xlabel('Ratio of room width / height')
    pylab.ylabel('Number of steps')
    pylab.title('Avg time for 2 robots to clean 75% of x-ratio room')
    pylab.show()
    

def showPlot4(typebot=Robot):
    """
    Produces a plot showing cleaning time vs. percentage cleaned, for
    each of 1-5 robots.
    """
    # TODO: Your code goes here
    sims = 3
    speed = 1
    side = 25
    #typebot = Robot
    #typebot = RandomWalkRobot
    visualize = False
    labels = []
    pylab.figure()
    
    for bots in range(1,6):
        run = runSimulation(bots,speed,side,side,1,sims,typebot,visualize)
        meanRun = computeMeans(run)
        label = '%d bots' % bots
        labels.append(label)        
        pylab.plot(meanRun*100,range(len(meanRun)), label=label)
        
    pylab.legend(labels)
    pylab.xlabel('Percent cleaned')
    pylab.ylabel('Number of steps')
    pylab.title('Time for x robots to clean x percent of room')
    pylab.show()
    
# === Problem 5

class RandomWalkRobot(BaseRobot):
    """
    A RandomWalkRobot is a robot with the "random walk" movement
    strategy: it chooses a new direction at random after each
    time-step.
    """
    # TODO: Your code goes here
    def updatePositionAndClean(self):
        """
        Simulate the passage of a single time-step.

        Move the robot to a new position and mark the tile it is on as having
        been cleaned.
        """
        # TODO: Your code goes here
        
        
        
        inroom = False
        while not inroom:
            self.setRobotDirection(random.randint(0,360))
            newpos = self.p.getNewPosition(self.d,self.speed)
            if self.room.isPositionInRoom(newpos):
                inroom = True
            else:
                self.setRobotDirection(random.randint(0,360))
        self.setRobotPosition(newpos)
        self.setRobotDirection(self.d)
        self.room.cleanTileAtPosition(newpos)
        
# === Problem 6

def showPlot5():
    """
    Produces a plot comparing the two robot strategies.
    """
    # TODO: Your code goes here
    sims = 10
    speed = 1
    side = 20
    visualize = False
    types = (Robot,RandomWalkRobot)
    labels = []
    totalbots = 3
    pylab.figure()
    
    for bots in range(1,totalbots+1):
        for bot_type in types:
            run = runSimulation(bots,speed,side,side,0.75,sims,bot_type,visualize)
            meanRun = computeMeans(run)
            if bot_type == Robot:
                label = '%d Robot type' % (bots)
            else:
                label = '%d RandomWalkRobot' % (bots)                       
            labels.append(label)
            pylab.plot(meanRun*100,range(len(meanRun)),label=label)
            
    pylab.legend(labels)
    pylab.xlabel('Percent cleaned')
    pylab.ylabel('Number of steps')
    pylab.title('Time for x robots of x type to clean 75 percent of room')
    pylab.show()
            
            
            
    
    
    
    
    
    
    

rfh (Self-grade: Outstanding)
Submitted 1 year ago | Permalink | Time spent: 1 minute

Designed my robot classes so RandomWalkRobot could inherit from Robot rather than BaseRobot, and avoid duplicate code in updatePositionAndClean

# Problem Set 11: Simulating robots
# Name:
# Collaborators:
# Time:

import math
import random
import ps11_visualize
import pylab

# === Provided classes

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).

        x: a real number indicating the x-coordinate
        y: a real number indicating the y-coordinate
        """
        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: integer 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()
        # 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)


# === Problems 1 and 2

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.
    """
    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
        """
        if width < 0: raise ValueException('Width cannot be less than 0')
        if height < 0: raise ValueException('Height cannot be less than 0')
        self.width = width
        self.height = height
        self.cleanedTiles = []
    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
        """
        coords = int(pos.getX()), int(pos.getY())
        if coords in self.cleanedTiles:
            return
        self.cleanedTiles.append(coords)
    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
        """
        return (int(m), int(n)) in self.cleanedTiles
    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
        """
        return len(self.cleanedTiles)
    def getRandomPosition(self):
        """
        Return a random position inside the room.

        returns: a Position object.
        """
        return Position(random.randrange(0, self.width), random.randrange(0, self.height))
    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.
        """
        return 0 <= pos.getX() <= self.width and 0 <= pos.getY() <= self.height

class BaseRobot(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 BaseRobot 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 d and a
        random position p in the room.

        The direction d is an integer satisfying 0 <= d < 360; it
        specifies an angle in degrees.

        p is a Position object giving the robot's position.

        room:  a RectangularRoom object.
        speed: a float (speed > 0)
        """
        self.room = room
        self.speed = speed
        self.direction = random.randrange(0, 360)
        self.position = room.getRandomPosition()
    def getRobotPosition(self):
        """
        Return the position of the robot.

        returns: a Position object giving the robot's position.
        """
        return self.position
    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
    def setRobotPosition(self, position):
        """
        Set the position of the robot to POSITION.

        position: a Position object.
        """
        self.position = position
    def setRobotDirection(self, direction):
        """
        Set the direction of the robot to DIRECTION.

        direction: integer representing an angle in degrees
        """
        self.direction = direction


class Robot(BaseRobot):
    """
    A Robot is a BaseRobot with the standard movement strategy.

    At each time-step, a Robot attempts to move in its current
    direction; when it hits a wall, it chooses a new direction
    randomly.
    """
    def updatePositionAndClean(self):
        """
        Simulate the 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)
        while not self.room.isPositionInRoom(newPosition):
            self.direction = random.randrange(0, 360)
            newPosition = self.position.getNewPosition(self.direction, self.speed)

        self.room.cleanTileAtPosition(newPosition)
        self.position = newPosition


# === Problem 3

def runSimulation(num_robots, speed, width, height, minCoverage, numTrials,
                  robotType, visualize = False):
    """
    Runs NUM_TRIALS trials of the simulation and returns a list of
    lists, one per trial. The list for a trial has an element for each
    timestep of that trial, the value of which is the percentage of
    the room that is clean after that timestep. Each trial stops when
    MIN_COVERAGE of the room is clean.

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

    Visualization is turned on when boolean VISUALIZE is set to True.

    num_robots: an int (num_robots > 0)
    speed: a float (speed > 0)
    width: an int (width > 0)
    height: an int (height > 0)
    minCoverage: a float (0 <= minCoverage <= 1.0)
    num_trials: an int (num_trials > 0)
    robotType: class of robot to be instantiated (e.g. Robot or
                RandomWalkRobot)
    visualize: a boolean (True to turn on visualization)
    """
    def percentageOfRoomCleaned(room):
        return float(room.getNumCleanedTiles()) / room.getNumTiles()

    results = []
    for trialNum in range(1, numTrials + 1):
        if visualize:
            anim = ps11_visualize.RobotVisualization(num_robots, width, height)

        trialResults = [] 
        room = RectangularRoom(width, height)
        robots = []
        for robotNum in range(0, num_robots):
            robots.append(robotType(room, speed))
        while percentageOfRoomCleaned(room) < minCoverage:
            if visualize:
                anim.update(room, robots)
            trialResults.append(percentageOfRoomCleaned(room))
            for robo in robots:
                robo.updatePositionAndClean()

        if visualize:
            anim.done()
        results.append(trialResults)

    return results

#print(runSimulation(1, 1, 15, 15, 1.0, 100, Robot, True))

# === Provided function
def computeMeans(list_of_lists):
    """
    Returns a list as long as the longest list in LIST_OF_LISTS, where
    the value at index i is the average of the values at index i in
    all of LIST_OF_LISTS' lists.

    Lists shorter than the longest list are padded with their final
    value to be the same length.
    """
    # Find length of longest list
    longest = 0
    for lst in list_of_lists:
        if len(lst) > longest:
           longest = len(lst)
    # Get totals
    tots = [0]*(longest)
    for lst in list_of_lists:
        for i in range(longest):
            if i < len(lst):
                tots[i] += lst[i]
            else:
                tots[i] += lst[-1]
    # Convert tots to an array to make averaging across each index easier
    tots = pylab.array(tots)
    # Compute means
    means = tots/float(len(list_of_lists))
    return means

def computeAverageTime(listOfLists):
    totalTime = 0
    for l in listOfLists:
        totalTime += len(l)

    return float(totalTime) / len(listOfLists)

# === Problem 4
def showPlot1():
    """
    Produces a plot showing dependence of cleaning time on room size.
    """
    numTrials = 50
    averageTimes = []
    roomSideSizes = (5, 10, 15, 20, 25)
    roomAreas = []

    for size in roomSideSizes:
        roomAreas.append(size**2)
        averageTimes.append(computeAverageTime(runSimulation(1, 1.0, size, size, .75, numTrials, Robot)))

    pylab.figure()
    pylab.plot(roomAreas, averageTimes)
    pylab.ylabel('Timesteps')
    pylab.xlabel('Room area')
    pylab.title('Time to clean 75%% of a square room with 1 robot (%d trials)' % numTrials)

def showPlot2():
    """
    Produces a plot showing dependence of cleaning time on number of robots.
    """
    numTrials = 50
    averageTimes = []
    for numRobots in range(1, 11):
        averageTimes.append(computeAverageTime(runSimulation(numRobots, 1.0, 25, 25, .75, numTrials, Robot)))

    pylab.figure()
    pylab.plot(range(1, 11), averageTimes)
    pylab.ylabel('Timesteps')
    pylab.xlabel('Number of robots')
    pylab.title('Time to clean 75%% of a 25x25 room (%d trials)' % numTrials)

def showPlot3():
    """
    Produces a plot showing dependence of cleaning time on room shape.
    """
    numTrials = 50
    averageTimes = []
    widthHeightRatios = []
    for width, height in ((20, 20), (25, 16), (40, 10), (50, 8), (80, 5), (100, 4)):
        widthHeightRatios.append(float(width) / height)
        averageTimes.append(computeAverageTime(runSimulation(2, 1.0, width, height, .75, numTrials, Robot)))

    pylab.figure()
    pylab.plot(widthHeightRatios, averageTimes)
    pylab.ylabel('Timesteps')
    pylab.xlabel('Width to height ratio for room size')
    pylab.title('Time to clean 75%% of a room with the same area, but varying widths/heights using 2 robots (%d trials)' % numTrials)


def showPlot4():
    """
    Produces a plot showing cleaning time vs. percentage cleaned, for
    each of 1-5 robots.
    """
    numTrials = 15
    pylab.figure()
    labels = []

    for numRobots in range(1, 6):
        label = "%d robot(s)" % numRobots
        labels.append(label)
        results = computeMeans(runSimulation(numRobots, 1.0, 25, 25, 1, numTrials, Robot))
        pylab.plot(results * 100, range(0, len(results)), label=label)

    pylab.legend(labels)
    pylab.ylabel('Timesteps')
    pylab.xlabel('Minimum coverage %')
    pylab.title('Time to clean vs Minimum coverage %% with 1-5 robots (%d trials)' % numTrials)

#showPlot1()
#showPlot2()
#showPlot3()
#showPlot4()
#pylab.show()


# === Problem 5

class RandomWalkRobot(Robot):
    """
    A RandomWalkRobot is a robot with the "random walk" movement
    strategy: it chooses a new direction at random after each
    time-step.
    """
    def updatePositionAndClean(self):
        self.direction = random.randrange(0, 360)
        Robot.updatePositionAndClean(self)

#runSimulation(1, 1.0, 25, 25, 1, 1, RandomWalkRobot, True)

# === Problem 6

def showPlot5():
    """
    Produces a plot comparing the two robot strategies.
    """
    numTrials = 10
    pylab.figure()
    labels = []
    robotTypes = (Robot, RandomWalkRobot)

    for numRobots in range(1, 3):
        for robotType in robotTypes:
            label = "%d %s robot(s)" % (numRobots, robotType.__name__)
            labels.append(label)
            results = computeMeans(runSimulation(numRobots, 1.0, 25, 25, 1, numTrials, robotType))
            pylab.plot(results * 100, range(0, len(results)), label=label)

    pylab.legend(labels)
    pylab.ylabel('Timesteps')
    pylab.xlabel('Minimum coverage %')
    pylab.title('Time to clean vs Minimum coverage %% with 1-5 robots of type Robot and RandomWalkRobot (%d trials)' % numTrials)

showPlot5()
pylab.show()
geoB (Self-grade: Outstanding)
Submitted 1 year ago | Permalink | Time spent: 5 hours

Took a while but was able to include legends on some of the graphs.

# Problem Set 11: Simulating robots
# Name: gwb
# Collaborators:
# Time: 2.5 hrs

import math
import random
import pylab

##import ps11_visualize

# === Provided classes

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).

        x: a real number indicating the x-coordinate
        y: a real number indicating the y-coordinate
        """
        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: integer 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()
        # 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)


# === Problems 1 and 2

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.
    """
    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
        """
        # TODO: Your code goes here
        self.width = int(width)
        self.height = int(height)
        # cleaned is a list of cleaned tile tuples
        self.cleaned = []
    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
        """
        # TODO: Your code goes here
        tile = (int(pos.x), int(pos.y))
        self.cleaned += [tile]
    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
        """
        # TODO: Your code goes here
        if (m, n) not in self.cleaned: return False
        else: return True
    def getNumTiles(self):
        """
        Return the total number of tiles in the room.

        returns: an integer
        """
        # TODO: Your code goes here
        return self.width * self.height
    def getNumCleanedTiles(self):
        """
        Return the total number of clean tiles in the room.

        returns: an integer
        """
        # TODO: Your code goes here
        return len(self.cleaned)
    def getRandomPosition(self):
        """
        Return a random position inside the room.

        returns: a Position object.
        """
        # TODO: Your code goes here
        p_x = random.uniform(0,self.width)
        p_y = random.uniform(0,self.height)
        return Position(p_x, p_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.
        """
        # TODO: Your code goes here
        if pos.x < 0 or pos.x > self.width or pos.y < 0 or pos.y > self.height:
            return False
        else:
            return True


class BaseRobot(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 BaseRobot 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 d and a
        random position p in the room.

        The direction d is an integer satisfying 0 <= d < 360; it
        specifies an angle in degrees.

        p is a Position object giving the robot's position.

        room:  a RectangularRoom object.
        speed: a float (speed > 0)
        """
        # TODO: Your code goes here
        self.room = room
        self.speed = float(speed)
        self.direction = int(random.uniform(0,360))
        self.pos = self.room.getRandomPosition()
        # clean tile at initial position!
        self.room.cleanTileAtPosition(self.pos)
    def getRobotPosition(self):
        """
        Return the position of the robot.

        returns: a Position object giving the robot's position.
        """
        # TODO: Your code goes here
        return self.pos
    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.
        """
        # TODO: Your code goes here
        return self.direction
    def setRobotPosition(self, position):
        """
        Set the position of the robot to POSITION.

        position: a Position object.
        """
        # TODO: Your code goes here
        self.pos = position
    def setRobotDirection(self, direction):
        """
        Set the direction of the robot to DIRECTION.

        direction: integer representing an angle in degrees
        """
        # TODO: Your code goes here
        self.direction = direction
    def getRobotTile(self):
        """
        Returns the tile tuple for the current robot position
        """
        return (int(self.pos.x), int(self.pos.y))


class Robot(BaseRobot):
    """
    A Robot is a BaseRobot with the standard movement strategy.

    At each time-step, a Robot attempts to move in its current
    direction; when it hits a wall, it chooses a new direction
    randomly.
    """
    def updatePositionAndClean(self):
        """
        Simulate the passage of a single time-step.

        Move the robot to a new position and mark the tile it is on as having
        been cleaned.
        """
        # TODO: Your code goes here
        newLocation = self.pos.getNewPosition(self.direction, self.speed)
        # make sure new position is in room!
        while self.room.isPositionInRoom(newLocation) is False:
            # try a different direction
            self.setRobotDirection(int(random.uniform(0,360)))
            newLocation = self.pos.getNewPosition(self.direction, self.speed)
        self.setRobotPosition(newLocation)
        tile = self.getRobotTile()
        if self.room.isTileCleaned(tile[0], tile[1]) == False:
            self.room.cleanTileAtPosition(newLocation)
            

# === Problem 3

def runSimulation(num_robots, speed, width, height, min_coverage, num_trials,
                  robot_type, visualize):
    """
    Runs NUM_TRIALS trials of the simulation and returns a list of
    lists, one per trial. The list for a trial has an element for each
    timestep of that trial, the value of which is the percentage of
    the room that is clean after that timestep. Each trial stops when
    MIN_COVERAGE of the room is clean.

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

    Visualization is turned on when boolean VISUALIZE is set to True.

    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. Robot or
                RandomWalkRobot)
    visualize: a boolean (True to turn on visualization)
    """
    # TODO: Your code goes here
##    print 'Robots: ' + str(num_robots) + '; Trials: ' + str(num_trials) + '; Room: ' + str(width) + 'x' + str(height) \
##          + '; Speed: ' + str(speed) + '; Coverage: ' + str(min_coverage)
    # a container for list of lists
    output = []
    for i in range(num_trials):
        # a container for trial list
##        anim = ps11_visualize.RobotVisualization(num_robots, width, height)
        room = RectangularRoom(width, height)
        totalTiles = float(width * height)
        # set of robots
        robots = []
        for j in range(num_robots):
            robots += [robot_type(room, speed)]
        # initialize cleaned tiles
        cleaned = float(room.getNumCleanedTiles())
        pctCleaned = cleaned/totalTiles
        result = [pctCleaned]
        # work until min_coverage is clean
        while pctCleaned < min_coverage:
            for j in range(num_robots):
                robots[j].updatePositionAndClean()
            cleaned = float(room.getNumCleanedTiles())
            pctCleaned = cleaned/totalTiles
            result.append(pctCleaned)
##            anim.update(room, robots)
        output.append(result)
    return output
        
def avgLength(listOfLists):
    """
    computes average length of list for list of lists
    """
    n = len(listOfLists)
    total = 0
    for i in range(n - 1):
        total += len(listOfLists[i])
    return float(total)/float(n)

# === Provided function
def computeMeans(list_of_lists):
    """
    Returns a list as long as the longest list in LIST_OF_LISTS, where
    the value at index i is the average of the values at index i in
    all of LIST_OF_LISTS' lists.

    Lists shorter than the longest list are padded with their final
    value to be the same length.
    """
    # Find length of longest list
    longest = 0
    for lst in list_of_lists:
        if len(lst) > longest:
           longest = len(lst)
    # Get totals
    tots = [0]*(longest)
    for lst in list_of_lists:
        for i in range(longest):
            if i < len(lst):
                tots[i] += lst[i]
            else:
                tots[i] += lst[-1]
    # Convert tots to an array to make averaging across each index easier
    tots = pylab.array(tots)
    # Compute means
    means = tots/float(len(list_of_lists))
    return means


# === Problem 4
def showPlot1():
    """
    Produces a plot showing dependence of cleaning time on room size.
    Single robot, 75% clean, 5x5, 10x10, 15x15, 20x20, 25x25 rooms
    """
    # TODO: Your code goes here
    num_robots = 1
    speed = 1
    min_coverage = 0.75
    num_trials = 30
    robot_type = Robot
    visualize = False
    x_values = []
    y_values = []
    initial_dim = 5
    for i in range(1,6):
        run = runSimulation(num_robots, speed, i*initial_dim, i*initial_dim, min_coverage, num_trials, robot_type, visualize)
        x_values.append((i*initial_dim)**2)
        y_values.append(avgLength(run))
    pylab.plot(x_values, y_values)
    pylab.xlabel('Area')
    pylab.ylabel('Time')
    pylab.title('Mean time vs. Area; single robot')
    pylab.show()
    

def showPlot2():
    """
    Produces a plot showing dependence of cleaning time on number of robots.
    """
    # TODO: Your code goes here
    num_robots = 1
    speed = 1
    min_coverage = 0.75
    num_trials = 30
    robot_type = Robot
    visualize = False
    x_values = []
    y_values = []
    initial_dim = 25
    for i in range(1,11):
        run = runSimulation(i*num_robots, speed, initial_dim, initial_dim, min_coverage, num_trials, robot_type, visualize)
        x_values.append(i)
        y_values.append(avgLength(run))
    pylab.plot(x_values, y_values)
    pylab.xlabel('No. Robots')
    pylab.ylabel('Time')
    pylab.title('Mean time vs. Number of Robots')
    pylab.show()

def showPlot3():
    """
    Produces a plot showing dependence of cleaning time on room shape.
    """
    # TODO: Your code goes here
    num_robots = 2
    speed = 1
    min_coverage = 0.75
    num_trials = 60
    robot_type = Robot
    visualize = False
    x_values = []
    y_values = []
    width = [20, 25, 40, 50, 80, 100]
    height = [20, 16, 10, 8, 5, 4]
    for i in range(6):
        run = runSimulation(num_robots, speed, width[i], height[i], min_coverage, num_trials, robot_type, visualize)
        ratio = float(width[i])/float(height[i])
        x_values.append(ratio)
        y_values.append(avgLength(run))
    pylab.plot(x_values, y_values)
    pylab.xlabel('Ratio')
    pylab.ylabel('Time')
    pylab.title('Mean time vs. Width/Height')
    pylab.show()

def showPlot4():
    """
    Produces a plot showing cleaning time vs. percentage cleaned, for
    each of 1-5 robots.
    """
    # TODO: Your code goes here
    speed = 1
    min_coverage = 0.9
    num_trials = 30
    robot_type = Robot
    visualize = False
    width = 25
    height = 25
    for r in range(1,6):
        run = runSimulation(r, speed, width, height, min_coverage, num_trials, robot_type, visualize)
        means = computeMeans(run)
        pylab.plot(means, range(len(means)), label = str(r) + ' robots')
    pylab.xlabel('Pct Cleaned')
    pylab.ylabel('Time')
    pylab.title('Mean time vs.Pct Cleaned\n1 - 5 robots')
    pylab.legend(loc='upper left')
    pylab.show()

# === Problem 5

class RandomWalkRobot(BaseRobot):
    """
    A RandomWalkRobot is a robot with the "random walk" movement
    strategy: it chooses a new direction at random after each
    time-step.
    """
    # TODO: Your code goes here
    def updatePositionAndClean(self):
        """
        Simulate the passage of a single time-step.

        Move the robot to a new position at each time increment and mark the tile it is on as having
        been cleaned.
        """
        # TODO: Your code goes here
        # only difference: get a new direction every update
        self.setRobotDirection(int(random.uniform(0,360)))
        newLocation = self.pos.getNewPosition(self.direction, self.speed)
        # make sure new position is in room!
        while self.room.isPositionInRoom(newLocation) is False:
            # try a different direction
            self.setRobotDirection(int(random.uniform(0,360)))
            newLocation = self.pos.getNewPosition(self.direction, self.speed)
        self.setRobotPosition(newLocation)
        tile = self.getRobotTile()
        if self.room.isTileCleaned(tile[0], tile[1]) == False:
            self.room.cleanTileAtPosition(newLocation)


# === Problem 6

def showPlot5():
    """
    Produces a plot comparing the two robot strategies.
    """
    # TODO: Your code goes here
    num_robots = 1
    speed = 1
    min_coverage = 0.75
    num_trials = 30
    robot_type = Robot
    visualize = False
    x_values = []
    y_values = []
    initial_dim = 5
    for i in range(1,6):
        run = runSimulation(num_robots, speed, i*initial_dim, i*initial_dim, min_coverage, num_trials, robot_type, visualize)
        x_values.append((i*initial_dim)**2)
        y_values.append(avgLength(run))
    pylab.plot(x_values, y_values, label = 'Standard robot')

    robot_type = RandomWalkRobot
    visualize = False
    x_values = []
    y_values = []
    initial_dim = 5
    for i in range(1,6):
        run = runSimulation(num_robots, speed, i*initial_dim, i*initial_dim, min_coverage, num_trials, robot_type, visualize)
        x_values.append((i*initial_dim)**2)
        y_values.append(avgLength(run))
    pylab.plot(x_values, y_values, label = 'Random walk robot')
    pylab.xlabel('Area')
    pylab.ylabel('Time')
    pylab.title('Mean time vs. Area')
    pylab.legend(loc = 'upper left')
    pylab.show()
Joe (Self-grade: Outstanding)
Submitted 1 year ago | Permalink

This one is really fun.

# 6.00 Problem Set 12
#
# Name: Joe Li
# Collaborators:
# Time: 7:00

import numpy
import random
import pylab

class NoChildException(Exception):
    """
    NoChildException is raised by the reproduce() method in the SimpleVirus
    and ResistantVirus classes to indicate that a virus particle does not
    reproduce. You can use NoChildException as is, you do not need to
    modify/add any code.
    """    

#
# PROBLEM 1
#

class SimpleVirus(object):
    """
    Representation of a simple virus (does not model drug effects/resistance).
    """
    
    def __init__(self, maxBirthProb, clearProb):
        """
        Initialize a SimpleVirus instance, saves all parameters as attributes
        of the instance.        
        
        maxBirthProb: Maximum reproduction probability (a float between 0-1)        
        
        clearProb: Maximum clearance probability (a float between 0-1).
        """
        self.maxBirthProb = maxBirthProb
        self.clearProb = clearProb
        
    def doesClear(self):
        """
        Stochastically determines whether this virus is cleared from the
        patient's body at a time step. 

        returns: Using a random number generator (random.random()), this method
        returns True with probability self.clearProb and otherwise returns
        False.
        """
        prob = random.random()
        if prob < self.clearProb:
            return True
        else:
            return False
    
    def reproduce(self, popDensity):
        """
        Stochastically determines whether this virus particle reproduces at a
        time step. Called by the update() method in the SimplePatient and
        Patient classes. The virus particle reproduces with probability
        self.maxBirthProb * (1 - popDensity).
        
        If this virus particle reproduces, then reproduce() creates and returns
        the instance of the offspring SimpleVirus (which has the same
        maxBirthProb and clearProb values as its parent).         

        popDensity: the population density (a float), defined as the current
        virus population divided by the maximum population.         
        
        returns: a new instance of the SimpleVirus class representing the
        offspring of this virus particle. The child should have the same
        maxBirthProb and clearProb values as this virus. Raises a
        NoChildException if this virus particle does not reproduce.               
        """
        chance = self.maxBirthProb * (1 - popDensity)
        prob = random.random()
        if prob < chance:
            offspring = SimpleVirus(self.maxBirthProb, self.clearProb)
            return offspring
        raise NoChildException

class SimplePatient(object):
    """
    Representation of a simplified patient. The patient does not take any drugs
    and his/her virus populations have no drug resistance.
    """
    
    def __init__(self, viruses, maxPop):
        """
        Initialization function, saves the viruses and maxPop parameters as
        attributes.

        viruses: the list representing the virus population (a list of
        SimpleVirus instances)
        
        maxPop: the  maximum virus population for this patient (an integer)
        """
        self.viruses = viruses
        self.maxPop = maxPop

    def getTotalPop(self):
        """
        Gets the current total virus population. 

        returns: The total virus population (an integer)
        """
        return len(self.viruses)    

    def update(self):
        """
        Update the state of the virus population in this patient for a single
        time step. update() should execute the following steps in this order:

        - Determine whether each virus particle survives and updates the list
          of virus particles accordingly.

        - The current population density is calculated. This population density
          value is used until the next call to update() 

        - Determine whether each virus particle should reproduce and add
          offspring virus particles to the list of viruses in this patient.                    

        returns: the total virus population at the end of the update (an
        integer)
        """
        health = []
        # the list of viruses isn't cleared
        offsprings = []
        for v in self.viruses:
        # append the virus which is able to reproduce to health
            if not v.doesClear():
                health.append(v)
        popDensity = float(len(health))/float(self.maxPop)
        assert 0 <= popDensity <=1,'wrong popDensity'
        for v in health:
        # append the offsprings the viruses reproduce to a list
            try:
                offsprings.append(v.reproduce(popDensity))
            except NoChildException: pass
        self.viruses = health + offsprings
        # the new viruses list is both list health and list offspring
        return len(self.viruses)

#
# PROBLEM 2
#

def problem2():
    """
    Run the simulation and plot the graph for problem 2 (no drugs are used,
    viruses do not have any drug resistance).    

    Instantiates a patient, runs a simulation for 300 timesteps, and plots the
    total virus population as a function of time.    
    """
    v = []
    pop = []
    for i in range(100):
        v.append(SimpleVirus(0.1, 0.05))
    poorguy = SimplePatient(v, 1000)
    for i in range(300):
        pop.append(poorguy.update())
    pylab.plot(pop)
    pylab.xlabel('Time')
    pylab.ylabel('Total virus population')
    pylab.title('Simulation of viruses reproduction without drug')
    pylab.show()
        
    
#
# PROBLEM 3
#

class ResistantVirus(SimpleVirus):
    """
    Representation of a virus which can have drug resistance.
    """    
    
    def __init__(self, maxBirthProb, clearProb, resistances, mutProb):
        """
        Initialize a ResistantVirus instance, saves all parameters as attributes
        of the instance.
        
        maxBirthProb: Maximum reproduction probability (a float between 0-1)        
        
        clearProb: Maximum clearance probability (a float between 0-1).
        
        resistances: A dictionary of drug names (strings) mapping to the state
        of this virus particle's resistance (either True or False) to each drug.
        e.g. {'guttagonol':False, 'grimpex',False}, means that this virus
        particle is resistant to neither guttagonol nor grimpex.

        mutProb: Mutation probability for this virus particle (a float). This is
        the probability of the offspring acquiring or losing resistance to a drug.        
        """
        assert type(maxBirthProb) == type(clearProb) == type(mutProb) == float, 'wrong input type'
        assert 0 <= maxBirthProb <= 1, 'maxBirthProb should be in [0,1]'
        assert 0 <= clearProb <= 1, 'clearProb should be in [0,1]'
        assert 0 <= mutProb <= 1, 'mutProb should be in [0,1]'
        self.maxBirthProb = maxBirthProb
        self.clearProb = clearProb
        self.resistances = resistances
        self.mutProb = mutProb
        
    def getResistance(self, drug):
        """
        Get the state of this virus particle's resistance to a drug. This method
        is called by getResistPop() in Patient to determine how many virus
        particles have resistance to a drug.        

        drug: the drug (a string).

        returns: True if this virus instance is resistant to the drug, False
        otherwise.
        """
        return self.resistances[drug]
        
    def reproduce(self, popDensity, activeDrugs):
        """
        Stochastically determines whether this virus particle reproduces at a
        time step. Called by the update() method in the Patient class.

        If the virus particle is not resistant to any drug in activeDrugs,
        then it does not reproduce. Otherwise, the virus particle reproduces
        with probability:       
        
        self.maxBirthProb * (1 - popDensity).                       
        
        If this virus particle reproduces, then reproduce() creates and returns
        the instance of the offspring ResistantVirus (which has the same
        maxBirthProb and clearProb values as its parent). 

        For each drug resistance trait of the virus (i.e. each key of
        self.resistances), the offspring has probability 1-mutProb of
        inheriting that resistance trait from the parent, and probability
        mutProb of switching that resistance trait in the offspring.        

        For example, if a virus particle is resistant to guttagonol but not
        grimpex, and `self.mutProb` is 0.1, then there is a 10% chance that
        that the offspring will lose resistance to guttagonol and a 90% 
        chance that the offspring will be resistant to guttagonol.
        There is also a 10% chance that the offspring will gain resistance to
        grimpex and a 90% chance that the offspring will not be resistant to
        grimpex.

        popDensity: the population density (a float), defined as the current
        virus population divided by the maximum population        

        activeDrugs: a list of the drug names acting on this virus particle
        (a list of strings). 
        
        returns: a new instance of the ResistantVirus class representing the
        offspring of this virus particle. The child should have the same
        maxBirthProb and clearProb values as this virus. Raises a
        NoChildException if this virus particle does not reproduce.         
        """
        for drug in activeDrugs:
            if not self.resistances[drug]:
                raise NoChildException
        chance = self.maxBirthProb * (1 - popDensity)
        prob = random.random()
        if prob < chance:
        # reproduce
            child_resistances = {}
            for drug in self.resistances:
                switch = random.random()
                if switch < self.mutProb:
                # mutate
                    child_resistances[drug] = not self.resistances[drug]
                else:
                # inherit
                    child_resistances[drug] = self.resistances[drug]
                offspring = ResistantVirus(self.maxBirthProb, self.clearProb, child_resistances, self.mutProb)
            return offspring
        raise NoChildException
            
class Patient(SimplePatient):
    """
    Representation of a patient. The patient is able to take drugs and his/her
    virus population can acquire resistance to the drugs he/she takes.
    """
    
    def __init__(self, viruses, maxPop):
        """
        Initialization function, saves the viruses and maxPop parameters as
        attributes. Also initializes the list of drugs being administered
        (which should initially include no drugs).               

        viruses: the list representing the virus population (a list of
        SimpleVirus instances)
        
        maxPop: the  maximum virus population for this patient (an integer)
        """
        self.viruses = viruses
        self.maxPop = maxPop
        self.prescription = []
        
    def addPrescription(self, newDrug):
        """
        Administer a drug to this patient. After a prescription is added, the 
        drug acts on the virus population for all subsequent time steps. If the
        newDrug is already prescribed to this patient, the method has no effect.

        newDrug: The name of the drug to administer to the patient (a string).

        postcondition: list of drugs being administered to a patient is updated
        """
        if newDrug not in self.prescription:
            self.prescription.append(newDrug)

    def getPrescriptions(self):
        """
        Returns the drugs that are being administered to this patient.

        returns: The list of drug names (strings) being administered to this
        patient.
        """
        return self.prescription
        
    def getResistPop(self, drugResist):
        """
        Get the population of virus particles resistant to the drugs listed in 
        drugResist.        

        drugResist: Which drug resistances to include in the population (a list
        of strings - e.g. ['guttagonol'] or ['guttagonol', 'grimpex'])

        returns: the population of viruses (an integer) with resistances to all
        drugs in the drugResist list.
        """
        resistpop = 0
        for v in self.viruses:
            resist_all = True
            for drug in drugResist:
                if not v.resistances[drug]:
                    resist_all = False
            if resist_all == True:
                resistpop += 1
        return resistpop

    def update(self):
        """
        Update the state of the virus population in this patient for a single
        time step. update() should execute these actions in order:

        - Determine whether each virus particle survives and update the list of 
          virus particles accordingly
          
        - The current population density is calculated. This population density
          value is used until the next call to update().

        - Determine whether each virus particle should reproduce and add
          offspring virus particles to the list of viruses in this patient. 
          The list of drugs being administered should be accounted for in the
          determination of whether each virus particle reproduces. 

        returns: the total virus population at the end of the update (an
        integer)
        """
        health = []
        # the list of viruses isn't cleared
        offsprings = []
        for v in self.viruses:
        # append the virus which is able to reproduce to health
            if not v.doesClear():
                health.append(v)
        popDensity = float(len(health))/float(self.maxPop)
        assert 0 <= popDensity <=1,'wrong popDensity'
        for v in health:
        # append the offsprings the viruses reproduce to a list
            try:
                offsprings.append(v.reproduce(popDensity, self.prescription))
            except NoChildException: pass
        self.viruses = health + offsprings
        # the new viruses list is both list health and list offspring
        return len(self.viruses)

#
# PROBLEM 4
#

def problem4():
    """
    Runs simulations and plots graphs for problem 4.

    Instantiates a patient, runs a simulation for 150 timesteps, adds
    guttagonol, and runs the simulation for an additional 150 timesteps.

    total virus population vs. time  and guttagonol-resistant virus population
    vs. time are plotted
    """
    v = []
    pop = []
    for i in range(100):
        v.append(ResistantVirus(0.1, 0.05, {'guttagonol':False}, 0.005))
    poorguy = Patient(v, 1000)
    poorguy.addPrescription('guttagonol')
    for i in range(150):
        pop.append(poorguy.update())
    pylab.plot(pop)
    pylab.xlabel('Time')
    pylab.ylabel('Total virus population')
    pylab.title('Simulation of viruses reproduction with drug')
    pylab.show()

#
# PROBLEM 5
#
        
def problem5():
    """
    Runs simulations and make histograms for problem 5.

    Runs multiple simulations to show the relationship between delayed treatment
    and patient outcome.

    Histograms of final total virus populations are displayed for delays of 300,
    150, 75, 0 timesteps (followed by an additional 150 timesteps of
    simulation).    
    """
    tot_trial = 100
    delay = [300, 150, 75, 0]
    for s in delay:
        trial = 0
        record = []
        for test in range(tot_trial):
            trial += 1
            v = []
            pop = []
            for i in range(100):
                v.append(ResistantVirus(0.1, 0.05, {'guttagonol':False}, 0.005))
            poorguy = Patient(v, 1000)
            # instiate patient
            for i in range(s):
                poorguy.update()
            poorguy.addPrescription('guttagonol')
            # take meds
            for i in range(150):
                poorguy.update()
            record.append(poorguy.getTotalPop())
            # record the final virus population
            print 'delay: '+str(s)+' timesteps, Tiral: '+str(trial)
        cured = 0
        for result in record:
            if result <= 50:
                cured +=1
        curerate = str(float(cured) / len(record) * 100.0) + '%'
        pylab.figure()
        pylab.hist(record, bins=20, label='Cure rate: '+curerate, facecolor='g')
        pylab.legend()
        pylab.title('Simulation of viruses reproduction with drug delay '+str(s)+' timesteps')
        pylab.xlabel('Total virus population')
        pylab.ylabel('Number of patients')
    pylab.show()
    
#
# PROBLEM 6
#

def problem6():
    """
    Runs simulations and make histograms for problem 6.

    Runs multiple simulations to show the relationship between administration
    of multiple drugs and patient outcome.
    
    Histograms of final total virus populations are displayed for lag times of
    150, 75, 0 timesteps between adding drugs (followed by an additional 150
    timesteps of simulation).
    """
    delay = [300, 150, 75, 0]
    for s in delay:
        trial = 0
        record = []
        for test in range(30):
            trial += 1
            v = []
            pop = []
            for i in range(100):
                v.append(ResistantVirus(0.1, 0.05, {'guttagonol':False,'grimpex':False}, 0.005))
            poorguy = Patient(v, 1000)
            # initiate patient
            for i in range(150):
                poorguy.update()
            poorguy.addPrescription('guttagonol')
            # take meds 1
            for i in range(s):
                poorguy.update()
            poorguy.addPrescription('grimpex')
            # take meds 2
            for i in range(150):
                poorguy.update()
            record.append(poorguy.getTotalPop())
            # record the final virus population
            print 'delay: '+str(s)+' timesteps, Tiral: '+str(trial)
        cured = 0
        for result in record:
            if result <= 50:
                cured +=1
        curerate = str(float(cured) / len(record) * 100.0) + '%'
        pylab.figure()
        pylab.hist(record, bins=20, label='Cure rate: '+curerate, facecolor='g')
        pylab.legend()
        pylab.title('Simulation of viruses reproduction with delay '+str(s)+' timesteps between 2 drugs')
        pylab.xlabel('Total virus population')
        pylab.ylabel('Number of patients')
    pylab.show()

#
# PROBLEM 7
#
     
def problem7():
    """
    Run simulations and plot graphs examining the relationship between
    administration of multiple drugs and patient outcome.

    Plots of total and drug-resistant viruses vs. time are made for a
    simulation with a 300 time step delay between administering the 2 drugs and
    a simulations for which drugs are administered simultaneously.        
    """
    delay = [300, 0]
    t = {300:'Simulation of viruses population agianst time \n with a 300 time step delay between administering the 2 drugs',0:'Simulation of viruses population agianst time \n with drugs administered simultaneously'}
    for s in delay:
        v = []
        pop = []    # total virus population
        pop1 = []   # the population of guttagonol-resistant virus
        pop2 = []   # the population of grimpex- resistant virus
        popb = []   # the population of viruses that are resistant to both drugs
        for i in range(100):
            v.append(ResistantVirus(0.1, 0.05, {'guttagonol':False,'grimpex':False}, 0.005))
        poorguy = Patient(v, 1000)
        # initiate patient
        for i in range(150):
            pop.append(poorguy.update())
            p1 = 0
            p2 = 0
            pb = 0
            for v in poorguy.viruses:
                if v.resistances['guttagonol']: p1 += 1
                if v.resistances['grimpex']: p2 += 1
                if v.resistances['guttagonol'] and v.resistances['grimpex']: pb += 1
            pop1.append(p1)
            pop2.append(p2)
            popb.append(pb)
        poorguy.addPrescription('guttagonol')
        # take meds 1
        for i in range(s):
            pop.append(poorguy.update())
            p1 = 0
            p2 = 0
            pb = 0
            for v in poorguy.viruses:
                if v.resistances['guttagonol']: p1 += 1
                if v.resistances['grimpex']: p2 += 1
                if v.resistances['guttagonol'] and v.resistances['grimpex']: pb += 1
            pop1.append(p1)
            pop2.append(p2)
            popb.append(pb)
        poorguy.addPrescription('grimpex')
        # take meds 2
        for i in range(150):
            pop.append(poorguy.update())
            p1 = 0
            p2 = 0
            pb = 0
            for v in poorguy.viruses:
                if v.resistances['guttagonol']: p1 += 1
                if v.resistances['grimpex']: p2 += 1
                if v.resistances['guttagonol'] and v.resistances['grimpex']: pb += 1
            pop1.append(p1)
            pop2.append(p2)
            popb.append(pb)
        pylab.figure()
        pylab.plot(pop, label='total')
        pylab.plot(pop1, label='guttagonol-resistant virus')
        pylab.plot(pop2, label='grimpex- resistant virus')
        pylab.plot(popb, label='resistant to both drugs')
        pylab.legend(loc=0)
        pylab.xlabel('Time')
        pylab.ylabel('Virus population')
        pylab.title(t[s])
    pylab.show()
mrphud (Self-grade: Outstanding)
Submitted 1 year ago | Permalink
# Problem Set 11: Simulating robots
# Name:
# Collaborators:
# Time:

import math
import random
import pylab
##import time
##import ps11_visualize_amended

# === Provided classes

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).

        x: a real number indicating the x-coordinate
        y: a real number indicating the y-coordinate
        """
        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: integer 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()
        # Compute the change in position
        delta_y = speed * math.sin(math.radians(angle))
        delta_x = speed * math.cos(math.radians(angle))
        # Add that to the existing position
        new_x = old_x + delta_x
        new_y = old_y + delta_y
        # returns two floats
        return new_x, new_y


# === Problems 1 and 2

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.
    """
    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
        # represent the state of the floor using a dictionary. If dirty value = 0, clean = 1
        self.cleanDict = {}
        for fat in range(width):
            for tall in range(height):
                self.cleanDict[fat, tall] = 0
        
    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 object
        """
        self.cleanDict[pos] = 1
        
    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
        """
        return self.cleanDict[int(m), int(n)] == 1 
        
    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
        """      
        return sum(self.cleanDict.values())
        
    def getRandomPosition(self):
        """
        Return a random position inside the room.

        returns: a Position object.
        """
        return random.choice(self.cleanDict.keys())
        
    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.
        """
        return pos in self.cleanDict
    
##a = RectangularRoom(5,5)       

class BaseRobot(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 BaseRobot 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 d and a
        random position p in the room.

        The direction d is an integer satisfying 0 <= d < 360; it
        specifies an angle in degrees.

        p is a Position object giving the robot's position.

        room:  a RectangularRoom object.
        speed: a float (speed > 0)
        """
        self.room = room
        self.speed = float(speed)
        self.direction = random.randint(0, 359)
        self.position = self.room.getRandomPosition()
        
    def getRobotPosition(self):
        """
        Return the position of the robot.

        returns: a Position object giving the robot's position.
        """
        return self.position
        
    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
        
    def setRobotPosition(self, position):
        """
        Set the position of the robot to POSITION.

        position: a Position object.
        """
        self.position = position
        
    def setRobotDirection(self, direction):
        """
        Set the direction of the robot to DIRECTION.

        direction: integer representing an angle in degrees
        """
        self.direction = direction

##b = BaseRobot(a, 1)

class Robot(BaseRobot):
    """
    A Robot is a BaseRobot with the standard movement strategy.

    At each time-step, a Robot attempts to move in its current
    direction; when it hits a wall, it chooses a new direction
    randomly.
    """
    def updatePositionAndClean(self):
        """
        Simulate the passage of a single time-step.

        Move the robot to a new position and mark the tile it is on as having
        been cleaned.
        """
        # get new position
        self.new_x, self.new_y = Position(self.getRobotPosition()[0], self.getRobotPosition()[1]).getNewPosition(self.getRobotDirection(), self.speed)
        # checking for negative establishes left and bottom boundaries
        if self.new_x < 0 or self.new_y < 0:
            # if the new position is out of the room, change direction and do it again
            self.direction = random.randint(0, 359)
            self.updatePositionAndClean() 
        # if it's in the room clean it. int rounds down to whole number.
        elif self.room.isPositionInRoom((int(self.new_x), int(self.new_y))): 
            self.setRobotPosition((self.new_x, self.new_y)) # position must be kept as float, otherwise distance will keep being reset
            self.room.cleanTileAtPosition((int(self.new_x), int(self.new_y))) # coordinates are integers. must keep with convention.
        else:
            # if the new position is out of the room, change direction and do it again
            self.direction = random.randint(0, 359)
            self.updatePositionAndClean()

##c = Robot(a, 1)

# === Problem 3

def runSimulation(num_robots, width, height, speed, min_coverage, num_trials,
                  robot_type, visualize):
    """
    Runs NUM_TRIALS trials of the simulation and returns a list of
    lists, one per trial. The list for a trial has an element for each
    timestep of that trial, the value of which is the percentage of
    the room that is clean after that timestep. Each trial stops when
    MIN_COVERAGE of the room is clean.

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

    Visualization is turned on when boolean VISUALIZE is set to True.

    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. Robot or
                RandomWalkRobot)
    visualize: a boolean (True to turn on visualization)
    """
    anim = None
    robotDict = {}
    simlist = []
    trial = 0
    while trial < num_trials:
        room = RectangularRoom(width, height)
        # create a dictionary of numbered robots of length num_robots.
        # a list of variables would not work for this task. The variables were
        # cleared when passed to runtrial
        for ronum in range(num_robots):
            robotDict[ronum] = robot_type(room, speed)
        if visualize:
            anim = ps11_visualize_amended.RobotVisualization(num_robots, width, height)
        # append the trial's resultant list to simlist
        simlist.append(runtrial(room, robotDict, min_coverage, anim, visualize))
        trial += 1 # go to next trial
        if visualize:
            anim.done()  
    return simlist
    
def runtrial(room, robotDict, min_coverage, anim, visualize):
    """Returns the list for one trial run"""
    triallist = []
    percentclean = 0
    totaltiles = room.getNumTiles()
    while percentclean < min_coverage:
        # run num_robots simultaneously in time step
        for robot in robotDict:
            robotDict[robot].updatePositionAndClean()
        percentclean = round(float(room.getNumCleanedTiles())/totaltiles, 2)
        triallist.append(percentclean)
        robots = robotDict.values()
        if visualize:
            anim.update(room, robots)
    return triallist

##d = runSimulation(5, 20, 20, 1, .9, 1, Robot, True)
    
# === Provided function

def computeMeans(list_of_lists):
    """
    Returns a list as long as the longest list in LIST_OF_LISTS, where
    the value at index i is the average of the values at index i in
    all of LIST_OF_LISTS' lists.

    Lists shorter than the longest list are padded with their final
    value to be the same length.
    """
    # Find length of longest list
    longest = 0
    for lst in list_of_lists:
        if len(lst) > longest:
           longest = len(lst)
    # Get totals
    tots = [0]*(longest)
    for lst in list_of_lists:
        for i in range(longest):
            if i < len(lst):
                tots[i] += lst[i]
            else:
                tots[i] += lst[-1]
    # Convert tots to an array to make averaging across each index easier
    tots = pylab.array(tots)
    # Compute means
    means = tots/float(len(list_of_lists))
    return means

##q = [[ 0.11,  0.22,  0.33],[ 0.11,   0.198,  0.308,  0.396,  0.44 ],[ 0.11,   0.22,   0.33,   0.418,  0.488,  0.536,  0.536,  0.536,  0.536,  0.56 ]]
##p = computeMeans(q)

# === Helper Function

def avgTrialTime(simulation):
    """Returns the average number of time steps (an integer) for a simulation"""
    # get the number of trials run
    num_lists = float(len(simulation)) 
    total_length = 0
    # get the total number of time steps from one trial
    for result in simulation:
        total_length += len(result)
    # return the average number of time steps per trial
    return round(total_length/num_lists, 2)

# === Problem 4

def showPlot1():
    """
    Produces a plot showing dependence of cleaning time on room size.
    """
    roomside = [5, 10, 15, 20, 25]
    lengthlist = []
    roomarea = []
    for side in roomside:
        tempsim = runSimulation(3, side, side, 1, .75, 20, Robot, False)
        roomarea.append(side*side)
        lengthlist.append(avgTrialTime(tempsim))
    pylab.figure()
    pylab.plot(roomarea, lengthlist)
    pylab.axis([25, 650, 0, 350])
    pylab.ylabel('Cleaning Time')
    pylab.xlabel('Room Area')
    pylab.title('Average Time to Clean 75% of Various Room Sizes with 3 Robots')
    pylab.show()

##showPlot1()

def showPlot2():
    """
    Produces a plot showing dependence of cleaning time on number of robots.
    """
    robotlist = list(range(1, 11))
    lengthlist = []
    for robot in robotlist:
        tempsim = runSimulation(robot, 25, 25, 1, .75, 20, Robot, False)
        lengthlist.append(avgTrialTime(tempsim))
    pylab.figure()
    pylab.plot(robotlist,lengthlist, 'ro')
    pylab.axis([1, 10, 0, 1000])
    pylab.ylabel('Cleaning Time')
    pylab.xlabel('Number of Robots')
    pylab.title('Average Time to Clean 75% of a 25x25 Room with 1-10 Robots')
    pylab.show()

##showPlot2()

def showPlot3():
    """
    Produces a plot showing dependence of cleaning time on room shape.
    """
    widthlist = [20, 25, 40, 50, 80, 100]
    roomlist = []
    lengthlist = []
    for width in widthlist:
        roomlist.append((width, 400/width))
    for room in roomlist:
        tempsim = runSimulation(2, room[0], room[1], 1, .75, 50, Robot, False)
        lengthlist.append(avgTrialTime(tempsim))
    pylab.figure()
    pylab.plot(widthlist,lengthlist)
    pylab.axis([20, 100, 250, 400])
    pylab.ylabel('Cleaning Time')
    pylab.xlabel('Room Width')
    pylab.title('Average Time to Clean 75% of a Various Room Shapes with Same Area using 2 Robots')
    pylab.show()

##showPlot3()
    
def showPlot4():
    """
    Produces a plot showing cleaning time vs. percentage cleaned, for
    each of 1-5 robots.
    """
    lengthDict = {}
    x = [25, 40, 55, 70, 85]
    for robot in range(1, 6):
        templength = []
        for coverage in x:
            tempsim = runSimulation(robot, 25, 25, 1, coverage/100.0, 20, Robot, False)
            templength.append(avgTrialTime(tempsim))
        lengthDict[robot] = templength
    pylab.figure()
    pylab.plot(x, lengthDict[1], 'r', x, lengthDict[2], 'b', x, lengthDict[3], 'g', \
               x, lengthDict[4], 'k', x, lengthDict[5], 'y')
    pylab.legend(('1 robot', '2 robots', '3 robots', '4 robots', '5 robots'), loc = 2)
    pylab.axis([20, 90, 0, 1275])
    pylab.ylabel('Mean Cleaning Time')
    pylab.xlabel('Percent Cleaned')
    pylab.title('Average Time to Clean Various Percentages of a 25x25 Room with 1-5 Robots')
    pylab.show()

##showPlot4()

# === Problem 5

class RandomWalkRobot(BaseRobot):
    """
    A RandomWalkRobot is a robot with the "random walk" movement
    strategy: it chooses a new direction at random after each
    time-step.
    """
    def updatePositionAndClean(self):
        """
        Simulate the passage of a single time-step.

        Move the robot to a new position and mark the tile it is on as having
        been cleaned. Choose a new random direction after each move.
        """
        # get new position
        self.new_x, self.new_y = Position(self.getRobotPosition()[0], self.getRobotPosition()[1])\
                                 .getNewPosition(self.getRobotDirection(), self.speed)
        # checking for negative establishes left and bottom boundaries
        if self.new_x < 0 or self.new_y < 0:
            # if the new position is out of the room, change direction and do it again
            self.direction = random.randint(0, 359)
            self.updatePositionAndClean() 
        # if it's in the room clean it. int rounds down to whole number.
        elif self.room.isPositionInRoom((int(self.new_x), int(self.new_y))): 
            self.setRobotPosition((self.new_x, self.new_y)) # position must be kept as float, otherwise distance will keep being reset
            self.room.cleanTileAtPosition((int(self.new_x), int(self.new_y))) # coordinates are integers. must keep with convention.
            self.direction = random.randint(0, 359) # choose a random direction after cleaning
        else:
            # if the new position is out of the room, change direction and do it again
            self.direction = random.randint(0, 359)
            self.updatePositionAndClean()

##e = runSimulation(1, 20, 20, 1, .9, 1, RandomWalkRobot, True)
            
# === Problem 6

def showPlot5():
    """
    Produces a plot comparing the two robot strategies. Shows the average time
    to clean 75% of a 20x20 room as a function of number of robots.    

    It seems as though the most telling comparison is between the two approaches
    is to between time and robot density. For a fixed room area, robot density
    translates to number of robots.

    RandomWalk is inferior, because it has too much repetition.
    """
    robotlist = list(range(1, 11))
    lengthlistReg = []
    lengthlistRandom = []
    for robot in robotlist:
        tempsimReg = runSimulation(robot, 20, 20, 1, .75, 20, Robot, False)
        tempsimRandom = runSimulation(robot, 20, 20, 1, .75, 20, RandomWalkRobot, False)
        lengthlistReg.append(avgTrialTime(tempsimReg))
        lengthlistRandom.append(avgTrialTime(tempsimRandom))
    pylab.figure()
    pylab.plot(robotlist,lengthlistReg, 'ro', robotlist,lengthlistRandom, 'bo')
    pylab.axis([1, 10, 0, 1700])
    pylab.legend(('NormalWalk' ,'RandomWalk'), loc = 1)
    pylab.ylabel('Cleaning Time')
    pylab.xlabel('Number of Robots')
    pylab.title('Average Time to Clean 75% of a 20x20 Room with 1-10 Robots')
    pylab.show()

##showPlot5()



ps_11_visualize_amended
# Problem Set 11:
# Visualization code for simulated robots.
#
# See the problem set for instructions on how to use this code.

import math
import time

from Tkinter import *

class RobotVisualization:
    def __init__(self, num_robots, width, height, delay = 0.2):
        "Initializes a visualization with the specified parameters."
        # Number of seconds to pause after each frame
        self.delay = delay

        self.max_dim = max(width, height)
        self.width = width
        self.height = height
        self.num_robots = num_robots

        # Initialize a drawing surface
        self.master = Tk()
        self.w = Canvas(self.master, width=500, height=500)
        self.w.pack()
        self.master.update()

        # Draw a backing and lines
        x1, y1 = self._map_coords(0, 0)
        x2, y2 = self._map_coords(width, height)
        self.w.create_rectangle(x1, y1, x2, y2, fill = "white")

        # Draw gray squares for dirty tiles
        self.tiles = {}
        for i in range(width):
            for j in range(height):
                x1, y1 = self._map_coords(i, j)
                x2, y2 = self._map_coords(i + 1, j + 1)
                self.tiles[(i, j)] = self.w.create_rectangle(x1, y1, x2, y2,
                                                             fill = "gray")

        # Draw gridlines
        for i in range(width + 1):
            x1, y1 = self._map_coords(i, 0)
            x2, y2 = self._map_coords(i, height)
            self.w.create_line(x1, y1, x2, y2)
        for i in range(height + 1):
            x1, y1 = self._map_coords(0, i)
            x2, y2 = self._map_coords(width, i)
            self.w.create_line(x1, y1, x2, y2)

        # Draw some status text
        self.robots = None
        self.text = self.w.create_text(25, 0, anchor=NW,
                                       text=self._status_string(0, 0))
        self.time = 0
        self.master.update()

    def _status_string(self, time, num_clean_tiles):
        "Returns an appropriate status string to print."
        percent_clean = 100 * num_clean_tiles / (self.width * self.height)
        return "Time: %04d; %d tiles (%d%%) cleaned" % \
            (time, num_clean_tiles, percent_clean)

    def _map_coords(self, x, y):
        "Maps grid positions to window positions (in pixels)."
        return (250 + 450 * ((x - self.width / 2.0) / self.max_dim),
                250 + 450 * ((self.height / 2.0 - y) / self.max_dim))

    def _draw_robot(self, position, direction):
        "Returns a polygon representing a robot with the specified parameters."
        x, y = position[0], position[1]
        d1 = direction + 165
        d2 = direction - 165
        x1, y1 = self._map_coords(x, y)
        x2, y2 = self._map_coords(x + 0.6 * math.sin(math.radians(d1)),
                                  y + 0.6 * math.cos(math.radians(d1)))
        x3, y3 = self._map_coords(x + 0.6 * math.sin(math.radians(d2)),
                                  y + 0.6 * math.cos(math.radians(d2)))
        return self.w.create_polygon([x1, y1, x2, y2, x3, y3], fill="red")

    def update(self, room, robots):
        "Redraws the visualization with the specified room and robot state."
        # Removes a gray square for any tiles have been cleaned.
        for i in range(self.width):
            for j in range(self.height):
                if room.isTileCleaned(i, j):
                    self.w.delete(self.tiles[(i, j)])
        # Delete all existing robots.
        if self.robots:
            for robot in self.robots:
                self.w.delete(robot)
                self.master.update_idletasks()
        # Draw new robots
        self.robots = []
        for robot in robots:
            pos = robot.getRobotPosition()
            x, y = pos[0], pos[1]
            x1, y1 = self._map_coords(x - 0.08, y - 0.08)
            x2, y2 = self._map_coords(x + 0.08, y + 0.08)
            self.robots.append(self.w.create_oval(x1, y1, x2, y2,
                                                  fill = "black"))
            self.robots.append(
                self._draw_robot(robot.getRobotPosition(), robot.getRobotDirection()))
        # Update text
        self.w.delete(self.text)
        self.time += 1
        self.text = self.w.create_text(
            25, 0, anchor=NW,
            text=self._status_string(self.time, room.getNumCleanedTiles()))
        self.master.update()
        time.sleep(self.delay)

    def done(self):
        "Indicate that the animation is done so that we allow the user to close the window."
        mainloop()




BTheMad (Self-grade: Pretty good)
Submitted 1 year ago | Permalink
# Problem Set 11: Simulating robots
# Name: Alex

import math
import ps11_visualize
from matplotlib import pyplot
import random
# === Provided classes
class Position(object):
    """
    A Position represents a location in a two-dimensional room.
    """
    def __init__(self, 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):
        old_x, old_y = self.getX(), self.getY()
        # 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)

# === Problems 1 and 2

class RectangularRoom(object):
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.tiles = {}
        for i in range(self.width):
            for j in range(self.height):
                self.tiles[(i, j)] = False

    def cleanTileAtPosition(self, pos):
        self.tiles[int(pos.getX()), int(pos.getY())] = True
        
    def isTileCleaned(self, m, n):
        return self.tiles[m, n]

    def getNumTiles(self):
        return len(self.tiles)
    
    def getNumCleanedTiles(self):
        return len(filter((lambda k: self.tiles[k] == True), self.tiles))
    
    def getRandomPosition(self):
        x = random.choice(range(self.width))
        y = random.choice(range(self.height))
        return Position(x, y)

    def isPositionInRoom(self, pos):
        if pos.getX() > 0 and pos.getY() > 0 and \
           pos.getX() < self.width and pos.getY() < self.height:
            return True
        else:
            return False


class BaseRobot(object):
    def __init__(self, room, speed):
        self.room = room
        self.speed = speed
        self.p = self.room.getRandomPosition()
        self.d = random.choice(range(360))

    def getRobotPosition(self):
        return self.p
    
    def getRobotDirection(self):
        return self.d
    
    def setRobotPosition(self, position):
        self.p = position
    
    def setRobotDirection(self, direction):
        self.d = direction


class Robot(BaseRobot):
    def updatePositionAndClean(self):
        while 1:
            tmp_p = self.p.getNewPosition(self.d, self.speed)
            if self.room.isPositionInRoom(tmp_p):
                break
            else:
                self.d = (random.choice(range(360)))
        self.p = tmp_p
        self.room.cleanTileAtPosition(self.p)

# === Problem 3
def runSimulation(num_robots, speed, width, height, min_coverage, num_trials,
                  robot_type, visualize):
    """
    Runs NUM_TRIALS trials of the simulation and returns a list of
    lists, one per trial. The list for a trial has an element for each
    timestep of that trial, the value of which is the percentage of
    the room that is clean after that timestep. Each trial stops when
    MIN_COVERAGE of the room is clean.

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

    Visualization is turned on when boolean VISUALIZE is set to True.

    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. Robot or
                RandomWalkRobot)
    visualize: a boolean (True to turn on visualization)
    """
    result = []
    # anim = ps11_visualize.RobotVisualization(num_robots, width, height)
    for i in range(0, num_trials):
#        anim = ps11_visualize.RobotVisualization(num_robots, width, height)
        # Initialize room and robots
        room = RectangularRoom(width, height)
        robots = []
        for j in range(0, num_robots):
            robots.append(robot_type(room, speed))
        # Run the simulation
        room_coverrage = 0
        trial_result = []
        while room_coverrage < min_coverage:
            for r in robots:
                r.updatePositionAndClean()
            room_coverrage = (float(room.getNumCleanedTiles()) / room.getNumTiles())
            trial_result.append(room_coverrage)
#            anim.update(room, robots)
        result.append(trial_result)
#        anim.done()
    return result

# === Provided function
def computeMeans(list_of_lists):
    """
    Returns a list as long as the longest list in LIST_OF_LISTS, where
    the value at index i is the average of the values at index i in
    all of LIST_OF_LISTS' lists.

    Lists shorter than the longest list are padded with their final
    value to be the same length.
    """
    # Find length of longest list
    longest = 0
    for lst in list_of_lists:
        if len(lst) > longest:
           longest = len(lst)
    # Get totals
    tots = [0]*(longest)
    for lst in list_of_lists:
        for i in range(longest):
            if i < len(lst):
                tots[i] += lst[i]
            else:
                tots[i] += lst[-1]
    # Convert tots to an array to make averaging across each index easier
    tots = pylab.array(tots)
    # Compute means
    means = tots/float(len(list_of_lists))
    return means

def avg_list_len(list):
    """Documentation"""
    total_lenght = 0.0
    for l in list:
        total_lenght += len(l)
    return total_lenght / len(list)

# === Problem 4
def showPlot1():
    """
    Produces a plot showing dependence of cleaning time on room size.
    """
    num_robots = 1
    num_trials = 5
    speed = 1
    min_coverage = 0.75
    x_values = []
    y_values = []
    for i in range(5, 30, 5):
        y_values.append(avg_list_len(runSimulation(num_robots, speed, i, i,
                        min_coverage, num_trials, Robot, False)))
        x_values.append(i)
    pyplot.plot(x_values, y_values)
    pyplot.xlabel('Room size')
    pyplot.ylabel('Time')
    pyplot.title('Time by room size')
    pyplot.show()

# showPlot1()

def showPlot2():
    """
    Produces a plot showing dependence of cleaning time on number of robots.
    """
    room_width = 25
    room_height = 25
    num_trials = 5
    speed = 1
    min_coverage = 0.75
    x_values = []
    y_values = []
    for i in range(1, 11):
        y_values.append(avg_list_len(runSimulation(i, speed, room_width, room_height,
                        min_coverage, num_trials, Robot, False)))
        x_values.append(i)
    print y_values
    pyplot.plot(x_values, y_values)
    pyplot.xlabel('Robot count')
    pyplot.ylabel('Time')
    pyplot.title('Time by robot count')
    pyplot.show()

#showPlot2()

def showPlot3():
    """
    Produces a plot showing dependence of cleaning time on room shape.
    """
    room_width = [20, 25, 40, 50, 80, 100]
    room_height = [20, 16, 10, 8, 5, 4]
    num_trials = 20
    speed = 1
    min_coverage = 0.75
    x_values = []
    y_values = []
    for i in range(6):
        print i
        tw = room_width[i]
        th = room_height[i]
        y_values.append(avg_list_len(runSimulation(2, speed, tw, th,
                        min_coverage, num_trials, Robot, False)))
        x_values.append(i)
    print y_values
    pyplot.plot(x_values, y_values)
    pyplot.xlabel('Room dims')
    pyplot.ylabel('Time')
    pyplot.title('Time by room shape')
    pyplot.show()

# showPlot3()

def showPlot4():
    """
    Produces a plot showing cleaning time vs. percentage cleaned, for
    each of 1-5 robots.
    """
    room_width = 25
    room_height = 25
    num_trials = 1
    speed = 1
    for r in range(1, 6):
        x_values = []
        y_values = []
        for i in range(1, 101):
            cleanage_per = float(i) / 100
            y_values.append(avg_list_len(runSimulation(r, speed, room_width,
                            room_height, cleanage_per, num_trials, Robot, False)))
            x_values.append(i)
        pyplot.figure(1)
        pyplot.plot(x_values, y_values, label = str(r) + ' robots')
    pyplot.xlabel('% cleanage')
    pyplot.ylabel('Time')
    pyplot.title('Time by % cleaned and robots')
    pyplot.show()

#showPlot4()
# === Problem 5

class RandomWalkRobot(BaseRobot):
    """
    A RandomWalkRobot is a robot with the "random walk" movement
    strategy: it chooses a new direction at random after each
    time-step.
    """
    def updatePositionAndClean(self):
        while 1:
            self.d = (random.choice(range(360)))
            tmp_p = self.p.getNewPosition(self.d, self.speed)
            if self.room.isPositionInRoom(tmp_p):
                break
            else:
                self.d = (random.choice(range(360)))
        self.p = tmp_p
        self.room.cleanTileAtPosition(self.p)

#runSimulation(1, 1, 10, 10, 1, 1, RandomWalkRobot, True)
# === Problem 6

def showPlot5():
    """
    Produces a plot comparing the two robot strategies.
    """
    num_robots = 5
    num_trials = 10
    speed = 1
    min_coverage = 0.75
    x_values = []
    y_values = []
    for i in range(5, 30, 5):
        y_values.append(avg_list_len(runSimulation(num_robots, speed, i, i,
                        min_coverage, num_trials, Robot, False)))
        x_values.append(i)
    pyplot.figure(1)
    pyplot.plot(x_values, y_values, label='Logic')

    x_values = []
    y_values = []
    for i in range(5, 30, 5):
        y_values.append(avg_list_len(runSimulation(num_robots, speed, i, i,
                        min_coverage, num_trials, RandomWalkRobot, False)))
        x_values.append(i)
    pyplot.figure(1)
    pyplot.plot(x_values, y_values, label='Random')
    pyplot.xlabel('Room size')
    pyplot.ylabel('Time')
    pyplot.title('Time by room size')
    pyplot.legend()
    pyplot.show()

showPlot5()