def get_neighbors(self, x, y): # TODO: fix this so it doeesn't double-check the coordinates (once in get_neighbor coords, once in get_hex! return [ self.get_hex(new_x, new_y) for (new_x, new_y) in hexgrid.get_neighbor_coords( Loc(x, y), self.width, self.height) ]
def find_regions(game_map, filter_func): region_to_locs = {} region_neighbors = {} loc_to_region = {} region_id = 0 for y in range(game_map.height): for x in range(game_map.width): curr_hex = game_map.get_hex(x, y) if filter_func(curr_hex): curr_loc = Loc(x, y) region_to_locs[region_id] = [curr_loc] loc_to_region[curr_loc] = region_id region_neighbors[curr_loc] = [] region_id += 1 for loc in loc_to_region.iterkeys(): neighbors = hexgrid.get_neighbor_coords(loc, game_map.width, game_map.height) region_id = loc_to_region[loc] for neighbor in neighbors: if neighbor in loc_to_region: region_neighbors[loc].append(neighbor) if loc_to_region[neighbor] != region_id: neighbor_id = loc_to_region[neighbor] region_to_locs[region_id] += region_to_locs[neighbor_id] for adj_region_loc in region_to_locs[neighbor_id]: loc_to_region[adj_region_loc] = region_id region_to_locs[neighbor_id] = [] return [locs for locs in region_to_locs.values() if len(locs) > 0], region_neighbors
def build_road_net(game_map): regions, region_neighbors = find_regions(game_map, lambda x: x.is_settled()) road_map = {} # clear existing roads for y in range(game_map.height): for x in range(game_map.width): game_map.get_hex(x, y).set_road(None) # road_msts = [] for region in regions: settlements = find_settlements(region, game_map) if len(settlements) <= 1: continue edges = [] for i in range(len(settlements)): for j in range(i + 1, len(settlements)): edges.append((settlements[i], settlements[j])) paths = find_road_paths(region_neighbors, edges) edges.sort(key=lambda edge: paths[edge]["cost"]) road_mst = find_mst(region, edges, settlements) # print("settlements: " + str(settlements)) # print("Mst: " + str(road_mst)) for edge in road_mst: # print ("edge: " + str(edge) + " path: " + str(paths[edge]["locs"])) for loc in paths[edge]["locs"]: road_map[loc] = True # game_map.get_hex(loc.x, loc.y).set_road() for loc in road_map.iterkeys(): hex_road = Road() neighbors = hexgrid.get_neighbor_coords(loc, game_map.width, game_map.height) for neighbor in neighbors: if neighbor in road_map: hex_road.add_connection(hexgrid.get_direction(loc, neighbor)) game_map.get_hex(loc.x, loc.y).set_road(hex_road)
def erode_heightmap(self, heightmap, repeats): locs = [] neighbors = {} for x in range(self.padded_width): for y in range(self.padded_height): new_loc = Loc(x, y) locs.append(new_loc) neighbors[new_loc] = hexgrid.get_neighbor_coords( new_loc, self.padded_width, self.padded_height) # erode results in order to create a smoother terrain - donate elevation from higher places to lower ones for i in range(self.padded_width * self.padded_height * repeats): # randomly choose a spot from which to start erosion hex_loc = random.choice(locs) # x, y = random.randint(0, self.padded_width - 1), random.randint(0, self.padded_height - 1) while True: lowest = min( neighbors[hex_loc], key=lambda hex_loc: heightmap[hex_loc.x][hex_loc.y]) # lowest, lowest_elev = self.find_lowest_neighbor(Loc(x, y), heightmap) # make sure eroding will leave lowest neighbor lower than we are - # dirt can only slide downhill! if heightmap[lowest.x][ lowest.y] + 2 * self.EROSION_UNIT >= heightmap[ hex_loc.x][hex_loc.y]: break # if there's a lower place to erode to, donate an erosion unit to it, heightmap[hex_loc.x][hex_loc.y] -= self.EROSION_UNIT heightmap[lowest.x][lowest.y] += self.EROSION_UNIT # see if we can continue eroding down from place we eroded to hex_loc = lowest # x = lowest.x # y = lowest.y return heightmap
def gen_rivers(self, elevation): catchment = make_2D_list(self.padded_width, self.padded_height, 0) rivers = {} # hex_coords = [ Loc(x, y) for x in range(self.padded_width) for y in range(self.padded_height) ] locations_by_elev = sorted(hex_coords, key=lambda loc: elevation[loc.x][loc.y], reverse=True) # initialize rain on higher elevations highest_elevs = locations_by_elev[:int( len(locations_by_elev) * self.RAIN_THRESHOLD)] for (x, y) in highest_elevs: catchment[x][y] = 1 river_starts = [] # visit locations from highest to lowest, moving rain downhill while len(locations_by_elev) > 0: curr_loc = locations_by_elev.pop(0) local_elev = elevation[curr_loc.x][curr_loc.y] local_accum = catchment[curr_loc.x][curr_loc.y] # don't care what happens to rain once it reaches sea if local_elev <= self.sea_level: break neighbors = hexgrid.get_neighbor_coords(curr_loc, self.padded_width, self.padded_height) # if enough has accumulated to start a river, and there isn't already a river start # neighboring this one, begin a new river here. if local_accum > self.RIVER_THRESHOLD: river_start = True for neighbor in neighbors: if neighbor in river_starts: river_start = False if river_start: river_starts.append(curr_loc) else: # if no river started yet, move all water from this loc to lowest neighbor (in some # cases the lowest neighbor will actually be up, which will have no effect, since we won't revisit # that location. lowest = min( neighbors, key=lambda hex_loc: elevation[hex_loc.x][hex_loc.y]) # #self.find_lowest_neighbor(curr_loc, elevation) catchment[lowest.x][lowest.y] += local_accum # lowest locations on edge of map are potential river end points edge_locs = [Loc(x, 0) for x in range(self.padded_width)] edge_locs += [Loc(0, y) for y in range(self.padded_height)] edge_locs += [ Loc(x, self.padded_height - 1) for x in range(self.padded_width) ] edge_locs += [ Loc(self.padded_width - 1, y) for y in range(self.padded_height) ] edges_by_elev = sorted(edge_locs, key=lambda loc: elevation[loc.x][loc.y]) lowest_edge_elevs = edges_by_elev[:int( len(edges_by_elev) * self.EDGE_RIVER_END_THRESHOLD)] # generate rivers for start_point in river_starts: self.find_river_path(start_point, lowest_edge_elevs, rivers, elevation) return rivers
def find_river_path(self, start, targets, rivers, elevation): reach_cost = {} predecessor = {} open_locs = [] closed_locs = [] heappush(open_locs, (0.0, start)) reach_cost[start] = 0 while len(open_locs) > 0: (curr_cost, curr_loc) = heappop(open_locs) curr_cost = reach_cost[curr_loc] closed_locs.append(curr_loc) curr_elev = elevation[curr_loc.x][curr_loc.y] # stop if we reach sea level, one of the lower board edges, or an existing river #end river if it reaches sea or low point on edge of map (targets), or existing river if curr_elev <= self.sea_level or curr_loc in targets or curr_loc in rivers: break neighbors = hexgrid.get_neighbor_coords(curr_loc, self.padded_width, self.padded_height) for neigh_loc in neighbors: # don't visit an already visited place or an already full-up river if neigh_loc in closed_locs or ( neigh_loc in rivers and rivers[neigh_loc].num_inflows() >= river.MAX_INFLOWS): continue new_elev = elevation[neigh_loc.x][neigh_loc.y] if new_elev > curr_elev: new_cost = curr_cost + ( new_elev - curr_elev) * 2 # expensive to go up! else: new_cost = curr_cost + (new_elev - curr_elev) if (neigh_loc not in reach_cost) or (new_cost < reach_cost[neigh_loc]): reach_cost[neigh_loc] = new_cost predecessor[neigh_loc] = curr_loc heappush(open_locs, (new_cost, neigh_loc)) # translate search data into river entry/exit info for each hex in path. # first, handle special case for river ending at map edge, # make it appear to go off map if curr_elev > self.sea_level and curr_loc in targets: # find direction of off map direction = -1 if curr_loc.x == 0: direction = hexgrid.WEST if curr_loc.x == self.padded_width - 1: direction = hexgrid.EAST if curr_loc.y == 0: direction = hexgrid.NORTHEAST if curr_loc.y == self.padded_height - 1: direction = hexgrid.SOUTHWEST new_river = river.River() new_river.set_outflow(direction) rivers[curr_loc] = new_river # otherwise must be going into sea or joining an existing river; code below # will handle that automatically while curr_loc != start: prev_loc = curr_loc curr_loc = predecessor[curr_loc] new_river = river.River() # find direction from prev_loc to curr_loc direction = hexgrid.get_direction(curr_loc, prev_loc) new_river.set_outflow(direction) rivers[curr_loc] = new_river if prev_loc in rivers: rivers[prev_loc].add_inflow(hexgrid.get_reverse(direction))