ogamejs 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -321,7 +321,7 @@ Ogame.Fleets.getDebris(Ogame.models.Destroyable[1], 100, 0.3);
321
321
 
322
322
  #### `simulateCombat(attacker, defender, options = {})`
323
323
 
324
- A full battle: up to six rounds, every unit fires once per round at a random enemy, rapid fire grants extra shots, shots below 1% of the target's shield bounce off, shields come back every round, and a unit under 70% hull may explode at the end of the round.
324
+ A full battle: up to six rounds, every unit fires once per round at a random enemy, rapid fire grants extra shots, shots below 1% of the target's shield bounce off, shields come back every round, and a unit hit while under 70% hull may explode. Afterwards, destroyed defenses are rebuilt (70% chance each), the debris field gives the moon chance, and a winning attacker loads what its surviving ships can carry.
325
325
 
326
326
  ```javascript
327
327
  const battle = Ogame.Fleets.simulateCombat(
@@ -333,26 +333,50 @@ const battle = Ogame.Fleets.simulateCombat(
333
333
  fleet: [{ ship: Ogame.models.Destroyable[201], count: 200 }],
334
334
  techs: { weapons: 10, shielding: 10, armour: 10 },
335
335
  },
336
- { seed: 42, debrisFactor: 0.3 },
336
+ {
337
+ seed: 42,
338
+ debrisFactor: 0.3,
339
+ plunder: { resources: { metal: 500000, crystal: 200000, deuterium: 100000 }, hyperspaceLevel: 8 },
340
+ },
337
341
  );
338
342
  // → {
339
343
  // winner: 'attacker' | 'defender' | 'draw',
340
344
  // rounds: Number,
341
345
  // seed: Number,
342
346
  // attacker: { survivors: [{ ship, count }], losses: [{ ship, count }] },
343
- // defender: { survivors: [...], losses: [...] },
347
+ // defender: { survivors: [...], losses: [...], rebuilt: [...] },
344
348
  // debris: { metal, crystal, deuterium },
349
+ // moonChance: Number, // in %
350
+ // plunder: { metal, crystal, deuterium },
345
351
  // }
346
352
  ```
347
353
 
348
- A battle is **random**, so one run is one possible outcome. Pass a `seed` to replay the exact same fight; average several seeds to get a feel for the likely result:
354
+ | Option | Default | |
355
+ | --- | --- | --- |
356
+ | `seed` | `Date.now()` | replays the exact same fight |
357
+ | `debrisFactor` | `0.3` | share of the destroyed ships' metal and crystal left as debris |
358
+ | `deuteriumDebrisFactor` | `0` | same for deuterium |
359
+ | `defenseDebrisFactor` | `0` (`debrisFactor` with `defenseDebris: true`) | same for destroyed defenses |
360
+ | `repairFactor` | `0.7` | chance for each destroyed defense to be rebuilt |
361
+ | `plunder` | none | `{ resources, ratio = 0.5, hyperspaceLevel, hyperspaceMultiplier, bonus }`; the loot is spread like the resources on the planet |
362
+
363
+ #### `simulateCombats(attacker, defender, options = {})` / `getCombatStatistics(results)`
364
+
365
+ A battle is **random**, so one run is one possible outcome. `simulateCombats` runs the same battle `runs` times (100 by default, seeds `seed`, `seed + 1`…) and averages them; `getCombatStatistics` does the averaging on results you simulated yourself, for instance in batches from a Web Worker.
349
366
 
350
367
  ```javascript
351
- const runs = Array.from({ length: 100 }, (_, seed) => simulateCombat(a, d, { seed }));
352
- const winRate = runs.filter((run) => run.winner === 'attacker').length / runs.length;
368
+ Ogame.Fleets.simulateCombats(attacker, defender, { runs: 100, seed: 1, debrisFactor: 0.3 });
369
+ // → {
370
+ // runs: 100,
371
+ // outcomes: { attacker: 0.92, defender: 0, draw: 0.08 }, // shares, from 0 to 1
372
+ // rounds: 2.4,
373
+ // attacker: { survivors, losses, lostResources },
374
+ // defender: { survivors, losses, rebuilt, lostResources },
375
+ // debris, moonChance, plunder,
376
+ // }
353
377
  ```
354
378
 
355
- Destroyed defenses are left out of the debris field unless you pass `defenseDebris: true`. Defense repair after a battle is not modelled.
379
+ Counts are averages, so not whole numbers. `lostResources` is what the units lost for good cost: rebuilt defenses do not count.
356
380
 
357
381
  #### `getMoonbreakChance(moonSize, fleets)` / `getMoonbreakLosses(moonSize, fleets)` / `getMoonbreakWaves(rip)`
358
382
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ogamejs",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "OGame formulas as a dependency-free ESM library: building and research costs, production, build times, fleet debris and marketplace rates.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,5 +1,7 @@
1
1
  import DESTROYABLE, { ATTRIBUTES } from '../models/destroyable.js';
2
2
  import getDebris from './getDebris.js';
3
+ import { getCargoCapacity } from './expedition.js';
4
+ import { MOON_DEBRIS_THRESHOLD, MAX_MOON_CHANCE } from './moonLock.js';
3
5
 
4
6
  const ROUNDS = 6;
5
7
 
@@ -12,9 +14,18 @@ const MAX_SHOTS = 10000;
12
14
  /** A shot weaker than this share of the target shield simply bounces off. */
13
15
  const BOUNCE_RATIO = 0.01;
14
16
 
15
- /** Below that share of its hull, a unit may explode at the end of the round. */
17
+ /** Below that share of its hull, a unit hit by a shot may explode on the spot. */
16
18
  const EXPLOSION_THRESHOLD = 0.7;
17
19
 
20
+ /** The share of destroyed defenses that come back after a battle, on most universes. */
21
+ const DEFAULT_REPAIR_FACTOR = 0.7;
22
+
23
+ /** The share of the defender's resources a winning attacker can carry away. */
24
+ const DEFAULT_PLUNDER_RATIO = 0.5;
25
+
26
+ /** Every 100 000 units of debris add 1% to the moon chance. */
27
+ const MOON_DEBRIS_PER_PERCENT = MOON_DEBRIS_THRESHOLD / MAX_MOON_CHANCE;
28
+
18
29
  /** Rapid-fire tables point at library ids, so we need the way back. */
