Example #1
0
 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)
     ]
Example #2
0
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
Example #3
0
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)
Example #4
0
    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
Example #5
0
    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
Example #6
0
    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))