-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBaseGenerator.js
207 lines (207 loc) · 13.8 KB
/
BaseGenerator.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
"use strict";
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _BaseGenerator_landCoverage, _BaseGenerator_landSize, _BaseGenerator_maxIterations, _BaseGenerator_clusterChance, _BaseGenerator_coverage, _BaseGenerator_pathChance, _BaseGenerator_map, _BaseGenerator_randomNumberGenerator, _BaseGenerator_ruleRegistry, _BaseGenerator_terrainRegistry;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BaseGenerator = void 0;
const Distribution_1 = require("@civ-clone/core-world-generator/Rules/Distribution");
const Generator_1 = require("@civ-clone/core-world-generator/Generator");
const Types_1 = require("@civ-clone/core-terrain/Types");
const RuleRegistry_1 = require("@civ-clone/core-rule/RuleRegistry");
const TerrainRegistry_1 = require("@civ-clone/core-terrain/TerrainRegistry");
const DistributionGroups_1 = require("@civ-clone/core-world-generator/Rules/DistributionGroups");
const getNeighbours_1 = require("@civ-clone/core-world-generator/lib/getNeighbours");
class BaseGenerator extends Generator_1.Generator {
constructor(height = 100, width = 160, options = {}, ruleRegistry = RuleRegistry_1.instance, terrainRegistry = TerrainRegistry_1.instance, randomNumberGenerator = () => Math.random()) {
super(height, width);
_BaseGenerator_landCoverage.set(this, void 0); // % of surface covered with land
_BaseGenerator_landSize.set(this, void 0); // average "size" of landmass
_BaseGenerator_maxIterations.set(this, void 0); // number of times a tile can be tested to change to land
_BaseGenerator_clusterChance.set(this, void 0); // chance for adjacent tiles to cluster
_BaseGenerator_coverage.set(this, void 0); // default total coverage of terrain type
_BaseGenerator_pathChance.set(this, void 0); // default chance for directly adjacent tiles to be part of the path
_BaseGenerator_map.set(this, void 0);
_BaseGenerator_randomNumberGenerator.set(this, void 0);
_BaseGenerator_ruleRegistry.set(this, void 0);
_BaseGenerator_terrainRegistry.set(this, void 0);
const { landCoverage = 0.4, // % of surface covered with land
landSize = 0.2, // average "size" of landmass
maxIterations = 10, // number of times a tile can be tested to change to land
clusterChance = 0.05, // chance for adjacent tiles to cluster
coverage = 0.25, // default total coverage of terrain type
pathChance = 0.05, // default chance for directly adjacent tiles to be part of the path
} = options;
__classPrivateFieldSet(this, _BaseGenerator_landCoverage, landCoverage, "f"); // % of surface covered with land
__classPrivateFieldSet(this, _BaseGenerator_landSize, landSize, "f"); // average "size" of landmass
__classPrivateFieldSet(this, _BaseGenerator_maxIterations, maxIterations, "f"); // number of times a tile can be tested to change to land
__classPrivateFieldSet(this, _BaseGenerator_clusterChance, clusterChance, "f"); // chance for adjacent tiles to cluster
__classPrivateFieldSet(this, _BaseGenerator_coverage, coverage, "f"); // default total coverage of terrain type
__classPrivateFieldSet(this, _BaseGenerator_pathChance, pathChance, "f"); // default chance for directly adjacent tiles to be part of the path
__classPrivateFieldSet(this, _BaseGenerator_ruleRegistry, ruleRegistry, "f");
__classPrivateFieldSet(this, _BaseGenerator_terrainRegistry, terrainRegistry, "f");
__classPrivateFieldSet(this, _BaseGenerator_randomNumberGenerator, randomNumberGenerator, "f");
__classPrivateFieldSet(this, _BaseGenerator_map, new Array(this.height() * this.width())
.fill(0)
.map(() => new Types_1.Water()), "f");
}
async generateIslands() {
const height = this.height(), width = this.width(), maxIslandPercentage = __classPrivateFieldGet(this, _BaseGenerator_landSize, "f") + __classPrivateFieldGet(this, _BaseGenerator_randomNumberGenerator, "f").call(this) * 0.2, landCoverage = __classPrivateFieldGet(this, _BaseGenerator_landCoverage, "f"), maxIterations = __classPrivateFieldGet(this, _BaseGenerator_maxIterations, "f"), maxIslandSize = Math.ceil(((height * width) / 100) * maxIslandPercentage), map = new Array(height * width).fill(0);
while (map.length === 0 ||
map.filter((value) => value === 1).length / map.length <
landCoverage) {
const seen = {}, currentIsland = [], toProcess = [], seedTile = Math.floor(height * width * Math.random()), flagAsSeen = (id) => {
if (!(id in seen)) {
seen[id] = 0;
}
seen[id]++;
};
map[seedTile] = 1;
currentIsland.push(seedTile);
flagAsSeen(seedTile);
toProcess.push(...(0, getNeighbours_1.default)(height, width, seedTile));
while (toProcess.length) {
const currentTile = toProcess.shift();
// ,
// distance = distanceFrom(height, width, seedTile, currentTile),
// surroundingLand = getNeighbours(height, width, currentTile, false).filter(
// (n: number): boolean => map[n] === 1
// );
if ((seen[currentTile] || 0) <= maxIterations) {
if (Math.random() > 0.3
// maxIslandPercentage >= Math.sqrt(distance) * Math.random() ||
// surroundingLand.length * Math.random() > 3
) {
map[currentTile] = 1;
currentIsland.push(currentTile);
(0, getNeighbours_1.default)(height, width, currentTile)
.filter((tile) => toProcess.indexOf(tile) === -1)
.forEach((tile) => toProcess.push(tile));
}
else {
(0, getNeighbours_1.default)(height, width, currentTile).forEach((tile) => {
const index = toProcess.indexOf(tile);
if (index > -1) {
toProcess.splice(index, 1);
}
});
}
flagAsSeen(currentTile);
}
if (currentIsland.length > maxIslandSize ||
map.filter((value) => value === 1).length /
map.length >=
landCoverage) {
break;
}
}
if (map.filter((value) => value === 1).length /
map.length >=
landCoverage) {
break;
}
}
__classPrivateFieldSet(this, _BaseGenerator_map, map.map((value) => {
if (value === 1) {
return new Types_1.Land();
}
return new Types_1.Water();
}), "f");
}
generate() {
return this.generateIslands().then(() => this.populateTerrain().then(() => __classPrivateFieldGet(this, _BaseGenerator_map, "f")));
}
getNeighbours(index, directNeighbours = true) {
return (0, getNeighbours_1.default)(this.height(), this.width(), index, directNeighbours);
}
populateTerrain() {
return new Promise((resolve) => {
const rules = __classPrivateFieldGet(this, _BaseGenerator_ruleRegistry, "f").get(Distribution_1.Distribution);
__classPrivateFieldGet(this, _BaseGenerator_ruleRegistry, "f")
.get(DistributionGroups_1.default)
.filter((rule) => rule.validate())
.map((rule) => {
const result = rule.process();
if (!result) {
throw new TypeError('Unexpected data from DistributionGroups.');
}
return result;
})
.forEach((group) => group.forEach((TerrainType) => rules
.filter((rule) => rule.validate(TerrainType, __classPrivateFieldGet(this, _BaseGenerator_map, "f")))
.map((rule) => {
const result = rule.process(TerrainType, __classPrivateFieldGet(this, _BaseGenerator_map, "f"));
if (!result) {
throw new TypeError('Unexpected data from Distribution.');
}
return result;
})
.forEach((distribution) => distribution.forEach(({ cluster = false, clusterChance = __classPrivateFieldGet(this, _BaseGenerator_clusterChance, "f"), coverage = __classPrivateFieldGet(this, _BaseGenerator_coverage, "f"), fill = false, from = 0, path = false, pathChance = __classPrivateFieldGet(this, _BaseGenerator_pathChance, "f"), to = 1, }) => {
const validIndices = Object.keys(__classPrivateFieldGet(this, _BaseGenerator_map, "f"))
.map((index) => parseInt(index, 10))
.filter((index) =>
// @ts-ignore
__classPrivateFieldGet(this, _BaseGenerator_map, "f")[index] instanceof TerrainType.__proto__)
.filter((index) => index >= from * this.height() * this.width() &&
index <= to * this.height() * this.width());
if (fill) {
validIndices.forEach((index) => {
__classPrivateFieldGet(this, _BaseGenerator_map, "f")[index] = new TerrainType();
});
return;
}
let max = validIndices.length * coverage;
while (max > 0) {
const currentIndex = validIndices[Math.floor(__classPrivateFieldGet(this, _BaseGenerator_randomNumberGenerator, "f").call(this) * validIndices.length)];
__classPrivateFieldGet(this, _BaseGenerator_map, "f")[currentIndex] = new TerrainType();
max--;
if (cluster) {
const clusteredNeighbours = this.getNeighbours(currentIndex).filter((index) => !(__classPrivateFieldGet(this, _BaseGenerator_map, "f")[index] instanceof TerrainType));
while (clusteredNeighbours.length && max > 0) {
const index = clusteredNeighbours.shift();
if (clusterChance >=
__classPrivateFieldGet(this, _BaseGenerator_randomNumberGenerator, "f").call(this) /
this.distanceFrom(currentIndex, index)) {
__classPrivateFieldGet(this, _BaseGenerator_map, "f")[index] = new TerrainType();
max--;
clusteredNeighbours.push(...this.getNeighbours(index)
.filter((index) => !(__classPrivateFieldGet(this, _BaseGenerator_map, "f")[index] instanceof TerrainType))
.filter((index) => __classPrivateFieldGet(this, _BaseGenerator_map, "f")[index] instanceof
// @ts-ignore
TerrainType.__proto__)
.filter((index) => !clusteredNeighbours.includes(index)));
}
}
}
if (path) {
let index = currentIndex;
while (pathChance >= __classPrivateFieldGet(this, _BaseGenerator_randomNumberGenerator, "f").call(this)) {
const candidates = this.getNeighbours(index).filter((index) => __classPrivateFieldGet(this, _BaseGenerator_map, "f")[index] instanceof
// @ts-ignore
TerrainType.__proto__ &&
!(__classPrivateFieldGet(this, _BaseGenerator_map, "f")[index] instanceof TerrainType));
index =
candidates[Math.floor(__classPrivateFieldGet(this, _BaseGenerator_randomNumberGenerator, "f").call(this) *
candidates.length)];
__classPrivateFieldGet(this, _BaseGenerator_map, "f")[index] = new TerrainType();
max--;
}
}
}
}))));
resolve();
});
}
}
exports.BaseGenerator = BaseGenerator;
_BaseGenerator_landCoverage = new WeakMap(), _BaseGenerator_landSize = new WeakMap(), _BaseGenerator_maxIterations = new WeakMap(), _BaseGenerator_clusterChance = new WeakMap(), _BaseGenerator_coverage = new WeakMap(), _BaseGenerator_pathChance = new WeakMap(), _BaseGenerator_map = new WeakMap(), _BaseGenerator_randomNumberGenerator = new WeakMap(), _BaseGenerator_ruleRegistry = new WeakMap(), _BaseGenerator_terrainRegistry = new WeakMap();
exports.default = BaseGenerator;
//# sourceMappingURL=BaseGenerator.js.map