19
30
  const LIBRARY_ID = new Map(
20
31
  Object.entries(DESTROYABLE).map(([id, entry]) => [entry, Number(id)]),
@@ -106,6 +117,10 @@ function rapidFireAgainst(unit, target) {
106
117
  /**
107
118
  * One unit fires at a random enemy, then keeps firing while its rapid-fire
108
119
  * bonus rolls in its favour.
120
+ *
121
+ * Every shot that gets through may blow up a target left under 70% of its
122
+ * hull, so a unit hit several times in a round gets several rolls. A wrecked
123
+ * unit stays a target until the end of the round, and shots on it are wasted.
109
124
  */
110
125
  function fire(unit, enemies, random) {
111
126
  for (let shots = 0; shots < MAX_SHOTS; shots += 1) {
@@ -117,13 +132,19 @@ function fire(unit, enemies, random) {
117
132
  const damage = unit.attack;
118
133
 
119
134
  // A shot too weak to dent the shield bounces off without doing anything.
120
- if (damage >= target.shield * BOUNCE_RATIO) {
135
+ if (target.hull > 0 && damage >= target.shield * BOUNCE_RATIO) {
121
136
  if (damage <= target.shield) {
122
137
  target.shield -= damage;
123
138
  } else {
124
139
  target.hull -= damage - target.shield;
125
140
  target.shield = 0;
126
141
  }
142
+
143
+ const integrity = target.hull / target.maxHull;
144
+
145
+ if (target.hull > 0 && integrity < EXPLOSION_THRESHOLD && random() < 1 - integrity) {
146
+ target.hull = 0;
147
+ }
127
148
  }
128
149
 
129
150
  const rapid = rapidFireAgainst(unit, target);
@@ -135,11 +156,8 @@ function fire(unit, enemies, random) {
135
156
  }
136
157
  }
137
158
 
138
- /**
139
- * Destroyed units are removed, damaged ones may explode, survivors get their
140
- * shield back for the next round.
141
- */
142
- function endRound(units, random) {
159
+ /** Destroyed units are removed, survivors get their shield back for the next round. */
160
+ function endRound(units) {
143
161
  const survivors = [];
144
162
 
145
163
  for (const unit of units) {
@@ -147,12 +165,6 @@ function endRound(units, random) {
147
165
  continue;
148
166
  }
149
167
 
150
- const integrity = unit.hull / unit.maxHull;
151
-
152
- if (integrity < EXPLOSION_THRESHOLD && random() < 1 - integrity) {
153
- continue;
154
- }
155
-
156
168
  unit.shield = unit.maxShield;
157
169
  survivors.push(unit);
158
170
  }
@@ -186,12 +198,13 @@ function summarise(units, initial) {
186
198
  }
187
199
 
188
200
  function debrisOf(losses, options) {
189
- const { debrisFactor, deuteriumDebrisFactor, defenseDebris } = options;
201
+ const { debrisFactor, deuteriumDebrisFactor, defenseDebrisFactor } = options;
190
202
 
191
203
  return losses
192
- // On most universes only ships leave debris behind.
193
- .filter(({ ship }) => defenseDebris || ship.category === ATTRIBUTES.CATEGORIES.SHIPS)
194
- .map(({ ship, count }) => getDebris(ship, count, debrisFactor, deuteriumDebrisFactor))
204
+ // On most universes only ships leave debris behind, defenses have their own factor.
205
+ .map(({ ship, count }) => (ship.category === ATTRIBUTES.CATEGORIES.SHIPS
206
+ ? getDebris(ship, count, debrisFactor, deuteriumDebrisFactor)
207
+ : getDebris(ship, count, defenseDebrisFactor, deuteriumDebrisFactor > 0 ? defenseDebrisFactor : 0)))
195
208
  .reduce((total, debris) => ({
196
209
  metal: total.metal + debris.metal,
197
210
  crystal: total.crystal + debris.crystal,
@@ -199,17 +212,101 @@ function debrisOf(losses, options) {
199
212
  }), { metal: 0, crystal: 0, deuterium: 0 });
200
213
  }
201
214
 
215
+ /** Each destroyed defense is rebuilt, or not, on its own roll. */
216
+ function rebuild(losses, repairFactor, random) {
217
+ const rebuilt = [];
218
+
219
+ for (const { ship, count } of losses) {
220
+ if (ship.category !== ATTRIBUTES.CATEGORIES.DEFENSES) {
221
+ continue;
222
+ }
223
+
224
+ let back = 0;
225
+
226
+ for (let i = 0; i < count; i += 1) {
227
+ if (random() < repairFactor) {
228
+ back += 1;
229
+ }
230
+ }
231
+
232
+ if (back > 0) {
233
+ rebuilt.push({ ship, count: back });
234
+ }
235
+ }
236
+
237
+ return rebuilt;
238
+ }
239
+
240
+ /**
241
+ * The surviving ships carry away up to `ratio` of the defender's resources,
242
+ * spread in proportion to what lies on the planet.
243
+ */
244
+ function plunderOf(survivors, plunder) {
245
+ const {
246
+ resources, ratio = DEFAULT_PLUNDER_RATIO, hyperspaceLevel, hyperspaceMultiplier, bonus,
247
+ } = plunder;
248
+
249
+ const lootable = {
250
+ metal: (resources.metal ?? 0) * ratio,
251
+ crystal: (resources.crystal ?? 0) * ratio,
252
+ deuterium: (resources.deuterium ?? 0) * ratio,
253
+ };
254
+ const total = lootable.metal + lootable.crystal + lootable.deuterium;
255
+ const capacity = survivors
256
+ .map(({ ship, count }) => getCargoCapacity(ship, { hyperspaceLevel, hyperspaceMultiplier, bonus }) * count)
257
+ .reduce((sum, value) => sum + value, 0);
258
+
259
+ if (total === 0 || capacity >= total) {
260
+ return lootable;
261
+ }
262
+
263
+ const share = capacity / total;
264
+
265
+ return {
266
+ metal: Math.floor(lootable.metal * share),
267
+ crystal: Math.floor(lootable.crystal * share),
268
+ deuterium: Math.floor(lootable.deuterium * share),
269
+ };
270
+ }
271
+
272
+ function moonChanceOf(debris) {
273
+ const total = debris.metal + debris.crystal + debris.deuterium;
274
+
275
+ return Math.min(MAX_MOON_CHANCE, Math.floor(total / MOON_DEBRIS_PER_PERCENT));
276
+ }
277
+
278
+ /**
279
+ * @typedef {object} CombatResult One simulated battle
280
+ * @property {'attacker'|'defender'|'draw'} winner Who won
281
+ * @property {number} rounds How many rounds it lasted
282
+ * @property {number} seed The PRNG seed it ran with
283
+ * @property {{survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[]}} attacker
284
+ * What is left of the attacker, and what it lost
285
+ * @property {{
286
+ * survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[],
287
+ * rebuilt: import('../types.js').FleetEntry[],
288
+ * }} defender What is left of the defender, what it lost, and which of its lost defenses come back
289
+ * @property {import('../types.js').Resources} debris The debris field
290
+ * @property {number} moonChance The moon chance it gives, in %
291
+ * @property {import('../types.js').Resources} plunder What the attacker carries away
292
+ */
293
+
202
294
  /**
203
295
  *
204
296
  * Simulate a battle between two fleets
205
297
  *
206
298
  * Follows the game rules: up to six rounds, every unit fires once per round at a
207
299
  * random enemy, rapid fire grants extra shots, shots below 1% of the target
208
- * shield bounce off, shields come back every round, and a unit under 70% hull
209
- * may explode at the end of the round.
300
+ * shield bounce off, shields come back every round, and a unit hit while under
301
+ * 70% hull may explode.
302
+ *
303
+ * Afterwards, each destroyed defense is rebuilt with a `repairFactor` chance,
304
+ * the debris field gives the moon chance (1% per 100 000, up to 20%), and a
305
+ * winning attacker loads what it can of the defender's resources when
306
+ * `plunder` says what lies on the planet.
210
307
  *
211
308
  * A battle is random, so one run is one possible outcome. The `seed` makes a run
212
- * reproducible; average several seeds to get a feel for the likely result.
309
+ * reproducible; `getCombatStatistics` averages several of them.
213
310
  * @param {object} attacker The attacking side
214
311
  * @param {import('../types.js').FleetEntry[]} attacker.fleet Its ships
215
312
  * @param {import('../types.js').CombatTechs} [attacker.techs] Its combat technology levels
@@ -218,13 +315,16 @@ function debrisOf(losses, options) {
218
315
  * @param {number} [options.seed] The PRNG seed, for a reproducible battle
219
316
  * @param {number} [options.debrisFactor] The universe debris factor
220
317
  * @param {number} [options.deuteriumDebrisFactor] The universe deuterium debris factor
221
- * @param {boolean} [options.defenseDebris] Whether destroyed defenses leave debris
222
- * @returns {{
223
- * winner: 'attacker'|'defender'|'draw', rounds: number, seed: number,
224
- * attacker: {survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[]},
225
- * defender: {survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[]},
226
- * debris: import('../types.js').Resources,
227
- * }} Who won, how long it took, what is left on each side, and the debris field
318
+ * @param {boolean} [options.defenseDebris] Whether destroyed defenses leave debris, at `debrisFactor`
319
+ * @param {number} [options.defenseDebrisFactor] The universe defense debris factor, overrides `defenseDebris`
320
+ * @param {number} [options.repairFactor] The chance for a destroyed defense to be rebuilt, 0.7 by default
321
+ * @param {object} [options.plunder] What the attacker may carry away if it wins
322
+ * @param {import('../types.js').Resources} options.plunder.resources The resources on the planet
323
+ * @param {number} [options.plunder.ratio] The share of them up for grabs, 0.5 by default
324
+ * @param {number} [options.plunder.hyperspaceLevel] The attacker's Hyperspace Technology level
325
+ * @param {number} [options.plunder.hyperspaceMultiplier] The % of base cargo per level, 5 by default
326
+ * @param {number} [options.plunder.bonus] Any other cargo bonus, as a fraction
327
+ * @returns {CombatResult} Who won, how long it took, what is left on each side, and what the battle left behind
228
328
  */
229
329
  function simulateCombat(attacker, defender, options = {}) {
230
330
  const {
@@ -232,6 +332,9 @@ function simulateCombat(attacker, defender, options = {}) {
232
332
  debrisFactor = 0.3,
233
333
  deuteriumDebrisFactor = 0,
234
334
  defenseDebris = false,
335
+ defenseDebrisFactor = defenseDebris ? debrisFactor : 0,
336
+ repairFactor = DEFAULT_REPAIR_FACTOR,
337
+ plunder,
235
338
  } = options;
236
339
 
237
340
  const random = createRandom(seed);
@@ -258,8 +361,8 @@ function simulateCombat(attacker, defender, options = {}) {
258
361
  fire(unit, attackers, random);
259
362
  }
260
363
 
261
- attackers = endRound(attackers, random);
262
- defenders = endRound(defenders, random);
364
+ attackers = endRound(attackers);
365
+ defenders = endRound(defenders);
263
366
  }
264
367
 
265
368
  const attackerResult = summarise(attackers, attackerFleet);
@@ -273,19 +376,25 @@ function simulateCombat(attacker, defender, options = {}) {
273
376
  winner = 'defender';
274
377
  }
275
378
 
379
+ const debris = debrisOf([...attackerResult.losses, ...defenderResult.losses], {
380
+ debrisFactor,
381
+ deuteriumDebrisFactor,
382
+ defenseDebrisFactor,
383
+ });
384
+
276
385
  return {
277
386
  winner,
278
387
  rounds,
279
388
  seed,
280
389
  attacker: attackerResult,
281
- defender: defenderResult,
282
- debris: debrisOf([...attackerResult.losses, ...defenderResult.losses], {
283
- debrisFactor,
284
- deuteriumDebrisFactor,
285
- defenseDebris,
286
- }),
390
+ defender: { ...defenderResult, rebuilt: rebuild(defenderResult.losses, repairFactor, random) },
391
+ debris,
392
+ moonChance: moonChanceOf(debris),
393
+ plunder: plunder && winner === 'attacker'
394
+ ? plunderOf(attackerResult.survivors, plunder)
395
+ : { metal: 0, crystal: 0, deuterium: 0 },
287
396
  };
288
397
  }
289
398
 
290
- export { createRandom, ROUNDS };
399
+ export { createRandom, ROUNDS, DEFAULT_REPAIR_FACTOR, DEFAULT_PLUNDER_RATIO };
291
400
  export default simulateCombat;
@@ -0,0 +1,189 @@
1
+ import simulateCombat from './combat.js';
2
+
3
+ /** Enough runs for the averages to settle, without making a big battle wait. */
4
+ const DEFAULT_RUNS = 100;
5
+
6
+ const NO_RESOURCES = Object.freeze({ metal: 0, crystal: 0, deuterium: 0 });
7
+
8
+ function addResources(total, resources) {
9
+ return {
10
+ metal: total.metal + resources.metal,
11
+ crystal: total.crystal + resources.crystal,
12
+ deuterium: total.deuterium + resources.deuterium,
13
+ };
14
+ }
15
+
16
+ function scaleResources(resources, factor) {
17
+ return {
18
+ metal: resources.metal * factor,
19
+ crystal: resources.crystal * factor,
20
+ deuterium: resources.deuterium * factor,
21
+ };
22
+ }
23
+
24
+ function costOf(fleet) {
25
+ return fleet
26
+ .map(({ ship, count }) => ({
27
+ metal: (ship.cost.metal ?? 0) * count,
28
+ crystal: (ship.cost.crystal ?? 0) * count,
29
+ deuterium: (ship.cost.deuterium ?? ship.cost.deut ?? 0) * count,
30
+ }))
31
+ .reduce(addResources, NO_RESOURCES);
32
+ }
33
+
34
+ /** Two copies of the same model entry are the same ship. */
35
+ function keyOf(ship) {
36
+ return ship.ogameId ?? ship;
37
+ }
38
+
39
+ /** Sum the counts of every run, keeping the order ships first appear in. */
40
+ function addFleets(totals, fleet) {
41
+ for (const { ship, count } of fleet) {
42
+ const key = keyOf(ship);
43
+ const entry = totals.get(key);
44
+
45
+ if (entry) {
46
+ entry.count += count;
47
+ } else {
48
+ totals.set(key, { ship, count });
49
+ }
50
+ }
51
+ }
52
+
53
+ function averageFleet(totals, runs) {
54
+ return [...totals.values()].map(({ ship, count }) => ({ ship, count: count / runs }));
55
+ }
56
+
57
+ /**
58
+ * Take back what was rebuilt from what was lost, ship by ship.
59
+ * @param {import('../types.js').FleetEntry[]} losses What was destroyed
60
+ * @param {import('../types.js').FleetEntry[]} rebuilt What came back
61
+ * @returns {import('../types.js').FleetEntry[]} What is gone for good
62
+ */
63
+ function subtractFleet(losses, rebuilt) {
64
+ return losses
65
+ .map(({ ship, count }) => ({
66
+ ship,
67
+ count: count - (rebuilt.find((entry) => keyOf(entry.ship) === keyOf(ship))?.count ?? 0),
68
+ }))
69
+ .filter(({ count }) => count > 0);
70
+ }
71
+
72
+ /**
73
+ *
74
+ * Average a set of simulated battles
75
+ *
76
+ * Counts are averages, so they are not whole numbers. `lostResources` is what
77
+ * the units lost for good would cost to build again: every loss for the
78
+ * attacker, the defender's losses minus its rebuilt defenses.
79
+ * @param {import('./combat.js').CombatResult[]} results Battles from `simulateCombat`
80
+ * @returns {{
81
+ * runs: number,
82
+ * outcomes: {attacker: number, defender: number, draw: number},
83
+ * rounds: number,
84
+ * attacker: {
85
+ * survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[],
86
+ * lostResources: import('../types.js').Resources,
87
+ * },
88
+ * defender: {
89
+ * survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[],
90
+ * rebuilt: import('../types.js').FleetEntry[], lostResources: import('../types.js').Resources,
91
+ * },
92
+ * debris: import('../types.js').Resources,
93
+ * moonChance: number,
94
+ * plunder: import('../types.js').Resources,
95
+ * }} The share of battles each side won (from 0 to 1), and the average of everything else
96
+ */
97
+ function getCombatStatistics(results) {
98
+ if (!Array.isArray(results) || results.length === 0) {
99
+ throw new Error('results must be a non empty array of simulateCombat results');
100
+ }
101
+
102
+ const runs = results.length;
103
+ const outcomes = { attacker: 0, defender: 0, draw: 0 };
104
+ const fleets = {
105
+ attackerSurvivors: new Map(),
106
+ attackerLosses: new Map(),
107
+ defenderSurvivors: new Map(),
108
+ defenderLosses: new Map(),
109
+ defenderRebuilt: new Map(),
110
+ };
111
+ let rounds = 0;
112
+ let moonChance = 0;
113
+ let debris = NO_RESOURCES;
114
+ let plunder = NO_RESOURCES;
115
+ let attackerLost = NO_RESOURCES;
116
+ let defenderLost = NO_RESOURCES;
117
+
118
+ for (const result of results) {
119
+ outcomes[result.winner] += 1;
120
+ rounds += result.rounds;
121
+ moonChance += result.moonChance;
122
+ debris = addResources(debris, result.debris);
123
+ plunder = addResources(plunder, result.plunder);
124
+ attackerLost = addResources(attackerLost, costOf(result.attacker.losses));
125
+ defenderLost = addResources(
126
+ defenderLost,
127
+ costOf(subtractFleet(result.defender.losses, result.defender.rebuilt)),
128
+ );
129
+
130
+ addFleets(fleets.attackerSurvivors, result.attacker.survivors);
131
+ addFleets(fleets.attackerLosses, result.attacker.losses);
132
+ addFleets(fleets.defenderSurvivors, result.defender.survivors);
133
+ addFleets(fleets.defenderLosses, result.defender.losses);
134
+ addFleets(fleets.defenderRebuilt, result.defender.rebuilt);
135
+ }
136
+
137
+ return {
138
+ runs,
139
+ outcomes: {
140
+ attacker: outcomes.attacker / runs,
141
+ defender: outcomes.defender / runs,
142
+ draw: outcomes.draw / runs,
143
+ },
144
+ rounds: rounds / runs,
145
+ attacker: {
146
+ survivors: averageFleet(fleets.attackerSurvivors, runs),
147
+ losses: averageFleet(fleets.attackerLosses, runs),
148
+ lostResources: scaleResources(attackerLost, 1 / runs),
149
+ },
150
+ defender: {
151
+ survivors: averageFleet(fleets.defenderSurvivors, runs),
152
+ losses: averageFleet(fleets.defenderLosses, runs),
153
+ rebuilt: averageFleet(fleets.defenderRebuilt, runs),
154
+ lostResources: scaleResources(defenderLost, 1 / runs),
155
+ },
156
+ debris: scaleResources(debris, 1 / runs),
157
+ moonChance: moonChance / runs,
158
+ plunder: scaleResources(plunder, 1 / runs),
159
+ };
160
+ }
161
+
162
+ /**
163
+ *
164
+ * Simulate the same battle many times and average the outcomes
165
+ *
166
+ * Run `i` uses the seed `seed + i`, so the whole set is reproducible.
167
+ * @param {Parameters<typeof simulateCombat>[0]} attacker The attacking side, as for `simulateCombat`
168
+ * @param {Parameters<typeof simulateCombat>[1]} defender The defending side, as for `simulateCombat`
169
+ * @param {Parameters<typeof simulateCombat>[2] & {runs?: number}} [options] The `simulateCombat`
170
+ * options, plus `runs`, how many battles to simulate (100 by default)
171
+ * @returns {ReturnType<typeof getCombatStatistics>} The averaged outcome, see `getCombatStatistics`
172
+ */
173
+ function simulateCombats(attacker, defender, options = {}) {
174
+ const { runs = DEFAULT_RUNS, seed = Date.now(), ...rest } = options;
175
+
176
+ if (!Number.isInteger(runs) || runs < 1) {
177
+ throw new Error(`runs must be an integer >= 1, received ${runs}`);
178
+ }
179
+
180
+ const results = [];
181
+
182
+ for (let i = 0; i < runs; i += 1) {
183
+ results.push(simulateCombat(attacker, defender, { ...rest, seed: seed + i }));
184
+ }
185
+
186
+ return getCombatStatistics(results);
187
+ }
188
+
189
+ export { getCombatStatistics, simulateCombats, DEFAULT_RUNS };
@@ -2,7 +2,8 @@ import getDebris from './getDebris.js';
2
2
  import getDistance from './distance.js';
3
3
  import getShipSpeed, { getActiveDrive, getFleetSpeed } from './speed.js';
4
4
  import { getFlightTime, getFuelConsumption, getTrip } from './flight.js';
5
- import simulateCombat from './combat.js';
5
+ import simulateCombat, { DEFAULT_REPAIR_FACTOR, DEFAULT_PLUNDER_RATIO } from './combat.js';
6
+ import { getCombatStatistics, simulateCombats, DEFAULT_RUNS } from './combatStatistics.js';
6
7
  import {
7
8
  getWaveChance,
8
9
  getMoonbreakWaves,
@@ -31,6 +32,8 @@ const Fleets = {
31
32
  getFuelConsumption,
32
33
  getTrip,
33
34
  simulateCombat,
35
+ simulateCombats,
36
+ getCombatStatistics,
34
37
  getWaveChance,
35
38
  getMoonbreakWaves,
36
39
  getMoonbreakChance,
@@ -49,6 +52,9 @@ export {
49
52
  EXPEDITION_TOP_TIER_BASE,
50
53
  MOON_DEBRIS_THRESHOLD,
51
54
  MAX_MOON_CHANCE,
55
+ DEFAULT_REPAIR_FACTOR,
56
+ DEFAULT_PLUNDER_RATIO,
57
+ DEFAULT_RUNS,
52
58
  };
53
59
 
54
60
  export default Fleets;
@@ -1,19 +1,84 @@
1
1
  declare const ROUNDS = 6;
2
+ /** The share of destroyed defenses that come back after a battle, on most universes. */
3
+ declare const DEFAULT_REPAIR_FACTOR = 0.7;
4
+ /** The share of the defender's resources a winning attacker can carry away. */
5
+ declare const DEFAULT_PLUNDER_RATIO = 0.5;
2
6
  /**
3
7
  * A tiny deterministic PRNG (mulberry32), so a battle can be replayed.
4
8
  */
5
9
  declare function createRandom(seed: any): () => number;
10
+ export type CombatResult = {
11
+ /**
12
+ * Who won
13
+ */
14
+ winner: 'attacker' | 'defender' | 'draw';
15
+ /**
16
+ * How many rounds it lasted
17
+ */
18
+ rounds: number;
19
+ /**
20
+ * The PRNG seed it ran with
21
+ */
22
+ seed: number;
23
+ /**
24
+ * What is left of the attacker, and what it lost
25
+ */
26
+ attacker: {
27
+ survivors: import('../types.js').FleetEntry[];
28
+ losses: import('../types.js').FleetEntry[];
29
+ };
30
+ /**
31
+ * What is left of the defender, what it lost, and which of its lost defenses come back
32
+ */
33
+ defender: {
34
+ survivors: import('../types.js').FleetEntry[];
35
+ losses: import('../types.js').FleetEntry[];
36
+ rebuilt: import('../types.js').FleetEntry[];
37
+ };
38
+ /**
39
+ * The debris field
40
+ */
41
+ debris: import('../types.js').Resources;
42
+ /**
43
+ * The moon chance it gives, in %
44
+ */
45
+ moonChance: number;
46
+ /**
47
+ * What the attacker carries away
48
+ */
49
+ plunder: import('../types.js').Resources;
50
+ };
51
+ /**
52
+ * @typedef {object} CombatResult One simulated battle
53
+ * @property {'attacker'|'defender'|'draw'} winner Who won
54
+ * @property {number} rounds How many rounds it lasted
55
+ * @property {number} seed The PRNG seed it ran with
56
+ * @property {{survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[]}} attacker
57
+ * What is left of the attacker, and what it lost
58
+ * @property {{
59
+ * survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[],
60
+ * rebuilt: import('../types.js').FleetEntry[],
61
+ * }} defender What is left of the defender, what it lost, and which of its lost defenses come back
62
+ * @property {import('../types.js').Resources} debris The debris field
63
+ * @property {number} moonChance The moon chance it gives, in %
64
+ * @property {import('../types.js').Resources} plunder What the attacker carries away
65
+ */
6
66
  /**
7
67
  *
8
68
  * Simulate a battle between two fleets
9
69
  *
10
70
  * Follows the game rules: up to six rounds, every unit fires once per round at a
11
71
  * random enemy, rapid fire grants extra shots, shots below 1% of the target
12
- * shield bounce off, shields come back every round, and a unit under 70% hull
13
- * may explode at the end of the round.
72
+ * shield bounce off, shields come back every round, and a unit hit while under
73
+ * 70% hull may explode.
74
+ *
75
+ * Afterwards, each destroyed defense is rebuilt with a `repairFactor` chance,
76
+ * the debris field gives the moon chance (1% per 100 000, up to 20%), and a
77
+ * winning attacker loads what it can of the defender's resources when
78
+ * `plunder` says what lies on the planet.
14
79
  *
15
80
  * A battle is random, so one run is one possible outcome. The `seed` makes a run
16
- * reproducible; average several seeds to get a feel for the likely result.
81
+ * reproducible; `getCombatStatistics` averages several of them.
17
82
  * @param {object} attacker The attacking side
18
83
  * @param {import('../types.js').FleetEntry[]} attacker.fleet Its ships
19
84
  * @param {import('../types.js').CombatTechs} [attacker.techs] Its combat technology levels
@@ -22,13 +87,16 @@ declare function createRandom(seed: any): () => number;
22
87
  * @param {number} [options.seed] The PRNG seed, for a reproducible battle
23
88
  * @param {number} [options.debrisFactor] The universe debris factor
24
89
  * @param {number} [options.deuteriumDebrisFactor] The universe deuterium debris factor
25
- * @param {boolean} [options.defenseDebris] Whether destroyed defenses leave debris
26
- * @returns {{
27
- * winner: 'attacker'|'defender'|'draw', rounds: number, seed: number,
28
- * attacker: {survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[]},
29
- * defender: {survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[]},
30
- * debris: import('../types.js').Resources,
31
- * }} Who won, how long it took, what is left on each side, and the debris field
90
+ * @param {boolean} [options.defenseDebris] Whether destroyed defenses leave debris, at `debrisFactor`
91
+ * @param {number} [options.defenseDebrisFactor] The universe defense debris factor, overrides `defenseDebris`
92
+ * @param {number} [options.repairFactor] The chance for a destroyed defense to be rebuilt, 0.7 by default
93
+ * @param {object} [options.plunder] What the attacker may carry away if it wins
94
+ * @param {import('../types.js').Resources} options.plunder.resources The resources on the planet
95
+ * @param {number} [options.plunder.ratio] The share of them up for grabs, 0.5 by default
96
+ * @param {number} [options.plunder.hyperspaceLevel] The attacker's Hyperspace Technology level
97
+ * @param {number} [options.plunder.hyperspaceMultiplier] The % of base cargo per level, 5 by default
98
+ * @param {number} [options.plunder.bonus] Any other cargo bonus, as a fraction
99
+ * @returns {CombatResult} Who won, how long it took, what is left on each side, and what the battle left behind
32
100
  */
33
101
  declare function simulateCombat(attacker: {
34
102
  fleet: import('../types.js').FleetEntry[];
@@ -38,20 +106,16 @@ declare function simulateCombat(attacker: {
38
106
  debrisFactor?: number;
39
107
  deuteriumDebrisFactor?: number;
40
108
  defenseDebris?: boolean;
41
- }): {
42
- winner: 'attacker' | 'defender' | 'draw';
43
- rounds: number;
44
- seed: number;
45
- attacker: {
46
- survivors: import('../types.js').FleetEntry[];
47
- losses: import('../types.js').FleetEntry[];
109
+ defenseDebrisFactor?: number;
110
+ repairFactor?: number;
111
+ plunder?: {
112
+ resources: import('../types.js').Resources;
113
+ ratio?: number;
114
+ hyperspaceLevel?: number;
115
+ hyperspaceMultiplier?: number;
116
+ bonus?: number;
48
117
  };
49
- defender: {
50
- survivors: import('../types.js').FleetEntry[];
51
- losses: import('../types.js').FleetEntry[];
52
- };
53
- debris: import('../types.js').Resources;
54
- };
55
- export { createRandom, ROUNDS };
118
+ }): CombatResult;
119
+ export { createRandom, ROUNDS, DEFAULT_REPAIR_FACTOR, DEFAULT_PLUNDER_RATIO };
56
120
  export default simulateCombat;
57
121
  //# sourceMappingURL=combat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"combat.d.ts","sourceRoot":"","sources":["../../src/fleets/combat.js"],"names":[],"mappings":"AAGA,QAAA,MAAM,MAAM,IAAI,CAAC;AAmCjB;;GAEG;AACH,iBAAS,YAAY,CAAC,IAAI,KAAA,gBAWzB;AAqJD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,iBAAS,cAAc,CAAC,QAAQ,EAf7B;IAAqD,KAAK,EAAlD,OAAO,aAAa,EAAE,UAAU,EAAE,CAC1C;IAAqD,KAAK,AAA1D,CACA,EADQ,OAAO,aAAa,EAAE,WAAW,CACzC;CAa6B,EAAE,QAAQ,EAb/B,MAa+B,EAAE,OAAO,AAZhD,CAKA,EAJA;IAAyB,IAAI,AAA7B,CACA,EADQ,MAAM,CACd;IAAyB,YAAY,AAArC,CACA,EADQ,MAAM,CACd;IAAyB,qBAAqB,AAA9C,CACA,EADQ,MAAM,CACd;IAA0B,aAAa,AAAvC,CACA,EADQ,OAAO,CACf;CAOqD,GAP3C;IACR,MAAM,EAAE,UAAU,GAAC,UAAU,GAAC,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IACnE,QAAQ,EAAE;QAAC,SAAS,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;QAAC,MAAM,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAA;KAAC,CAAC;IACtG,QAAQ,EAAE;QAAC,SAAS,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;QAAC,MAAM,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAA;KAAC,CAAC;IACtG,MAAM,EAAE,OAAO,aAAa,EAAE,SAAS,CAAC;CACzC,CA6DH;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC;eACjB,cAAc"}
1
+ {"version":3,"file":"combat.d.ts","sourceRoot":"","sources":["../../src/fleets/combat.js"],"names":[],"mappings":"AAKA,QAAA,MAAM,MAAM,IAAI,CAAC;AAcjB,wFAAwF;AACxF,QAAA,MAAM,qBAAqB,MAAM,CAAC;AAElC,+EAA+E;AAC/E,QAAA,MAAM,qBAAqB,MAAM,CAAC;AA0BlC;;GAEG;AACH,iBAAS,YAAY,CAAC,IAAI,KAAA,gBAWzB;AAuNE,YAAkB,YAAY,GAC9B;;;;IAAyC,MAAM,EAApC,UAAU,GAAC,UAAU,GAAC,MAAM,CACvC;;;;IAAmB,MAAM,EAAd,MAAM,CACjB;;;;IAAmB,IAAI,EAAZ,MAAM,CACjB;;;;IAAwG,QAAQ,EAArG;QAAC,SAAS,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;QAAC,MAAM,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAA;KAAC,CAEtG;;;;IAGG,QAAQ,EAHA;QACT,SAAS,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;QAAC,MAAM,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;QAC1F,OAAO,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;KAC7C,CACD;;;;IAA4C,MAAM,EAAvC,OAAO,aAAa,EAAE,SAAS,CAC1C;;;;IAAmB,UAAU,EAAlB,MAAM,CACjB;;;;IAA4C,OAAO,EAAxC,OAAO,aAAa,EAAE,SAAS,CAC5C;CAAA,CAAA;AAdD;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,iBAAS,cAAc,CAAC,QAAQ,EAlB7B;IAAqD,KAAK,EAAlD,OAAO,aAAa,EAAE,UAAU,EAAE,CAC1C;IAAqD,KAAK,AAA1D,CACA,EADQ,OAAO,aAAa,EAAE,WAAW,CACzC;CAgB6B,EAAE,QAAQ,EAhB/B,MAgB+B,EAAE,OAAO,AAfhD,CAaA,EAZA;IAAyB,IAAI,AAA7B,CACA,EADQ,MAAM,CACd;IAAyB,YAAY,AAArC,CACA,EADQ,MAAM,CACd;IAAyB,qBAAqB,AAA9C,CACA,EADQ,MAAM,CACd;IAA0B,aAAa,AAAvC,CACA,EADQ,OAAO,CACf;IAAyB,mBAAmB,AAA5C,CACA,EADQ,MAAM,CACd;IAAyB,YAAY,AAArC,CACA,EADQ,MAAM,CACd;IAAyB,OAAO,AAAhC,CAMA,EALA;QAAyD,SAAS,EAA1D,OAAO,aAAa,EAAE,SAAS,CACvC;QAAiC,KAAK,AAAtC,CACA,EADQ,MAAM,CACd;QAAiC,eAAe,AAAhD,CACA,EADQ,MAAM,CACd;QAAiC,oBAAoB,AAArD,CACA,EADQ,MAAM,CACd;QAAiC,KAAK,AAAtC,CACA,EADQ,MAAM,CACd;KAAA,CAAA;CAEqD,GAF3C,YAAY,CAsExB;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,CAAC;eAC/D,cAAc"}
@@ -0,0 +1,67 @@
1
+ import simulateCombat from './combat.js';
2
+ /** Enough runs for the averages to settle, without making a big battle wait. */
3
+ declare const DEFAULT_RUNS = 100;
4
+ /**
5
+ *
6
+ * Average a set of simulated battles
7
+ *
8
+ * Counts are averages, so they are not whole numbers. `lostResources` is what
9
+ * the units lost for good would cost to build again: every loss for the
10
+ * attacker, the defender's losses minus its rebuilt defenses.
11
+ * @param {import('./combat.js').CombatResult[]} results Battles from `simulateCombat`
12
+ * @returns {{
13
+ * runs: number,
14
+ * outcomes: {attacker: number, defender: number, draw: number},
15
+ * rounds: number,
16
+ * attacker: {
17
+ * survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[],
18
+ * lostResources: import('../types.js').Resources,
19
+ * },
20
+ * defender: {
21
+ * survivors: import('../types.js').FleetEntry[], losses: import('../types.js').FleetEntry[],
22
+ * rebuilt: import('../types.js').FleetEntry[], lostResources: import('../types.js').Resources,
23
+ * },
24
+ * debris: import('../types.js').Resources,
25
+ * moonChance: number,
26
+ * plunder: import('../types.js').Resources,
27
+ * }} The share of battles each side won (from 0 to 1), and the average of everything else
28
+ */
29
+ declare function getCombatStatistics(results: import('./combat.js').CombatResult[]): {
30
+ runs: number;
31
+ outcomes: {
32
+ attacker: number;
33
+ defender: number;
34
+ draw: number;
35
+ };
36
+ rounds: number;
37
+ attacker: {
38
+ survivors: import('../types.js').FleetEntry[];
39
+ losses: import('../types.js').FleetEntry[];
40
+ lostResources: import('../types.js').Resources;
41
+ };
42
+ defender: {
43
+ survivors: import('../types.js').FleetEntry[];
44
+ losses: import('../types.js').FleetEntry[];
45
+ rebuilt: import('../types.js').FleetEntry[];
46
+ lostResources: import('../types.js').Resources;
47
+ };
48
+ debris: import('../types.js').Resources;
49
+ moonChance: number;
50
+ plunder: import('../types.js').Resources;
51
+ };
52
+ /**
53
+ *
54
+ * Simulate the same battle many times and average the outcomes
55
+ *
56
+ * Run `i` uses the seed `seed + i`, so the whole set is reproducible.
57
+ * @param {Parameters<typeof simulateCombat>[0]} attacker The attacking side, as for `simulateCombat`
58
+ * @param {Parameters<typeof simulateCombat>[1]} defender The defending side, as for `simulateCombat`
59
+ * @param {Parameters<typeof simulateCombat>[2] & {runs?: number}} [options] The `simulateCombat`
60
+ * options, plus `runs`, how many battles to simulate (100 by default)
61
+ * @returns {ReturnType<typeof getCombatStatistics>} The averaged outcome, see `getCombatStatistics`
62
+ */
63
+ declare function simulateCombats(attacker: Parameters<typeof simulateCombat>[0], defender: Parameters<typeof simulateCombat>[1], options?: Parameters<typeof simulateCombat>[2] & {
64
+ runs?: number;
65
+ }): ReturnType<typeof getCombatStatistics>;
66
+ export { getCombatStatistics, simulateCombats, DEFAULT_RUNS };
67
+ //# sourceMappingURL=combatStatistics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combatStatistics.d.ts","sourceRoot":"","sources":["../../src/fleets/combatStatistics.js"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,aAAa,CAAC;AAEzC,gFAAgF;AAChF,QAAA,MAAM,YAAY,MAAM,CAAC;AAoEzB;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,iBAAS,mBAAmB,CAAC,OAAO,EAlBzB,OAAO,aAAa,EAAE,YAAY,EAkBT,GAjBvB;IACR,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE;QACR,SAAS,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;QAAC,MAAM,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;QAC1F,aAAa,EAAE,OAAO,aAAa,EAAE,SAAS,CAAC;KAChD,CAAC;IACF,QAAQ,EAAE;QACR,SAAS,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;QAAC,MAAM,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;QAC1F,OAAO,EAAE,OAAO,aAAa,EAAE,UAAU,EAAE,CAAC;QAAC,aAAa,EAAE,OAAO,aAAa,EAAE,SAAS,CAAC;KAC7F,CAAC;IACF,MAAM,EAAE,OAAO,aAAa,EAAE,SAAS,CAAC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,aAAa,EAAE,SAAS,CAAC;CAC1C,CAiEH;AAED;;;;;;;;;;GAUG;AACH,iBAAS,eAAe,CAAC,QAAQ,EANtB,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAMb,EAAE,QAAQ,EALhC,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAKH,EAAE,OAAO,AAJjD,CAEA,EAFQ,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,GAAG;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAIP,GAF5C,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAgBlD;AAED,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC"}
@@ -2,7 +2,8 @@ import getDebris from './getDebris.js';
2
2
  import getDistance from './distance.js';
3
3
  import getShipSpeed, { getActiveDrive, getFleetSpeed } from './speed.js';
4
4
  import { getFlightTime, getFuelConsumption, getTrip } from './flight.js';
5
- import simulateCombat from './combat.js';
5
+ import simulateCombat, { DEFAULT_REPAIR_FACTOR, DEFAULT_PLUNDER_RATIO } from './combat.js';
6
+ import { getCombatStatistics, simulateCombats, DEFAULT_RUNS } from './combatStatistics.js';
6
7
  import { getWaveChance, getMoonbreakWaves, getMoonbreakChance, getMoonbreakLosses, MIN_MOON_SIZE, MAX_MOON_SIZE, WAVES_PER_ATTACKER } from './moonbreak.js';
7
8
  import { getExpeditionFindBase, getExpeditionMaxFind, getCargoCapacity, EXPEDITION_FIND_TIERS, EXPEDITION_TOP_TIER_BASE } from './expedition.js';
8
9
  import { getMoonLockShips, MOON_DEBRIS_THRESHOLD, MAX_MOON_CHANCE } from './moonLock.js';
@@ -16,6 +17,8 @@ declare const Fleets: {
16
17
  getFuelConsumption: typeof getFuelConsumption;
17
18
  getTrip: typeof getTrip;
18
19
  simulateCombat: typeof simulateCombat;
20
+ simulateCombats: typeof simulateCombats;
21
+ getCombatStatistics: typeof getCombatStatistics;
19
22
  getWaveChance: typeof getWaveChance;
20
23
  getMoonbreakWaves: typeof getMoonbreakWaves;
21
24
  getMoonbreakChance: typeof getMoonbreakChance;
@@ -25,6 +28,6 @@ declare const Fleets: {
25
28
  getCargoCapacity: typeof getCargoCapacity;
26
29
  getMoonLockShips: typeof getMoonLockShips;
27
30
  };
28
- export { MIN_MOON_SIZE, MAX_MOON_SIZE, WAVES_PER_ATTACKER, EXPEDITION_FIND_TIERS, EXPEDITION_TOP_TIER_BASE, MOON_DEBRIS_THRESHOLD, MAX_MOON_CHANCE, };
31
+ export { MIN_MOON_SIZE, MAX_MOON_SIZE, WAVES_PER_ATTACKER, EXPEDITION_FIND_TIERS, EXPEDITION_TOP_TIER_BASE, MOON_DEBRIS_THRESHOLD, MAX_MOON_CHANCE, DEFAULT_REPAIR_FACTOR, DEFAULT_PLUNDER_RATIO, DEFAULT_RUNS, };
29
32
  export default Fleets;
30
33
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fleets/index.js"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,WAAW,MAAM,eAAe,CAAC;AACxC,OAAO,YAAY,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,cAAc,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEzF,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;CAkBX,CAAC;AAEF,OAAO,EACL,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,GAChB,CAAC;eAEa,MAAM"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fleets/index.js"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,gBAAgB,CAAC;AACvC,OAAO,WAAW,MAAM,eAAe,CAAC;AACxC,OAAO,YAAY,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACzE,OAAO,cAAc,EAAE,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC3F,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,kBAAkB,EACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEzF,QAAA,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;CAoBX,CAAC;AAEF,OAAO,EACL,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,GACb,CAAC;eAEa,MAAM"}
package/types/index.d.ts CHANGED
@@ -31,6 +31,8 @@ declare const Ogame: {
31
31
  getFuelConsumption: typeof import("./fleets/flight.js").getFuelConsumption;
32
32
  getTrip: typeof import("./fleets/flight.js").getTrip;
33
33
  simulateCombat: typeof import("./fleets/combat.js").default;
34
+ simulateCombats: typeof import("./fleets/combatStatistics.js").simulateCombats;
35
+ getCombatStatistics: typeof import("./fleets/combatStatistics.js").getCombatStatistics;
34
36
  getWaveChance: typeof import("./fleets/moonbreak.js").getWaveChance;
35
37
  getMoonbreakWaves: typeof import("./fleets/moonbreak.js").getMoonbreakWaves;
36
38
  getMoonbreakChance: typeof import("./fleets/moonbreak.js").getMoonbreakChance;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAOA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAS,MAAM,WAAW,CAAC;AAEvD,QAAA,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgBV,CAAC;eAEa,KAAK"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":"AAOA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAS,MAAM,WAAW,CAAC;AAEvD,QAAA,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgBV,CAAC;eAEa,KAAK"}