import random import elevator import passenger class Building: """Building with floors and elevators.""" def __init__(self, floors, elevators, pd=None): self.floors = [] for f in range(floors): self.floors.append(Floor(f)) self.elevators = [] for e in range(elevators): self.elevators.append(elevator.Elevator(floors, 20)) # Set up the new passenger probability distribution if pd: self.pd = pd else: self.pd = floors * [ 0.01 ] self.pd[0] = 0.05 self.pdTotal = 0.0 for v in self.pd: self.pdTotal += v def run(self, ticks): "Simulate elevator traffic." for t in ticks: self.tick() def tick(self): "Simulate one step of elevator traffic." for f in self.floors: f.tick() for e in self.elevators: e.tick() self.newPassengers() self.plan() for e in self.elevators: if e.state == elevator.OPEN: self.floors[e.floor].elevatorArrived(e) self.display() def newPassengers(self): "Create new passengers according to probability distribution." for f in range(len(self.floors)): while random.random() < self.pd[f]: dest = self._destination(f) p = passenger.Passenger(f, dest) self.floors[f].addPassenger(p) def _destination(self, f): "Compute destination from floor f." v = random.uniform(0.0, self.pdTotal) n = 0 sum = self.pd[n] while v >= sum: n += 1 sum += self.pd[n] if n == f: return self._destination(f) return n def plan(self): "Plan elevator strategy." # Currently, we use a strategy where all elevators are # managed independently. Obviously, a more sophisticated # strategy can be used to coordinate multiple elevators # so they do not try to service the same floors redundantly. for e in self.elevators: self._planElevator(e) def _planElevator(self, e): "Plan action for a single elevator." if e.state == elevator.OPEN: # First thing we do is close the doors e.nextState = elevator.STOPPED #print "PLAN: close door" elif e.state == elevator.STOPPED: if e.destination[e.floor]: # If someone wants to get here e.nextState = elevator.OPEN #print "PLAN: open door for unloading" elif (not e.isFull() and self.floors[e.floor].want(e.direction)): # If someone wants to get off or on here e.nextState = elevator.OPEN #print "PLAN: open door for loading" elif e.direction == elevator.UP: # We were going up, we keep going up e.nextState = elevator.UP #print "PLAN: keep going up" elif e.direction == elevator.DOWN: # We were going down, we keep going down e.nextState = elevator.DOWN #print "PLAN: keep going down" else: # We were stopped, let's go some # place useful e.nextState = self._getDirection(e.floor) #print "PLAN: get dir", e.nextState else: # Must be UP or DOWN if e.state == elevator.UP: f = e.floor + 1 else: f = e.floor - 1 if e.destination[f]: # Someone wants to get off next e.nextState = elevator.STOPPED #print "PLAN: stop and let people off" elif (not e.isFull() and self.floors[f].want(e.direction)): # Someone wants to get on next e.nextState = elevator.STOPPED #print "PLAN: stop and let people on" elif f == len(self.floors) - 1 or f == 0: # Top or bottom floor e.nextState = elevator.STOPPED #print "PLAN: stop at limit" else: # No change, keep going e.nextState = e.state #print "PLAN: keep going", e.state def _getDirection(self, floor): "Compute direction to go for a stopped elevator." aboveUp = 0 aboveDown = 0 for f in self.floors[floor + 1:]: if f.want(elevator.UP): aboveUp += 1 if f.want(elevator.DOWN): aboveDown += 1 belowUp = 0 belowDown = 0 for f in self.floors[:floor]: if f.want(elevator.UP): belowUp += 1 if f.want(elevator.DOWN): belowDown += 1 above = aboveUp + aboveDown below = belowUp + belowDown if above == below: return elevator.STOPPED elif above > below: return elevator.UP else: return elevator.DOWN def display(self): pass class Floor: """Floor in a building. Keeps track of floor number and passengers waiting for elevator.""" def __init__(self, number): self.number = number self.upPassengers = [] self.downPassengers = [] def tick(self): "Clock tick. Update passengers." for p in self.upPassengers: p.tick() for p in self.downPassengers: p.tick() def want(self, dir): "Check if there are passengers going in given direction." if dir is elevator.UP: return self.upPassengers if dir is elevator.DOWN: return self.downPassengers return self.upPassengers or self.downPassengers def addPassenger(self, p): "Add new passenger on this floor." if p.destination > self.number: self.upPassengers.append(p) else: self.downPassengers.append(p) def elevatorArrived(self, e): "Elevator arrived, try to board passengers." #print "elevatorArrived", self.number self.upPassengers = self._board(e, self.upPassengers) self.downPassengers = self._board(e, self.downPassengers) def _board(self, e, passengers): pList = [] for p in passengers: if not e.board(p): pList.append(p) return pList if __name__ == "__main__": b = Building(14, 1) b.run(12 * 60 * 1) print "%d/%d passengers" % (passenger.count(), passenger.totalCount()) print "average wait time: %f (%f)" % passenger.waitTime() print "average transit time: %f (%f)" % passenger.transitTime()