ruby-perlin-2D-map-generator 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'poisson_disk_sampling/sampler'
4
+ require 'poisson_disk_sampling/sample_area'
5
+ require 'road_generator'
6
+ require 'map_config'
7
+
8
+ #
9
+ # Generates building tile items using Poisson Disk Sampling for the given tiles
10
+ # Roads are generated between the buildings and between towns using A* pathfinding
11
+ #
12
+ class TownGenerator
13
+ attr_reader :sample_area, :road_generator
14
+
15
+ def initialize(tiles, seed: MapConfig::DEFAULT_TOWN_SEED)
16
+ @sample_area = PoissonDiskSampling::SampleArea.new(grid: tiles)
17
+ @road_generator = RoadGenerator.new(tiles)
18
+ @seed = seed
19
+ @all_town_points = []
20
+ end
21
+
22
+ def generate_random_towns(config)
23
+ return if config.towns <= 0
24
+
25
+ puts "generating #{config.towns} random towns..." if config.verbose
26
+
27
+ @all_town_points.concat(iterate_through_towns(config.towns) do |n|
28
+ generate_random_town(n, config.verbose)
29
+ end)
30
+
31
+ generate_roads_between_towns(config.verbose)
32
+ end
33
+
34
+ def generate_towns_from_coordinate_list(config)
35
+ return unless (config.towns_to_make.length % 4).zero?
36
+
37
+ puts "generating #{config.towns_to_make.length / 4} coordinate towns..." if config.verbose
38
+
39
+ @all_town_points.concat(iterate_through_towns((config.towns_to_make.length / 4)) do |n|
40
+ town_values = config.towns_to_make[(n - 1) * 4..].take(4)
41
+ generate_town(n, town_values[2], town_values[3], [sample_area[town_values[0], town_values[1]]], config.verbose)
42
+ end)
43
+
44
+ generate_roads_between_towns(config.verbose)
45
+ end
46
+
47
+ private
48
+
49
+ def iterate_through_towns(num_of_towns)
50
+ (1..num_of_towns).map do |n|
51
+ town_points = yield n
52
+ @seed += 1000
53
+ town_points
54
+ end
55
+ end
56
+
57
+ def generate_random_town(town_num, verbose)
58
+ random_town_gen = Random.new(@seed)
59
+ generate_town(town_num, random_town_gen.rand(10..40), random_town_gen.rand(2..4), nil, verbose)
60
+ end
61
+
62
+ def generate_town(town_num, num_of_points, radius, initial_coords, verbose)
63
+ puts "generating town #{town_num}..." if verbose
64
+
65
+ points = generate_points_for_town(num_of_points, radius, initial_coords)
66
+ generate_town_roads(points, town_num, verbose)
67
+ points
68
+ end
69
+
70
+ def generate_points_for_town(num_of_points, radius, intial_coordinates)
71
+ points =
72
+ PoissonDiskSampling::Sampler.new(
73
+ sample_area: sample_area,
74
+ seed: @seed
75
+ ).generate_points(num_of_points, radius, intial_coordinates)
76
+ points.each do |point|
77
+ @seed += 1
78
+ point.add_town_item(@seed)
79
+ end
80
+ points
81
+ end
82
+
83
+ def generate_town_roads(points, town_num, verbose)
84
+ # TODO: slow, bad (complete graph) will update to use minimum tree spanning algorithm instead
85
+ puts "generating town #{town_num} roads..." if verbose
86
+
87
+ connected_pairs = Set.new
88
+ points.each_with_index do |point_one, idx_one|
89
+ 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])
91
+
92
+ road_to_building_one = place_in_front_or_behind(point_one)
93
+ road_to_building_two = place_in_front_or_behind(point_two)
94
+
95
+ connected_pairs.add([point_one, point_two])
96
+ connected_pairs.add([point_two, point_one])
97
+
98
+ next if road_to_building_one.nil? || road_to_building_two.nil?
99
+
100
+ road_generator.generate_roads_from_coordinate_list(road_to_building_one.concat(road_to_building_two), false)
101
+ end
102
+ end
103
+ end
104
+
105
+ def place_in_front_or_behind(point)
106
+ return [point.x, point.y - 1] if sample_area.point_within_bounds_and_can_have_road?(point.x, point.y - 1)
107
+ return [point.x, point.y + 1] if sample_area.point_within_bounds_and_can_have_road?(point.x, point.y + 1)
108
+ return [point.x - 1, point.y] if sample_area.point_within_bounds_and_can_have_road?(point.x - 1, point.y)
109
+ return [point.x + 1, point.y] if sample_area.point_within_bounds_and_can_have_road?(point.x + 1, point.y)
110
+
111
+ nil
112
+ end
113
+
114
+ def generate_roads_between_towns(verbose)
115
+ return if @all_town_points.length < 2
116
+
117
+ puts 'generating roads between towns...' if verbose
118
+
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
137
+ end
138
+
139
+ def find_town_centroid(points)
140
+ total_x = 0
141
+ total_y = 0
142
+ num_coordinates = points.length
143
+
144
+ points.each do |point|
145
+ total_x += point.x
146
+ total_y += point.y
147
+ end
148
+
149
+ average_x = total_x / num_coordinates.to_f
150
+ average_y = total_y / num_coordinates.to_f
151
+
152
+ [average_x, average_y]
153
+ end
154
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-perlin-2D-map-generator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tyler Matthews (matthewstyler)
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-09 00:00:00.000000000 Z
11
+ date: 2023-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: perlin
@@ -58,14 +58,28 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 2.0.4
61
+ version: 2.1.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 2.0.4
68
+ version: 2.1.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-byebug
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.10.1
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.10.1
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rake
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +100,14 @@ dependencies:
86
100
  requirements:
