def get_ground_units(self, position=None, radius=None): """@see get_ships""" if position is not None and radius is not None: circle = Circle(position, radius) return [unit for unit in self.ground_units if circle.contains(unit.position)] else: return self.ground_units
def testCircle(self): c1 = Circle(Point(0,0), 1) c2 = Circle(Point(0,0), 2) c3 = Circle(Point(0,0), 0) self.assertFalse(c1 == c2) self.assertTrue(c1 != c2) self.assertNotEqual(c1, c2) self.assertEqual(c1.get_coordinates(), [(-1, 0), (0, -1), (0, 0), (0, 1), (1, 0)]) self.assertEqual(c3.get_coordinates(), [(0,0)])
def get_ground_units(self, position=None, radius=None): """@see get_ships""" if position is not None and radius is not None: circle = Circle(position, radius) units = [] for unit in self.ground_units: if circle.contains(unit.position): units.append(unit) return units else: return self.ground_units
def get_buildings(self, position=None, radius=None): """@see get_ships""" buildings = [] if position is not None and radius is not None: circle = Circle(position, radius) for island in self.islands: for building in island.buildings: if circle.contains(building.position.center()): buildings.append(building) return buildings else: return [b for b in island.buildings for island in self.islands]
def get_random_location(self, in_range): """Returns a random location in walking_range, that we can find a path to @param in_range: int, max distance to returned point from current position @return: Instance of Point or None""" possible_walk_targets = Circle(self.position, in_range).get_coordinates() possible_walk_targets.remove(self.position.to_tuple()) self.session.random.shuffle(possible_walk_targets) for coord in possible_walk_targets: target = Point(*coord) if self.check_move(target): return target return None
def get_ships(self, position=None, radius=None): """Returns all ships on the map, optionally only those in range around the specified position. @param position: Point or Rect instance. @param radius: int radius to use. @return: List of ships. """ if position is not None and radius is not None: circle = Circle(position, radius) return [ship for ship in self.ships if circle.contains(ship.position)] else: return self.ships
def send_ship_random_branch(self, ship, branch_office=None): """Sends a ship to a random branch office on the map @param ship: Ship instance that is to be used @param branch_office: Branch Office instance to move to. Random one is selected on None.""" self.log.debug("Trader %s ship %s moving to bo (random=%s)", self.worldid, ship.worldid, \ (branch_office is None)) # maybe this kind of list should be saved somewhere, as this is pretty performance intense branchoffices = self.session.world.get_branch_offices() if len(branchoffices) == 0: # there aren't any branch offices, so move randomly self.send_ship_random(ship) else: # select a branch office if branch_office is None: rand = self.session.random.randint(0, len(branchoffices) - 1) self.office[ship.worldid] = branchoffices[rand] else: self.office[ship.worldid] = branch_office found_path_to_bo = False # try to find a possible position near the bo for point in Circle(self.office[ship.worldid].position.center(), ship.radius): try: ship.move(point, Callback(self.reached_branch, ship)) except MoveNotPossible: continue found_path_to_bo = True self.ships[ship] = self.shipStates.moving_to_branch break if not found_path_to_bo: self.send_ship_random(ship)
def get_ships(self, position=None, radius=None): """Returns all ships on the map. Optionally only those in range around the specified position. @param position: Point or Rect instance. @param radius: int radius to use. @return: List of ships. """ if position is not None and radius is not None: circle = Circle(position, radius) ships = [] for ship in self.ships: if circle.contains(ship.position): ships.append(ship) return ships else: return self.ships
def get_surrounding_tiles(self, point, radius=1): """Returns tiles around point with specified radius. @param point: instance of Point""" for position in Circle(point, radius): tile = self.get_tile(position) if tile is not None: yield tile
def get_tiles_in_radius(self, position, radius, shuffle=False): """Returns a all tiles in the radius around the point. This is a generator, make sure you use it appropriately. @param position: Point instance @return List of tiles in radius. """ assert isinstance(position, Point) points = Circle(position, radius) if shuffle: points = list(points) self.session.random.shuffle(points) for point in points: if self.map_dimensions.contains_without_border(point): # don't yield if point is not in map, those points don't exist yield self.get_tile(point)
def move_to_next_route_bo(self): next_destination = self.get_next_destination() if next_destination == None: return branch_office = next_destination['branch_office'] found_path_to_bo = False for point in Circle(branch_office.position.center(), self.ship.radius): try: self.ship.move(point, self.on_route_bo_reached) except MoveNotPossible: continue found_path_to_bo = True break if not found_path_to_bo: self.disable()
def go(self, x, y): """Moves the ship. This is called when a ship is selected and RMB is pressed outside the ship""" self.stop() #disable the trading route if hasattr(self, 'route'): self.route.disable() ship_id = self.worldid # this has to happen here, # cause a reference to self in a temporary function is implemented # as a hard reference, which causes a memory leak def tmp(): if self.session.world.player == self.owner: self.session.view.renderer['GenericRenderer'].removeAll( "buoy_" + str(ship_id)) tmp() move_target = Point(int(round(x)), int(round(y))) try: self.move(move_target, tmp) except MoveNotPossible: # find a near tile to move to target_found = False surrounding = Circle(move_target, radius=0) while not target_found and surrounding.radius < 4: surrounding.radius += 1 for move_target in surrounding: try: self.move(move_target, tmp) except MoveNotPossible: continue target_found = True break if self.session.world.player == self.owner: if self.position.x != move_target.x or self.position.y != move_target.y: move_target = self.get_move_target() if move_target is not None: loc = fife.Location(self.session.view.layers[LAYERS.OBJECTS]) loc.thisown = 0 coords = fife.ModelCoordinate(move_target.x, move_target.y) coords.thisown = 0 loc.setLayerCoordinates(coords) self.session.view.renderer['GenericRenderer'].addAnimation( "buoy_" + str(self.worldid), fife.GenericRendererNode(loc), horizons.main.fife.animationpool.addResourceFromFile( "as_buoy0-idle-45"))
def move_to_next_route_bo(self, advance_waypoint=True): next_destination = self.get_next_destination(advance_waypoint) if next_destination == None: return branch_office = next_destination['branch_office'] if self.ship.position.distance_to_point( branch_office.position.center()) <= self.ship.radius: self.on_route_bo_reached() return try: self.ship.move(Circle(branch_office.position.center(), self.ship.radius), self.on_route_bo_reached, blocked_callback=self.on_ship_blocked) except MoveNotPossible: # retry in 5 seconds Scheduler().add_new_object(self.on_ship_blocked, self, GAME_SPEED.TICKS_PER_SECOND * 5)
def create_random_island(id_string): """Creates a random island as sqlite db. It is rather primitive; it places shapes on the dict. The coordinates of tiles will be 0 <= x < width and 0 <= y < height @param id_string: random island id string @return: sqlite db reader containing island """ # NOTE: the tilesystem will be redone soon, so constants indicating grounds are temporary # here and will have to be changed anyways. match_obj = re.match(_random_island_id_regexp, id_string) assert match_obj creation_method, width, height, seed = [ long(i) for i in match_obj.groups() ] rand = random.Random(seed) map_set = set() # creation_method 0 - standard small island for the 3x3 grid # creation_method 1 - large island # creation_method 2 - a number of randomly sized and placed islands # place this number of shapes for i in xrange(15 + width * height / 45): # place shape determined by shape_id on (x, y) add = True rect_chance = 6 if creation_method == 0: shape_id = rand.randint(3, 5) elif creation_method == 1: shape_id = rand.randint(5, 8) elif creation_method == 2: shape_id = rand.randint(2, 8) rect_chance = 29 if rand.randint(0, 4) == 0: rect_chance = 13 add = False shape = None if rand.randint(1, rect_chance) == 1: # use a rect if add: x = rand.randint(8, width - 7) y = rand.randint(8, height - 7) if creation_method == 0: shape = Rect.init_from_topleft_and_size(x - 3, y - 3, 5, 5) elif creation_method == 1: shape = Rect.init_from_topleft_and_size(x - 5, y - 5, 8, 8) elif creation_method == 2: shape = Rect.init_from_topleft_and_size( x - 5, y - 5, rand.randint(2, 8), rand.randint(2, 8)) else: x = rand.randint(0, width) y = rand.randint(0, height) shape = Rect.init_from_topleft_and_size( x - 5, y - 5, rand.randint(2, 8), rand.randint(2, 8)) else: # use a circle, where radius is determined by shape_id radius = shape_id if not add and rand.randint(0, 6) < 5: x = rand.randint(-radius * 3 / 2, width + radius * 3 / 2) y = rand.randint(-radius * 3 / 2, height + radius * 3 / 2) shape = Circle(Point(x, y), shape_id) elif width - radius - 4 >= radius + 3 and height - radius - 4 >= radius + 3: x = rand.randint(radius + 3, width - radius - 4) y = rand.randint(radius + 3, height - radius - 4) shape = Circle(Point(x, y), shape_id) if shape: for shape_coord in shape.tuple_iter(): if add: map_set.add(shape_coord) elif shape_coord in map_set: map_set.discard(shape_coord) # write values to db map_db = DbReader(":memory:") map_db( "CREATE TABLE ground(x INTEGER NOT NULL, y INTEGER NOT NULL, ground_id INTEGER NOT NULL)" ) map_db( "CREATE TABLE island_properties(name TEXT PRIMARY KEY NOT NULL, value TEXT NOT NULL)" ) map_db("BEGIN TRANSACTION") # add grass tiles for x, y in map_set: map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, GROUND.DEFAULT_LAND) def fill_tiny_spaces(tile): """Fills 1 tile gulfs and straits with the specified tile @param tile: ground tile to fill with """ all_neighbours = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] neighbours = [(-1, 0), (0, -1), (0, 1), (1, 0)] corners = [(-1, -1), (-1, 1)] knight_moves = [(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)] bad_configs = set([ 0, 1 << 0, 1 << 1, 1 << 2, 1 << 3, (1 << 0) | (1 << 3), (1 << 1) | (1 << 2) ]) edge_set = copy.copy(map_set) reduce_edge_set = True while True: to_fill = set() to_ignore = set() for x, y in edge_set: # ignore the tiles with no empty neighbours if reduce_edge_set: is_edge = False for x_offset, y_offset in all_neighbours: if (x + x_offset, y + y_offset) not in map_set: is_edge = True break if not is_edge: to_ignore.add((x, y)) continue for x_offset, y_offset in neighbours: x2 = x + x_offset y2 = y + y_offset if (x2, y2) in map_set: continue # (x2, y2) is now a point just off the island neighbours_dirs = 0 for i in xrange(len(neighbours)): x3 = x2 + neighbours[i][0] y3 = y2 + neighbours[i][1] if (x3, y3) not in map_set: neighbours_dirs |= (1 << i) if neighbours_dirs in bad_configs: # part of a straight 1 tile gulf to_fill.add((x2, y2)) else: for x_offset, y_offset in corners: x3 = x2 + x_offset y3 = y2 + y_offset x4 = x2 - x_offset y4 = y2 - y_offset if (x3, y3) in map_set and (x4, y4) in map_set: # part of a diagonal 1 tile gulf to_fill.add((x2, y2)) break # block 1 tile straits for x_offset, y_offset in knight_moves: x2 = x + x_offset y2 = y + y_offset if (x2, y2) not in map_set: continue if abs(x_offset) == 1: y2 = y + y_offset / 2 if (x2, y2) in map_set or (x, y2) in map_set: continue else: x2 = x + x_offset / 2 if (x2, y2) in map_set or (x2, y) in map_set: continue to_fill.add((x2, y2)) # block diagonal 1 tile straits for x_offset, y_offset in corners: x2 = x + x_offset y2 = y + y_offset x3 = x + 2 * x_offset y3 = y + 2 * y_offset if (x2, y2) not in map_set and (x3, y3) in map_set: to_fill.add((x2, y2)) elif (x2, y2) in map_set and (x2, y) not in map_set and ( x, y2) not in map_set: to_fill.add((x2, y)) if to_fill: for x, y in to_fill: map_set.add((x, y)) map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, tile) old_size = len(edge_set) edge_set = edge_set.difference(to_ignore).union(to_fill) reduce_edge_set = old_size - len(edge_set) > 50 else: break # possible movement directions all_moves = { 'sw': (-1, -1), 'w': (-1, 0), 'nw': (-1, 1), 's': (0, -1), 'n': (0, 1), 'se': (1, -1), 'e': (1, 0), 'ne': (1, 1) } def get_island_outline(): """ @return: the points just off the island as a dict """ result = set() for x, y in map_set: for offset_x, offset_y in all_moves.itervalues(): coords = (x + offset_x, y + offset_y) if coords not in map_set: result.add(coords) return result # add grass to sand tiles fill_tiny_spaces(GROUND.DEFAULT_LAND) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.SAND_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.SAND_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.SAND_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.SAND_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.SAND_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.SAND_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.SAND_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.SAND_NORTH # sandy corner elif filled == ['se']: tile = GROUND.SAND_NORTHWEST1 elif filled == ['ne']: tile = GROUND.SAND_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.SAND_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.SAND_NORTHEAST1 # grassy corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.SAND_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.SAND_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.SAND_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.SAND_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, tile) map_set = map_set.union(outline) # add sand to shallow water tiles fill_tiny_spaces(GROUND.SAND) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.COAST_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.COAST_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.COAST_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.COAST_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.COAST_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.COAST_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.COAST_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.COAST_NORTH # mostly wet corner elif filled == ['se']: tile = GROUND.COAST_NORTHWEST1 elif filled == ['ne']: tile = GROUND.COAST_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.COAST_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.COAST_NORTHEAST1 # mostly dry corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.COAST_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.COAST_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.COAST_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.COAST_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, tile) map_set = map_set.union(outline) # add shallow water to deep water tiles fill_tiny_spaces(GROUND.SHALLOW_WATER) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.DEEP_WATER_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.DEEP_WATER_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.DEEP_WATER_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.DEEP_WATER_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.DEEP_WATER_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.DEEP_WATER_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.DEEP_WATER_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.DEEP_WATER_NORTH # mostly deep corner elif filled == ['se']: tile = GROUND.DEEP_WATER_NORTHWEST1 elif filled == ['ne']: tile = GROUND.DEEP_WATER_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.DEEP_WATER_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.DEEP_WATER_NORTHEAST1 # mostly shallow corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.DEEP_WATER_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.DEEP_WATER_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.DEEP_WATER_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.DEEP_WATER_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, tile) map_db("COMMIT") return map_db
def create_random_island(id_string): """Creates a random island as sqlite db. It is rather primitive; it places shapes on the dict. @param id_string: random island id string @return: sqlite db reader containing island """ # NOTE: the tilesystem will be redone soon, so constants indicating grounds are temporary # here and will have to be changed anyways. match_obj = re.match(_random_island_id_regexp, id_string) assert match_obj creation_method, width, height, seed = [ long(i) for i in match_obj.groups() ] rand = random.Random(seed) map_dict = {} # creation_method 0 - standard small island for the 3x3 grid # creation_method 1 - large island # place this number of shapes for i in xrange(int(float(width + height) / 2 * 1.5)): x = rand.randint(8, width - 8) y = rand.randint(8, height - 8) # place shape determined by shape_id on (x, y) if creation_method == 0: shape_id = rand.randint(3, 5) elif creation_method == 1: shape_id = rand.randint(5, 8) if rand.randint(1, 4) == 1: # use a rect if creation_method == 0: for shape_coord in Rect.init_from_topleft_and_size( x - 3, y - 3, 5, 5).tuple_iter(): map_dict[shape_coord] = True elif creation_method == 1: for shape_coord in Rect.init_from_topleft_and_size( x - 5, y - 5, 8, 8).tuple_iter(): map_dict[shape_coord] = True else: # use a circle, where radius is determined by shape_id for shape_coord in Circle(Point(x, y), shape_id).tuple_iter(): map_dict[shape_coord] = True # write values to db map_db = DbReader(":memory:") map_db( "CREATE TABLE ground(x INTEGER NOT NULL, y INTEGER NOT NULL, ground_id INTEGER NOT NULL)" ) map_db( "CREATE TABLE island_properties(name TEXT PRIMARY KEY NOT NULL, value TEXT NOT NULL)" ) map_db("BEGIN TRANSACTION") # add grass tiles for x, y in map_dict.iterkeys(): map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, GROUND.DEFAULT_LAND) def fill_tiny_spaces(tile): """Fills 1 tile gulfs and straits with the specified tile @param tile: ground tile to fill with """ neighbours = [(-1, 0), (0, -1), (0, 1), (1, 0)] corners = [(-1, -1), (-1, 1)] knight_moves = [(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)] bad_configs = set([ 0, 1 << 0, 1 << 1, 1 << 2, 1 << 3, (1 << 0) | (1 << 3), (1 << 1) | (1 << 2) ]) while True: to_fill = {} for x, y in map_dict.iterkeys(): for x_offset, y_offset in neighbours: x2 = x + x_offset y2 = y + y_offset if (x2, y2) in map_dict: continue # (x2, y2) is now a point just off the island neighbours_dirs = 0 for i in range(len(neighbours)): x3 = x2 + neighbours[i][0] y3 = y2 + neighbours[i][1] if (x3, y3) not in map_dict: neighbours_dirs |= (1 << i) if neighbours_dirs in bad_configs: # part of a straight 1 tile gulf to_fill[(x2, y2)] = True else: for x_offset, y_offset in corners: x3 = x2 + x_offset y3 = y2 + y_offset x4 = x2 - x_offset y4 = y2 - y_offset if (x3, y3) in map_dict and (x4, y4) in map_dict: # part of a diagonal 1 tile gulf to_fill[(x2, y2)] = True break # block 1 tile straits for x_offset, y_offset in knight_moves: x2 = x + x_offset y2 = y + y_offset if (x2, y2) not in map_dict: continue if abs(x_offset) == 1: y2 = y + y_offset / 2 if (x2, y2) in map_dict or (x, y2) in map_dict: continue else: x2 = x + x_offset / 2 if (x2, y2) in map_dict or (x2, y) in map_dict: continue to_fill[(x2, y2)] = True if to_fill: for x, y in to_fill.iterkeys(): map_dict[(x, y)] = tile map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, tile) else: break # possible movement directions all_moves = { 'sw': (-1, -1), 'w': (-1, 0), 'nw': (-1, 1), 's': (0, -1), 'n': (0, 1), 'se': (1, -1), 'e': (1, 0), 'ne': (1, 1) } def get_island_outline(): """ @return: the points just off the island as a dict """ result = {} for x, y in map_dict.iterkeys(): for offset_x, offset_y in all_moves.itervalues(): coords = (x + offset_x, y + offset_y) if coords not in map_dict: result[coords] = True return result # add grass to sand tiles fill_tiny_spaces(GROUND.DEFAULT_LAND) outline = get_island_outline() for x, y in outline.iterkeys(): filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_dict: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.SAND_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.SAND_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.SAND_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.SAND_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.SAND_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.SAND_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.SAND_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.SAND_NORTH # sandy corner elif filled == ['se']: tile = GROUND.SAND_NORTHWEST1 elif filled == ['ne']: tile = GROUND.SAND_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.SAND_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.SAND_NORTHEAST1 # grassy corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.SAND_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.SAND_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.SAND_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.SAND_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, tile) for coords, type in outline.iteritems(): map_dict[coords] = type # add sand to shallow water tiles fill_tiny_spaces(GROUND.SAND) outline = get_island_outline() for x, y in outline.iterkeys(): filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_dict: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.COAST_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.COAST_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.COAST_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.COAST_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.COAST_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.COAST_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.COAST_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.COAST_NORTH # mostly wet corner elif filled == ['se']: tile = GROUND.COAST_NORTHWEST1 elif filled == ['ne']: tile = GROUND.COAST_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.COAST_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.COAST_NORTHEAST1 # mostly dry corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.COAST_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.COAST_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.COAST_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.COAST_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, tile) for coords, type in outline.iteritems(): map_dict[coords] = type # add shallow water to deep water tiles fill_tiny_spaces(GROUND.SHALLOW_WATER) outline = get_island_outline() for x, y in outline.iterkeys(): filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_dict: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.DEEP_WATER_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.DEEP_WATER_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.DEEP_WATER_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.DEEP_WATER_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.DEEP_WATER_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.DEEP_WATER_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.DEEP_WATER_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.DEEP_WATER_NORTH # mostly deep corner elif filled == ['se']: tile = GROUND.DEEP_WATER_NORTHWEST1 elif filled == ['ne']: tile = GROUND.DEEP_WATER_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.DEEP_WATER_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.DEEP_WATER_NORTHEAST1 # mostly shallow corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.DEEP_WATER_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.DEEP_WATER_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.DEEP_WATER_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.DEEP_WATER_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, tile) map_db("COMMIT") return map_db
def create_random_island(id_string): """Creates a random island as sqlite db. It is rather primitive; it places shapes on the dict. The coordinates of tiles will be 0 <= x < width and 0 <= y < height @param id_string: random island id string @return: sqlite db reader containing island """ match_obj = re.match(_random_island_id_regexp, id_string) assert match_obj creation_method, width, height, seed = [ long(i) for i in match_obj.groups() ] assert creation_method == 2, 'The only supported island creation method is 2.' rand = random.Random(seed) map_set = set() # place this number of shapes for i in xrange(15 + width * height / 45): # place shape determined by shape_id on (x, y) add = True shape_id = rand.randint(2, 8) rect_chance = 29 if rand.randint(0, 4) == 0: rect_chance = 13 add = False shape = None if rand.randint(1, rect_chance) == 1: # use a rect if add: x = rand.randint(8, width - 7) y = rand.randint(8, height - 7) else: x = rand.randint(0, width) y = rand.randint(0, height) shape = Rect.init_from_topleft_and_size(x - 5, y - 5, rand.randint(2, 8), rand.randint(2, 8)) else: # use a circle such that the radius is determined by shape_id radius = shape_id if not add and rand.randint(0, 6) < 5: x = rand.randint(-radius * 3 / 2, width + radius * 3 / 2) y = rand.randint(-radius * 3 / 2, height + radius * 3 / 2) shape = Circle(Point(x, y), shape_id) elif width - radius - 4 >= radius + 3 and height - radius - 4 >= radius + 3: x = rand.randint(radius + 3, width - radius - 4) y = rand.randint(radius + 3, height - radius - 4) shape = Circle(Point(x, y), shape_id) if shape: for shape_coord in shape.tuple_iter(): if add: map_set.add(shape_coord) elif shape_coord in map_set: map_set.discard(shape_coord) # write values to db map_db = DbReader(":memory:") map_db("CREATE TABLE ground(x INTEGER NOT NULL, y INTEGER NOT NULL, ground_id INTEGER NOT NULL, action_id TEXT NOT NULL, rotation INTEGER NOT NULL)") map_db("CREATE TABLE island_properties(name TEXT PRIMARY KEY NOT NULL, value TEXT NOT NULL)") map_db("BEGIN TRANSACTION") # add grass tiles for x, y in map_set: map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?)", x, y, *GROUND.DEFAULT_LAND) def fill_tiny_spaces(tile): """Fills 1 tile gulfs and straits with the specified tile @param tile: ground tile to fill with """ all_neighbours = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] neighbours = [(-1, 0), (0, -1), (0, 1), (1, 0)] corners = [(-1, -1), (-1, 1)] knight_moves = [(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)] bad_configs = set([0, 1 << 0, 1 << 1, 1 << 2, 1 << 3, (1 << 0) | (1 << 3), (1 << 1) | (1 << 2)]) edge_set = copy.copy(map_set) reduce_edge_set = True while True: to_fill = set() to_ignore = set() for x, y in edge_set: # ignore the tiles with no empty neighbours if reduce_edge_set: is_edge = False for x_offset, y_offset in all_neighbours: if (x + x_offset, y + y_offset) not in map_set: is_edge = True break if not is_edge: to_ignore.add((x, y)) continue for x_offset, y_offset in neighbours: x2 = x + x_offset y2 = y + y_offset if (x2, y2) in map_set: continue # (x2, y2) is now a point just off the island neighbours_dirs = 0 for i in xrange(len(neighbours)): x3 = x2 + neighbours[i][0] y3 = y2 + neighbours[i][1] if (x3, y3) not in map_set: neighbours_dirs |= (1 << i) if neighbours_dirs in bad_configs: # part of a straight 1 tile gulf to_fill.add((x2, y2)) else: for x_offset, y_offset in corners: x3 = x2 + x_offset y3 = y2 + y_offset x4 = x2 - x_offset y4 = y2 - y_offset if (x3, y3) in map_set and (x4, y4) in map_set: # part of a diagonal 1 tile gulf to_fill.add((x2, y2)) break # block 1 tile straits for x_offset, y_offset in knight_moves: x2 = x + x_offset y2 = y + y_offset if (x2, y2) not in map_set: continue if abs(x_offset) == 1: y2 = y + y_offset / 2 if (x2, y2) in map_set or (x, y2) in map_set: continue else: x2 = x + x_offset / 2 if (x2, y2) in map_set or (x2, y) in map_set: continue to_fill.add((x2, y2)) # block diagonal 1 tile straits for x_offset, y_offset in corners: x2 = x + x_offset y2 = y + y_offset x3 = x + 2 * x_offset y3 = y + 2 * y_offset if (x2, y2) not in map_set and (x3, y3) in map_set: to_fill.add((x2, y2)) elif (x2, y2) in map_set and (x2, y) not in map_set and (x, y2) not in map_set: to_fill.add((x2, y)) if to_fill: for x, y in to_fill: map_set.add((x, y)) map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?)", x, y, *tile) old_size = len(edge_set) edge_set = edge_set.difference(to_ignore).union(to_fill) reduce_edge_set = old_size - len(edge_set) > 50 else: break # possible movement directions all_moves = { 'sw' : (-1, -1), 'w' : (-1, 0), 'nw' : (-1, 1), 's' : (0, -1), 'n' : (0, 1), 'se' : (1, -1), 'e' : (1, 0), 'ne' : (1, 1) } def get_island_outline(): """ @return: the points just off the island as a dict """ result = set() for x, y in map_set: for offset_x, offset_y in all_moves.itervalues(): coords = (x + offset_x, y + offset_y) if coords not in map_set: result.add(coords) return result # add grass to sand tiles fill_tiny_spaces(GROUND.DEFAULT_LAND) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.SAND_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.SAND_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.SAND_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.SAND_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.SAND_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.SAND_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.SAND_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.SAND_NORTH # sandy corner elif filled == ['se']: tile = GROUND.SAND_NORTHWEST1 elif filled == ['ne']: tile = GROUND.SAND_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.SAND_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.SAND_NORTHEAST1 # grassy corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.SAND_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.SAND_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.SAND_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.SAND_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?)", x, y, *tile) map_set = map_set.union(outline) # add sand to shallow water tiles fill_tiny_spaces(GROUND.SAND) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.COAST_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.COAST_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.COAST_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.COAST_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.COAST_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.COAST_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.COAST_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.COAST_NORTH # mostly wet corner elif filled == ['se']: tile = GROUND.COAST_NORTHWEST1 elif filled == ['ne']: tile = GROUND.COAST_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.COAST_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.COAST_NORTHEAST1 # mostly dry corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.COAST_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.COAST_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.COAST_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.COAST_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?)", x, y, *tile) map_set = map_set.union(outline) # add shallow water to deep water tiles fill_tiny_spaces(GROUND.SHALLOW_WATER) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.DEEP_WATER_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.DEEP_WATER_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.DEEP_WATER_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.DEEP_WATER_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.DEEP_WATER_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.DEEP_WATER_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.DEEP_WATER_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.DEEP_WATER_NORTH # mostly deep corner elif filled == ['se']: tile = GROUND.DEEP_WATER_NORTHWEST1 elif filled == ['ne']: tile = GROUND.DEEP_WATER_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.DEEP_WATER_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.DEEP_WATER_NORTHEAST1 # mostly shallow corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.DEEP_WATER_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.DEEP_WATER_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.DEEP_WATER_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.DEEP_WATER_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?)", x, y, *tile) map_db("COMMIT") return map_db
def create_random_island(id_string): """Creates a random island as sqlite db. It is rather primitive; it places shapes on the dict. @param id_string: random island id string @return: sqlite db reader containing island """ # NOTE: the tilesystem will be redone soon, so constants indicating grounds are temporary # here and will have to be changed anyways. match_obj = re.match(_random_island_id_regexp, id_string) assert match_obj creation_method, width, height, seed = [ long(i) for i in match_obj.groups() ] rand = random.Random(seed) map_dict = {} # creation_method 0 - standard small island for the 3x3 grid # creation_method 1 - large island # place this number of shapes for i in xrange(int(float(width + height) / 2 * 1.5)): x = rand.randint(4, width - 4) y = rand.randint(4, height - 4) # place shape determined by shape_id on (x, y) if creation_method == 0: shape_id = rand.randint(3, 5) elif creation_method == 1: shape_id = rand.randint(5, 8) if rand.randint(1, 4) == 1: # use a rect if creation_method == 0: for shape_coord in Rect.init_from_topleft_and_size( x - 3, y - 3, 5, 5).tuple_iter(): map_dict[shape_coord] = 1 elif creation_method == 1: for shape_coord in Rect.init_from_topleft_and_size( x - 5, y - 5, 8, 8).tuple_iter(): map_dict[shape_coord] = 1 else: # use a circle, where radius is determined by shape_id for shape_coord in Circle(Point(x, y), shape_id).tuple_iter(): map_dict[shape_coord] = 1 # write values to db map_db = DbReader(":memory:") map_db( "CREATE TABLE ground(x INTEGER NOT NULL, y INTEGER NOT NULL, ground_id INTEGER NOT NULL)" ) map_db( "CREATE TABLE island_properties(name TEXT PRIMARY KEY NOT NULL, value TEXT NOT NULL)" ) map_db("BEGIN TRANSACTION") # assign these characters, if a coastline is found in this offset offset_coastline = {'a': (0, -1), 'b': (1, 0), 'c': (0, 1), 'd': (-1, 0)} for x, y in map_dict.iterkeys(): # add a coastline tile for coastline, or default land else coastline = "" for offset_char in sorted(offset_coastline): if (x + offset_coastline[offset_char][0], y + offset_coastline[offset_char][1]) not in map_dict: coastline += offset_char if coastline: # TODO: use coastline tile depending on coastline map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, 49) else: map_db("INSERT INTO ground VALUES(?, ?, ?)", x, y, GROUND.DEFAULT_LAND) map_db("COMMIT") return map_db