class ControlHome(Home): def __init__(self, w, h): super().__init__(w,h) self.scenario = Scenario() self.width_bound = [1, w-2] self.height_bound = [1, h-2] #self.presence, self.bulbs = self.scenario.diagonal(self.width, self.height) #self.presence, self.bulbs = self.scenario.corners(self.width, self.height) self.presence, self.bulbs = self.scenario.corners2(self.width, self.height) self.init_figures() # self.fig = plt.figure(figsize=(1, 2)) # # self.fig.add_subplot(121) # plt.imshow(self.presence, cmap='gray', interpolation='nearest', vmin=0, vmax=1) # # self.fig.add_subplot(122) # self.im = plt.imshow(self.luminosity, cmap='gray', interpolation='bilinear', animated=True, vmin=0, vmax=2) def updatefig(self, *args): #self.presence = self.scenario.random(self.width, self.height) for y in range(self.height): for x in range(self.width): if self.presence[x,y] > 0: if self.bulbs[x,y] > -1: self.luminosity[x,y] = random.choice([1,2]) else: self.luminosity[x,y] = 0 self.im.set_data(self.luminosity) self.luminosity_im.set_data(self.luminosity_extrapolate(self.luminosity)) #im.set_cmap("gray") #im.update() self.ani.event_source.stop() plt.grid() plt.grid() return self.im, self.luminosity_im def luminosity_extrapolate(self, luminosity): #there must some function doing this interpolation? for x in range(self.width_bound[0], self.width_bound[1]): for y in range(self.height_bound[0], self.height_bound[1]): if self.bulbs[x,y] > -1 and luminosity[x,y] > 0: block = ((x-1, y-1), (x, y-1), (x+1,y-1), (x+1, y), (x+1, y+1), (x, y+1), (x-1, y+1), (x-1, y)) for point in block: if point[0] in range(self.width_bound[0], self.width_bound[1]) and point[1] in range(self.height_bound[0], self.height_bound[1]): luminosity[point[0],point[1]] += 0.15*luminosity[x,y] return luminosity def run(self): self.ani = animation.FuncAnimation(self.fig, self.updatefig, interval=50, blit=True) plt.show()
class AdaptiveHome(ControlHome): def __init__(self, w, h): super().__init__(w, h) self.width_bound = [1, w - 2] self.height_bound = [1, h - 2] self.scenario = Scenario() #self.presence, self.bulbs = self.scenario.diagonal(self.width, self.height) #self.presence, self.bulbs = self.scenario.corners(self.width, self.height) self.presence, self.bulbs = self.scenario.corners2( self.height, self.width) #self.presence, self.bulbs = self.scenario.stripes(self.width, self.height) self.plans = [] self.generate_plans(20) #------ Fixed Strategies Generator ---------------- # This generates a fixed set of plans based on the scenario def generate_plans(self, num): for i in range(num): luminosity_plan = np.copy(self.luminosity) for y in range(self.height): for x in range(self.width): if self.presence[y, x] > 0: if self.bulbs[y, x] > -1: luminosity_plan[y, x] = 1 #random.choice([1,2]) else: y_near, x_near = self.strategy_find_near_bulb(y, x) #if x_near > -1 and y_near > -1: luminosity_plan[y_near, x_near] = 1 #random.choice([1,2]) #print('bulb found near') #else: # self.luminosity[x,y] = 0 self.plans.append(luminosity_plan) def strategy_find_near_bulb(self, y, x): block = ((y - 1, x - 1), (y, x - 1), (y + 1, x - 1), (y + 1, x), (y + 1, x + 1), (y, x + 1), (y - 1, x + 1), (y - 1, x)) candidate_bulbs = [] point = [-1, -1] for point in block: if point[0] in range(self.height) and point[1] in range( self.width): if self.bulbs[point[0], point[1]] > -1: candidate_bulbs.append(point) if len(candidate_bulbs) > 0: point = random.choice(candidate_bulbs) #print(point) return point[0], point[1] #---------- MAPE--------------- def monitor(self): # the bulbs, presence and luminsity data simulates monitoring self.analyse() def analyse(self): # due to simplicity, the example does not require analysis stage self.plan() def plan(self): if len(self.plans): plans_fitness = [] for plan in self.plans: fitness = self.fitness(plan) print('Fit', fitness) plans_fitness.append((fitness, plan)) plans_sorted = sorted(plans_fitness, key=self.plan_key, reverse=True) self.execute(plans_sorted[0]) else: print('ERROR: MAPE has no pre-defined plans to apply.') def execute(self, plan): self.luminosity = plan[1] print('Plan Fitness:', plan[0]) def plan_key(self, v): return v[0] # ------------- Evaluation ------- def fitness(self, plan): #evalInd4 score = 0 for y in range(self.height): # top left is (X=0,Y=0) for x in range(self.width): if plan[y, x] > 0: presence_score = self.presence_in_radius2(1, x, y, plan) if self.bulbs[y, x] > -1 and presence_score > 0: score += presence_score else: score -= 1 #penalty for using a broken bulb return (float(score), ) def presence_in_radius2(self, radius, x, y, plan): block = ((y - 1, x - 1), (y, x - 1), (y + 1, x - 1), (y + 1, x), (y + 1, x + 1), (y, x + 1), (y - 1, x + 1), (y - 1, x) ) # starts from left top presence_score = 0 if self.presence[y, x] > 0: presence_score += 5 for point in block: if point[0] in range(self.height) and point[1] in range( self.width): if self.presence[point[0], point[1]] > 0: presence_score += 1 return presence_score #-------------- Simulation stuff ------------- def updatefig(self, *args): #self.presence = self.scenario.random(self.width, self.height) #MAPE loop self.monitor() # the MAPE full chain is called from monitor # self.analyse() # self.plan() # self.execute() return super().updatefig(*args) # # visualizationn # self.im.set_data(self.luminosity) # self.luminosity_im.set_data(self.luminosity_extrapolate(self.luminosity)) # #im.set_cmap("gray") # #im.update() # #self.ani.event_source.stop() # plt.grid() # plt.grid() # # # return self.im, self.luminosity_im #im.set_cmap("gray") #im.update() #print("update called") #return self.im,self.luminosity_im def run(self): #self.ani = None self.ani = animation.FuncAnimation(self.fig, self.updatefig, interval=50, blit=True) #self.updatefig() plt.show()
class ControlHome(Home): def __init__(self, w, h): super().__init__(w, h) self.scenario = Scenario() self.width_bound = [1, w - 2] self.height_bound = [1, h - 2] #self.presence, self.bulbs = self.scenario.diagonal(self.width, self.height) #self.presence, self.bulbs = self.scenario.corners(self.width, self.height) self.presence, self.bulbs = self.scenario.corners2( self.height, self.width) self.init_figures() self.steps = 0 self.MAX_STEPS = 1 # self.fig = plt.figure(figsize=(1, 2)) # # self.fig.add_subplot(121) # plt.imshow(self.presence, cmap='gray', interpolation='nearest', vmin=0, vmax=1) # # self.fig.add_subplot(122) # self.im = plt.imshow(self.luminosity, cmap='gray', interpolation='bilinear', animated=True, vmin=0, vmax=2) # as a control system it just reacts to the situations self.react() def react(self): for y in range(self.height): for x in range(self.width): if self.presence[y, x] > 0: if self.bulbs[y, x] > -1: self.luminosity[y, x] = 1 #random.choice([1,2]) else: self.luminosity[y, x] = 0 # ---------- Simulation and visualization elements ---------- def updatefig(self, *args): #self.presence = self.scenario.random(self.width, self.height) #self.solve() print('------STEP-----', self.steps) self.steps += 1 if self.steps > self.MAX_STEPS: self.ani.event_source.stop( ) # it takes a while for the animation loop to notice the event else: self.im.set_data(self.luminosity) self.luminosity_im.set_data( self.luminosity_extrapolate(np.copy(self.luminosity))) plt.grid() plt.grid() #im.set_cmap("gray") #im.update() return self.im, self.luminosity_im # def luminosity_extrapolate(self, luminosity): # #there must some function doing this interpolation? # for x in range(self.width_bound[0], self.width_bound[1]): # for y in range(self.height_bound[0], self.height_bound[1]): # if self.bulbs[x,y] > -1 and luminosity[x,y] > 0: # block = ((x-1, y-1), (x, y-1), (x+1,y-1), (x+1, y), (x+1, y+1), (x, y+1), (x-1, y+1), (x-1, y)) # for point in block: # if point[0] in range(self.width_bound[0], self.width_bound[1]) and point[1] in range(self.height_bound[0], self.height_bound[1]): # luminosity[point[0],point[1]] += 0.15*luminosity[x,y] # return luminosity def luminosity_extrapolate(self, luminosity): #there must some function doing this interpolation? for y in range(self.height_bound[0], self.height_bound[1]): for x in range(self.width_bound[0], self.width_bound[1]): if self.bulbs[y, x] > -1: if luminosity[y, x] > 0: block = ((y - 1, x - 1), (y, x - 1), (y + 1, x - 1), (y + 1, x), (y + 1, x + 1), (y, x + 1), (y - 1, x + 1), (y - 1, x)) for point in block: if point[1] in range( self.width_bound[0], self.width_bound[1]) and point[0] in range( self.height_bound[0], self.height_bound[1]): luminosity[point[0], point[1]] += 0.15 * luminosity[y, x] else: luminosity[y, x] = 0 return luminosity def run(self): self.ani = animation.FuncAnimation(self.fig, self.updatefig, interval=50, blit=True) #self.updatefig() plt.show()
class AdaptiveHome(Home): def __init__(self, w, h): super().__init__(w, h) self.width_bound = [1, w - 2] self.height_bound = [1, h - 2] self.scenario = Scenario() #self.presence, self.bulbs = self.scenario.diagonal(self.width, self.height) #self.presence, self.bulbs = self.scenario.corners(self.width, self.height) self.presence, self.bulbs = self.scenario.corners2( self.height, self.width) #self.presence, self.bulbs = self.scenario.stripes(self.width, self.height) self.init_figures() # self.fig = plt.figure(figsize=(1, 3)) # # self.fig.add_subplot(131) # plt.imshow(self.presence, cmap='gray', interpolation='nearest', vmin=0, vmax=1) # # self.fig.add_subplot(132) # plt.imshow(self.bulbs, cmap='gray', interpolation='nearest', vmin=-1, vmax=0) # # self.fig.add_subplot(133) # self.im = plt.imshow(self.luminosity, cmap='gray', interpolation='bilinear', animated=True, vmin=0, vmax=1) def updatefig(self, *args): #self.presence = self.scenario.random(self.width, self.height) for y in range(self.height): for x in range(self.width): if self.presence[y, x] > 0: if self.bulbs[y, x] > -1: self.luminosity[y, x] = 1 #random.choice([1,2]) else: y_near, x_near = self.strategy_find_near_bulb(y, x) #if x_near > -1 and y_near > -1: self.luminosity[y_near, x_near] = 1 #random.choice([1,2]) #print('bulb found near') #else: # self.luminosity[x,y] = 0 self.im.set_data(self.luminosity) self.luminosity_im.set_data( self.luminosity_extrapolate(self.luminosity)) #im.set_cmap("gray") #im.update() #self.ani.event_source.stop() plt.grid() plt.grid() return self.im, self.luminosity_im #im.set_cmap("gray") #im.update() #print("update called") #return self.im,self.luminosity_im def luminosity_extrapolate(self, luminosity): #there must some function doing this interpolation? for y in range(self.height_bound[0], self.height_bound[1]): for x in range(self.width_bound[0], self.width_bound[1]): if self.bulbs[y, x] > -1: if luminosity[y, x] > 0: block = ((y - 1, x - 1), (y, x - 1), (y + 1, x - 1), (y + 1, x), (y + 1, x + 1), (y, x + 1), (y - 1, x + 1), (y - 1, x)) for point in block: if point[1] in range( self.width_bound[0], self.width_bound[1]) and point[0] in range( self.height_bound[0], self.height_bound[1]): luminosity[point[0], point[1]] += 0.15 * luminosity[y, x] else: luminosity[y, x] = 0 return luminosity def strategy_find_near_bulb(self, y, x): block = ((y - 1, x - 1), (y, x - 1), (y + 1, x - 1), (y + 1, x), (y + 1, x + 1), (y, x + 1), (y - 1, x + 1), (y - 1, x)) candidate_bulbs = [] point = [-1, -1] for point in block: if point[0] in range(self.height) and point[1] in range( self.width): if self.bulbs[point[0], point[1]] > -1: candidate_bulbs.append(point) if len(candidate_bulbs) > 0: point = random.choice(candidate_bulbs) print(point) return point[0], point[1] def run(self): #ani = animation.FuncAnimation(self.fig, self.updatefig, interval=50, blit=True) self.updatefig() plt.show()
class CreativeHome(AdaptiveHome): def __init__(self, w, h): super().__init__(w, h) self.scenario = Scenario() self.width_bound = [1, w - 1] self.height_bound = [1, h - 1] self.evaluation_unit = 10 #Enables/Disables EA self.strategy_aware = True self.goal_aware = True # each goal can be seperately turned on/off self.resource_aware = True #awareness of broken bulbs #awarenss of window and its control system self.context_aware = True #window controls system self.domain_aware = True #window gives light # time awareness: Pattern detected --> the mid section is always empty # Mid section should be ignore in initial population and mutations, it will lead to more efficient strategy generation. it worked! mutate() and generate_individuals() have been updated to reflect this. self.time_aware = True self.time_aware_width_bound = [0, self.width] self.time_aware_height_bound = [ int(self.height * 0.35), int(self.width * 0.65) ] # hypothesis:? self.goals = { 'luminosity': { 'enabled': True, 'maximize': True, 'evaluate': self.evaluate_luminosity }, 'cost': { 'enabled': True, 'maximize': False, 'evaluate': self.evaluate_cost } } self.weight = { 'present': 6.0, #luminosity 'cost': 4.0, 'penalty_broken_bulb': 12.0, 'context': 3.5 #time: } #self.presence, self.bulbs = self.scenario.diagonal(self.width, self.height) #self.presence, self.bulbs = self.scenario.stripes(self.width, self.height) #self.presence, self.bulbs = self.scenario.corners(self.width, self.height) self.presence, self.bulbs = self.scenario.corners2( self.width, self.height) #self.presence, self.bulbs = self.scenario.extreme(self.width, self.height) if self.strategy_aware: self.init_deap() #self.init_figures() #----- Strategy-Awareness ----- def generate_individual(self, icls): luminosity = np.zeros((self.height, self.width)) for y in range(self.height_bound[0], self.height_bound[1]): for x in range(self.width_bound[0], self.width_bound[1]): luminosity[y, x] = random.choice([0, 1]) #print ('mutating') genome = self.encode(luminosity) individual = icls(genome) # Time-Awareness, Code Injection into Strategy-Awareness # Time awareness detected a pattern, the mid section is always empty and therefore should remain untouched if self.time_aware: individual = self.apply_mid_section_empty_learning(individual) return individual def mutate(self, individual): #print('Mutating') #print('Mutating') luminosity = self.decode(individual) for i in range(self.width): x = random.randint(self.width_bound[0], self.width_bound[1]) y = random.randint(self.height_bound[0], self.height_bound[1]) if (self.bulbs[y, x] > -1): luminosity[y, x] = random.choice([0, 1]) #luminosity = self.encode (luminosity) self.update_individual(individual, luminosity) #for i in range(self.width): # individual[random.randint(0,len(individual)-1)] = random.choice([0,1]) #return (individual,) #individual = super().mutate(individual)[0] #print("Before Mutate ", individual) if self.time_aware: individual = self.apply_mid_section_empty_learning(individual) #print("MUTATE_IND", individual) return (individual, ) # Time-Awareness, Mid section is always empty, make strategy awareness (EA) ignore it def apply_mid_section_empty_learning(self, individual): #print ("INDIVIDUAL",individual) luminosity = self.decode(individual) #print("LUMINOSITY", luminosity) for y in range(self.time_aware_height_bound[0], self.time_aware_height_bound[1]): for x in range(self.time_aware_width_bound[0], self.time_aware_width_bound[1]): luminosity[y, x] = 0 self.update_individual(individual, luminosity) #print("UPDATED IND", individual) return individual # def generate_individual(self,icls): # luminosity = np.zeros((self.height,self.width)) # # for y in range(self.height_bound[0], self.height_bound[1]): # for x in range(self.width_bound[0], self.width_bound[1]): # luminosity[y,x] = random.choice([0,1]) # #print ('mutating') # # genome = self.encode(luminosity) # return icls(genome) def init_deap(self): creator.create("FitnessMax", base.Fitness, weights=(1.0, )) creator.create("Individual", list, fitness=creator.FitnessMax) self.steps = 0 self.MAX_STEPS = 500 self.toolbox = base.Toolbox() self.toolbox.register("random_num", random.choice, [0, 1]) self.DIM = self.width * self.height self.toolbox.register( "individual", self.generate_individual, #tools.initRepeat, creator.Individual #self.toolbox.random_num, #n=self.DIM ) print(self.toolbox.individual()) self.toolbox.register("population", tools.initRepeat, list, self.toolbox.individual) self.toolbox.register("mate", tools.cxTwoPoint) self.toolbox.register("select", tools.selBest) self.toolbox.register("evaluate", self.fitness_ea) self.toolbox.register("mutate", self.mutate) self.pop = self.toolbox.population(n=100) # def fitness_(self, individual, data): # print (data) # print (individual) # match = 0 # #for selected, bulbs in zip(individual, data): # #if selected: # for x in range(self.width): # for y in range(self.height): # if self.presence[x,y]>0 and individual[x,y]>0: # match += 1 # match_percentage = match / self.width * self.height # return match_percentage # def fitness(self, individual):#evalInd4 # score = 0 # #for selected, bulbs in zip(individual, data): # #if selected: # for y in range(self.height): # top left is (X=0,Y=0) # for x in range(self.width): # # if individual[y*self.width+x]>0: # #if self.presence_in_radius(1, x, y): # presence_score = self.presence_in_radius2(1, x, y, individual) # if self.bulbs[x,y] > -1 and presence_score > 0: # score += presence_score # else: # score -= 1 #penalty for using a broken bulb # return (float(score),) #------------------ goal awareness stuff starts ----------------- def evaluate_luminosity(self, plan): score = 0 #for selected, bulbs in zip(individual, data): #if selected: for y in range(self.height): # top left is (X=0,Y=0) for x in range(self.width): if plan[y, x] > 0: #if self.presence_in_radius(1, x, y): presence_score = self.presence_in_radius2(1, x, y, plan) #if self.bulbs[x,y] > -1 and presence_score > 0: #if presence_score > 0: score += presence_score #else: #penalty for using a broken bulb # score -= int( self.weight['penalty_broken_bulb'] * 1) return score def evaluate_cost(self, plan): cost_score = 0 #for selected, bulbs in zip(individual, data): #if selected: for y in range(self.height): # top left is (X=0,Y=0) for x in range(self.width): if plan[y, x] > 0 and self.bulbs[y, x] > -1: #presence_score = self.presence_in_radius2(1, x, y, individual) #if presence_score is 0: cost_score += int(self.weight['cost'] * self.evaluation_unit) return cost_score # ------- main fitness function ------- def fitness_ea(self, individual): return (float(self.fitness(self.decode(individual))), ) def fitness(self, plan): #print('goal based fitness') fitness = 0 if self.goal_aware: for goal_name, goal in self.goals.items(): if goal['enabled']: if goal['maximize']: fitness = fitness + goal['evaluate'](plan) else: fitness = fitness - goal['evaluate'](plan) #else: # fitness += super().fitness(individual)[0] if self.resource_aware: fitness += self.evaluate_resource(plan) if self.domain_aware and self.context_aware: fitness += self.evaluate_domain_context(plan) return fitness # def fitness(self, individual):#evalInd4 # score = 0 # #for selected, bulbs in zip(individual, data): # #if selected: # for y in range(self.height): # top left is (X=0,Y=0) # for x in range(self.width): # # if individual[y*self.width+x]>0: # #if self.presence_in_radius(1, x, y): # presence_score = self.presence_in_radius2(1, x, y, individual) # if self.bulbs[y,x] > -1 and presence_score > 0: # score += presence_score # else: # score -= 1 #penalty for using a broken bulb # return (float(score),) def presence_in_radius2(self, radius, x, y, individual): block = ((y - 1, x - 1), (y, x - 1), (y + 1, x - 1), (y + 1, x), (y + 1, x + 1), (y, x + 1), (y - 1, x + 1), (y - 1, x) ) # starts from left top presence_score = 0 if self.presence[y, x] > 0: presence_score += int( self.weight['present'] * self.evaluation_unit) #award for presence under the bulb #print ('+50') for point in block: if point[0] in range(self.height) and point[1] in range( self.width): if self.presence[point[0], point[1]] > 0: #and self.bulbs[point[0],point[1]] < 0: #individual[point[0]*self.width+point[1]] < 1:#someone present in radius and that area is dark presence_score += int(0.30 * self.weight['present'] * self.evaluation_unit) return presence_score # def mutate(self, individual): # #print('Mutating') # luminosity = self.decode(individual) # # for i in range(self.width): # x = random.randint(self.width_bound[0], self.width_bound[1]) # y = random.randint(self.height_bound[0], self.height_bound[1]) # # if(self.bulbs[y,x] > -1): # luminosity[y,x] = random.choice([0,1]) # # #luminosity = self.encode (luminosity) # self.update_individual(individual, luminosity) # #for i in range(self.width): # # individual[random.randint(0,len(individual)-1)] = random.choice([0,1]) # return (individual,) def update_individual(self, individual, data): for y in range(self.height): for x in range(self.width): individual[y * self.width + x] = data[y, x] def encode(self, luminosity): return luminosity.flatten() def decode(self, individual): plan = np.zeros((self.height, self.width)) for y in range(self.height): # top left is (X=0,Y=0) for x in range(self.width): plan[y, x] = individual[y * self.width + x] return plan #np.reshape(individual, (-1, self.width)) #------------------ simulation loop ---------------- def updatefig(self, *args): if self.strategy_aware: algorithms.eaMuPlusLambda( self.pop, self.toolbox, 400, 100, #parents, children 0.5, 0.5, #probabilities 1) #iterations top_plan = sorted(self.pop, key=lambda x: x.fitness.values[0])[-1] fit = top_plan.fitness.values[0] print('generation:{}, best fitness-: {}'.format(self.steps, fit)) #print("TOP:", top) #self.luminosity = self.decode(top) # TODO: In a realistic setting, EA will generate bunch of top plans # that will be pushed to the plans storage and then MAPE will # select the best one at the end of EA. EA could be running in # background in a realistic setup, always pushing new plans to the # plan storage self.plans = [self.decode(top_plan)] # #print("DRAW:",self.luminosity) # self.im.set_data(self.luminosity) # #self.im.set_array(self.luminosity) # #self.im.set_facecolors(self.luminosity) # # # self.luminosity_im.set_data(self.luminosity_extrapolate(self.luminosity)) # #im.set_cmap("gray") # #im.update() # self.steps += 1 # if self.steps > self.MAX_STEPS: # self.ani.event_source.stop() # plt.grid() # plt.grid() return super().updatefig(args) #return self.im, self.luminosity_im #------- domain and context, access window control, use windows -------------- def luminosity_extrapolate(self, luminosity): if self.domain_aware and self.context_aware: #add windows as bulbs at the edges of top-left corner for y in range(int(self.height * 0.5)): # top left is (X=0,Y=0) luminosity[y, 0] = 1 luminosity[y, 1] = 1 for x in range(int(self.width * 0.5)): luminosity[0, x] = 1 luminosity[1, x] = 1 #there must some function doing this interpolation? for y in range(self.height_bound[0], self.height_bound[1]): for x in range(self.width_bound[0], self.width_bound[1]): if self.bulbs[y, x] > -1: if luminosity[y, x] > 0: block = ((y - 1, x - 1), (y, x - 1), (y + 1, x - 1), (y + 1, x), (y + 1, x + 1), (y, x + 1), (y - 1, x + 1), (y - 1, x)) for point in block: if point[1] in range( self.width_bound[0], self.width_bound[1]) and point[0] in range( self.height_bound[0], self.height_bound[1]): luminosity[point[0], point[1]] += 0.15 * luminosity[y, x] else: luminosity[y, x] = 0 return luminosity #------- resoruce awareness def evaluate_resource(self, plan): # probe: luminosity sensors are resources too, may be merge prob-aw into resources. lumisity array is kind of probes score = 0 #for selected, bulbs in zip(individual, data): #if selected: for y in range(self.height): # top left is (X=0,Y=0) for x in range(self.width): if plan[y, x] > 0 and self.bulbs[y, x] == -1: score += int(self.weight['penalty_broken_bulb'] * self.evaluation_unit) #penalty, -1 makes it reduce the fitness of individuals using broken bulbs return -1 * score def evaluate_domain_context(self, plan): #domain: windows are source of light #context: windows can be controlled for light # assumption: one quarter of the space can be lit with windows there, left-top here score = 0 for y in range(self.height): # top left is (X=0,Y=0) for x in range(self.width): if x < int(0.5 * self.width) and y < int( 0.5 * self.height): #left-top area if plan[y, x] > 0: score += int(self.weight['context'] * self.evaluation_unit) #penalty for using bulbs in near window lit area return -1 * score