87
101
  - - "~>"
88
102
  - !ruby/object:Gem::Version
89
- version: 1.54.1
103
+ version: 1.56.0
90
104
  type: :development
91
105
  prerelease: false
92
106
  version_requirements: !ruby/object:Gem::Requirement
93
107
  requirements:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
- version: 1.54.1
110
+ version: 1.56.0
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: simplecov
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -108,10 +122,10 @@ dependencies:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
124
  version: 0.22.0
111
- description: A gem that procedurally generates a seeded and customizable 2D map using
112
- perlin noise. Map can be rendered in console using ansi colors or returned as 2D
113
- array of hashes describing each tile and binome. Completelycustomizable, use the
114
- --help option for full usage details.
125
+ description: A gem that procedurally generates a seeded and customizable 2D map with
126
+ optional roads using perlin noise. Map can be rendered in console using ansi colors
127
+ or returned as 2D array of hashes describing each tile and binome. Completely customizable,
128
+ use the --help option for full usage details.
115
129
  email: matthews.tyl@gmail.com
116
130
  executables:
117
131
  - ruby-perlin-2D-map-generator
@@ -124,20 +138,30 @@ files:
124
138
  - lib/CLI/command.rb
125
139
  - lib/ansi_colours.rb
126
140
  - lib/biome.rb
141
+ - lib/building.rb
127
142
  - lib/flora.rb
143
+ - lib/flora_generator.rb
128
144
  - lib/map.rb
129
145
  - lib/map_config.rb
130
146
  - lib/map_tile_generator.rb
147
+ - lib/pathfinding/a_star_finder.rb
148
+ - lib/pathfinding/grid.rb
149
+ - lib/pathfinding/priority_queue.rb
150
+ - lib/poisson_disk_sampling/sample_area.rb
151
+ - lib/poisson_disk_sampling/sampler.rb
152
+ - lib/road_generator.rb
131
153
  - lib/tile.rb
132
154
  - lib/tile_item.rb
133
155
  - lib/tile_perlin_generator.rb
156
+ - lib/town_generator.rb
134
157
  homepage: https://github.com/matthewstyler/ruby-perlin-2D-map-generator
135
158
  licenses:
136
159
  - MIT
137
160
  metadata:
138
161
  source_code_uri: https://github.com/matthewstyler/ruby-perlin-2D-map-generator
139
162
  bug_tracker_uri: https://github.com/matthewstyler/ruby-perlin-2D-map-generator/issues
140
- post_install_message:
163
+ post_install_message: Thanks for installing! Star on github if you found this useful,
164
+ or raise issues and requests.
141
165
  rdoc_options: []
142
166
  require_paths:
143
167
  - lib
@@ -155,6 +179,6 @@ requirements: []
155
179
  rubygems_version: 3.2.3
156
180
  signing_key:
157
181
  specification_version: 4
158
- summary: Procedurally generate seeded and customizable 2D maps, rendered with ansi
159
- colours or described in a 2D array of hashes
182
+ summary: Procedurally generate seeded and customizable 2D maps with optional roads
183
+ and towns, rendered with ansi colours or described in a 2D array of hashes
160
184
  test_files: []