88#
99# Generates building tile items using Poisson Disk Sampling for the given tiles
1010# Roads are generated between the buildings and between towns using A* pathfinding
11+ # and a minimum tree spanning algorithm
1112#
1213class TownGenerator
1314 attr_reader :sample_area , :road_generator
@@ -81,25 +82,66 @@ def generate_points_for_town(num_of_points, radius, intial_coordinates)
8182 end
8283
8384 def generate_town_roads ( points , town_num , verbose )
84- # TODO: slow, bad (complete graph) will update to use minimum tree spanning algorithm instead
8585 puts "generating town #{ town_num } roads..." if verbose
8686
87+ generate_roads_from_connected_pairs ( build_minimum_spanning_tree ( points , populate_distances_between_each_point ( points ) ) )
88+ end
89+
90+ def generate_roads_from_connected_pairs ( connected_pairs )
91+ connected_pairs . each do |edge |
92+ road_to_building_one = place_in_front_or_behind ( edge . first )
93+ road_to_building_two = place_in_front_or_behind ( edge . last )
94+
95+ next if road_to_building_one . nil? || road_to_building_two . nil?
96+
97+ road_generator . generate_roads_from_coordinate_list ( road_to_building_one . concat ( road_to_building_two ) , false )
98+ end
99+ end
100+
101+ def build_minimum_spanning_tree ( points , distances )
87102 connected_pairs = Set . new
103+ visited = Set . new ( [ points . first ] ) # Create a set to keep track of visited nodes
104+ until visited . size == points . size
105+ edge = find_minimum_edge ( distances , visited )
106+ connected_pairs . add ( edge )
107+ visited . add ( edge . last )
108+ end
109+ connected_pairs
110+ end
111+
112+ def populate_distances_between_each_point ( points )
113+ distances = { }
88114 points . each_with_index do |point_one , idx_one |
89115 points [ idx_one + 1 ..] . each do |point_two |
90- next if connected_pairs . include? ( [ point_one , point_two ] ) || connected_pairs . include? ( [ point_two , point_one ] )
116+ distance = calculate_distance ( point_one , point_two )
117+ distances [ [ point_one , point_two ] ] = distance
118+ distances [ [ point_two , point_one ] ] = distance
119+ end
120+ end
121+ distances
122+ end
91123
92- road_to_building_one = place_in_front_or_behind ( point_one )
93- road_to_building_two = place_in_front_or_behind ( point_two )
124+ def calculate_distance ( point1 , point2 )
125+ Math . sqrt ( ( point1 . y - point2 . y ) **2 + ( point1 . x - point2 . x ) **2 )
126+ end
94127
95- connected_pairs . add ( [ point_one , point_two ] )
96- connected_pairs . add ( [ point_two , point_one ] )
128+ def find_minimum_edge ( distances , visited )
129+ # method to find the minimum edge connecting visited and unvisited nodes
130+ min_edge = nil
131+ min_distance = Float ::INFINITY
97132
98- next if road_to_building_one . nil? || road_to_building_two . nil?
133+ visited . each do |visited_node |
134+ distances . each do |edge , distance |
135+ next if visited . include? ( edge . last ) || edge . first != visited_node
99136
100- road_generator . generate_roads_from_coordinate_list ( road_to_building_one . concat ( road_to_building_two ) , false )
137+ if distance < min_distance
138+ min_distance = distance
139+ min_edge = edge
140+ end
101141 end
102142 end
143+
144+ min_edge
103145 end
104146
105147 def place_in_front_or_behind ( point )
@@ -116,24 +158,8 @@ def generate_roads_between_towns(verbose)
116158
117159 puts 'generating roads between towns...' if verbose
118160
119- connected_pairs = Set . new
120- town_centroids = { }
121-
122- @all_town_points . each_with_index do |town_one , idx_one |
123- find_town_centroid ( town_one )
124-
125- @all_town_points [ idx_one + 1 ..] . each do |town_two |
126- next if connected_pairs . include? ( [ town_one , town_two ] ) || connected_pairs . include? ( [ town_two , town_one ] )
127-
128- town_one_center_x , town_one_center_y = ( town_centroids [ town_one ] ||= find_town_centroid ( town_one ) )
129- town_two_center_x , town_two_center_y = ( town_centroids [ town_two ] ||= find_town_centroid ( town_two ) )
130-
131- road_generator . generate_roads_from_coordinate_list ( [ town_one_center_x , town_one_center_y , town_two_center_x , town_two_center_y ] , false )
132-
133- connected_pairs . add ( [ town_one , town_two ] )
134- connected_pairs . add ( [ town_two , town_one ] )
135- end
136- end
161+ centroids = @all_town_points . map { |town_points | find_town_centroid ( town_points ) }
162+ generate_roads_from_connected_pairs ( build_minimum_spanning_tree ( centroids , populate_distances_between_each_point ( centroids ) ) )
137163 end
138164
139165 def find_town_centroid ( points )
@@ -149,6 +175,6 @@ def find_town_centroid(points)
149175 average_x = total_x / num_coordinates . to_f
150176 average_y = total_y / num_coordinates . to_f
151177
152- [ average_x , average_y ]
178+ OpenStruct . new ( x : average_x , y : average_y )
153179 end
154180end
0 commit comments