Package horizons :: Package util :: Module buildingindexer
[hide private]
[frames] | no frames]

Source Code for Module horizons.util.buildingindexer

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22   
23 -class BuildingIndexer:
24 """ 25 Indexes a subset of the buildings on an island to improve nearby building 26 lookup performance. 27 28 Used to answer queries of the form 'I am at (x, y), where is the closest / random 29 building that provides resource X in my range'. 30 """ 31
32 - def __init__(self, radius, coords_list, random=None, buildings=None):
33 """ 34 Create a BuildingIndexer 35 @param radius: int, maximum required radius of the buildings 36 @param coords_list: list of tuples, the coordinates of the island 37 @param random: the rng of the session 38 @param buildings: initial list of buildings. Will only be read. 39 """ 40 self.radius = radius 41 self._map = {} 42 for coords in coords_list: 43 self._map[coords] = BuildingIndex(coords, random) 44 self._add_set = set() 45 self._remove_set = set() 46 self._changed = False 47 48 if buildings: 49 self._update(add_buildings=buildings, initial=True)
50
51 - def add(self, building):
52 self._remove_set.discard(building) 53 self._add_set.add(building) 54 self._changed = True
55
56 - def remove(self, building):
57 self._add_set.discard(building) 58 self._remove_set.add(building) 59 self._changed = True
60
61 - def _update(self, add_buildings=None, initial=False):
62 """ 63 @param add_buildings: Don't use unless you know why. 64 @param initial: can be set on first call as optimization 65 """ 66 for building in self._remove_set: 67 for coords in building.position.get_radius_coordinates(self.radius, include_self=True): 68 try: 69 index = self._map[coords] 70 except KeyError: 71 continue # should be faster than contains check, since usually True 72 index._remove_set.add(building) 73 index._add_set.discard(building) 74 index._changed = True 75 76 if not add_buildings: 77 add_buildings = self._add_set 78 for building in add_buildings: 79 for coords in building.position.get_radius_coordinates(self.radius, include_self=True): 80 try: 81 index = self._map[coords] 82 except KeyError: 83 continue # should be faster than contains check, since usually True 84 if not initial: 85 index._remove_set.discard(building) 86 index._add_set.add(building) 87 index._changed = True 88 89 self._changed = False 90 self._add_set.clear() 91 self._remove_set.clear()
92
93 - def get_buildings_in_range(self, coords):
94 """ 95 Returns all buildings in range in the form of a Building generator 96 @param coords: tuple, the point around which to get the buildings 97 """ 98 if coords in self._map: 99 if self._changed: 100 self._update() 101 return self._map[coords].get_buildings_in_range() 102 return []
103
104 - def get_random_building_in_range(self, coords):
105 """ 106 Returns a random building in range or None if one doesn't exist 107 Don't use this for user interactions unless you want to break multiplayer 108 @param coords: tuple, the point around which to get the building 109 """ 110 if coords in self._map: 111 if self._changed: 112 self._update() 113 return self._map[coords].get_random_building_in_range() 114 return None
115
116 - def get_num_buildings_in_range(self, coords):
117 """ 118 Returns the number of buildings in range of the position 119 @param coords: tuple, the center point 120 """ 121 if coords in self._map: 122 if self._changed: 123 self._update() 124 return self._map[coords].get_num_buildings_in_range()
125 126
127 -class BuildingIndex:
128 """ 129 Indexes buildings around a tile to improve nearby building lookup speed. 130 The code isn't particularly pretty for performance reasons. 131 """ 132
133 - def __init__(self, coords, random):
134 self._coords = coords 135 self._random = random 136 self._add_set = set() 137 self._remove_set = set() 138 self._list = [] 139 self._changed = False
140
141 - def _update(self):
142 new_list = [] 143 for element in self._list: 144 if element[5] not in self._remove_set: 145 new_list.append(element) 146 147 x = self._coords[0] 148 y = self._coords[1] 149 for building in self._add_set: 150 pos = building.position 151 left = pos.left 152 right = pos.right 153 top = pos.top 154 bottom = pos.bottom 155 156 x_diff = left - x 157 if x_diff < x - right: 158 x_diff = x - right 159 if x_diff < 0: 160 x_diff = 0 161 162 y_diff = top - y 163 if y_diff < y - bottom: 164 y_diff = y - bottom 165 if y_diff < 0: 166 y_diff = 0 167 168 new_list.append((x_diff * x_diff + y_diff * y_diff, top, bottom, left, right, building)) 169 170 self._list = new_list 171 self._list.sort() 172 173 self._add_set.clear() 174 self._remove_set.clear() 175 self._changed = False
176
177 - def get_buildings_in_range(self):
178 if self._changed: 179 self._update() 180 for element in self._list: 181 yield element[5]
182
184 if self._changed: 185 self._update() 186 if self._list: 187 return self._random.choice(self._list)[5] 188 return None
189
191 if self._changed: 192 self._update() 193 return len(self._list)
194