ogamejs 4.0.2 → 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 +85 -8
- package/package.json +1 -1
- package/src/fleets/combat.js +145 -36
- package/src/fleets/combatStatistics.js +189 -0
- package/src/fleets/expedition.js +124 -0
- package/src/fleets/index.js +42 -1
- package/src/fleets/moonLock.js +31 -0
- package/src/fleets/moonbreak.js +133 -0
- package/types/fleets/combat.d.ts +88 -24
- package/types/fleets/combat.d.ts.map +1 -1
- package/types/fleets/combatStatistics.d.ts +67 -0
- package/types/fleets/combatStatistics.d.ts.map +1 -0
- package/types/fleets/expedition.d.ts +90 -0
- package/types/fleets/expedition.d.ts.map +1 -0
- package/types/fleets/index.d.ts +16 -1
- package/types/fleets/index.d.ts.map +1 -1
- package/types/fleets/moonLock.d.ts +18 -0
- package/types/fleets/moonLock.d.ts.map +1 -0
- package/types/fleets/moonbreak.d.ts +64 -0
- package/types/fleets/moonbreak.d.ts.map +1 -0
- package/types/index.d.ts +10 -0
- package/types/index.d.ts.map +1 -1
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The maximum metal an expedition can find, tiered by the score of the
|
|
3
|
+
* universe's top player. `below` is an exclusive upper bound; above the last
|
|
4
|
+
* tier every universe shares `EXPEDITION_TOP_TIER_BASE`.
|
|
5
|
+
*/
|
|
6
|
+
const EXPEDITION_FIND_TIERS = Object.freeze([
|
|
7
|
+
Object.freeze({ below: 1e4, base: 40000 }),
|
|
8
|
+
Object.freeze({ below: 1e5, base: 500000 }),
|
|
9
|
+
Object.freeze({ below: 1e6, base: 1200000 }),
|
|
10
|
+
Object.freeze({ below: 5e6, base: 1800000 }),
|
|
11
|
+
Object.freeze({ below: 25e6, base: 2400000 }),
|
|
12
|
+
Object.freeze({ below: 50e6, base: 3000000 }),
|
|
13
|
+
Object.freeze({ below: 75e6, base: 3600000 }),
|
|
14
|
+
Object.freeze({ below: 100e6, base: 4200000 }),
|
|
15
|
+
]);
|
|
16
|
+
const EXPEDITION_TOP_TIER_BASE = 5000000;
|
|
17
|
+
|
|
18
|
+
/** The Discoverer class bonus on expedition finds, `explorerBonusIncreasedExpeditionOutcome` in serverData. */
|
|
19
|
+
const DEFAULT_EXPLORER_BONUS = 0.5;
|
|
20
|
+
|
|
21
|
+
/** The share of base cargo each Hyperspace Technology level adds, `cargoHyperspaceTechMultiplier` in serverData. */
|
|
22
|
+
const DEFAULT_HYPERSPACE_MULTIPLIER = 5;
|
|
23
|
+
|
|
24
|
+
function assertBonus(name, value) {
|
|
25
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
26
|
+
throw new Error(`${name} must be a number >= 0, received ${value}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
* Return the expedition find base of a universe, before any multiplier
|
|
33
|
+
* @param {number} topScore The points of the universe's top player
|
|
34
|
+
* @return {number} The base, in metal
|
|
35
|
+
*/
|
|
36
|
+
function getExpeditionFindBase(topScore) {
|
|
37
|
+
if (!Number.isFinite(topScore) || topScore < 0) {
|
|
38
|
+
throw new Error(`topScore must be a number >= 0, received ${topScore}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const tier = EXPEDITION_FIND_TIERS.find(({ below }) => topScore < below);
|
|
42
|
+
return tier ? tier.base : EXPEDITION_TOP_TIER_BASE;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* Return the largest metal find a single expedition can bring back
|
|
48
|
+
*
|
|
49
|
+
* `base * classFactor * (pathfinder ? 2 : 1) * (1 + lifeformResourceBonus)`
|
|
50
|
+
*
|
|
51
|
+
* - a Discoverer gets `classFactor = (1 + explorerBonus * (1 + lifeformExplorerBonus)) * economySpeed`:
|
|
52
|
+
* the lifeform class enhancement amplifies the class bonus itself, and only
|
|
53
|
+
* the Discoverer's finds scale with the economy speed;
|
|
54
|
+
* - any other class gets `classFactor = 1`.
|
|
55
|
+
*
|
|
56
|
+
* Crystal and deuterium finds are smaller than metal finds. Every bonus is a
|
|
57
|
+
* fraction: 0.2 for the 20 % the in-game lifeform bonus page shows.
|
|
58
|
+
* @param {object} options The universe and the player's bonuses
|
|
59
|
+
* @param {number} options.topScore The points of the universe's top player
|
|
60
|
+
* @param {number} options.economySpeed The universe economy speed
|
|
61
|
+
* @param {boolean} [options.explorer] Whether the player is a Discoverer
|
|
62
|
+
* @param {boolean} [options.pathfinder] Whether the fleet holds a Pathfinder
|
|
63
|
+
* @param {number} [options.explorerBonus] The Discoverer class bonus, 0.5 by default
|
|
64
|
+
* @param {number} [options.lifeformExplorerBonus] The lifeform Discoverer enhancement
|
|
65
|
+
* @param {number} [options.lifeformResourceBonus] The lifeform expedition resource bonus
|
|
66
|
+
* @return {number} The maximum find, in metal
|
|
67
|
+
*/
|
|
68
|
+
function getExpeditionMaxFind({
|
|
69
|
+
topScore,
|
|
70
|
+
economySpeed,
|
|
71
|
+
explorer = false,
|
|
72
|
+
pathfinder = false,
|
|
73
|
+
explorerBonus = DEFAULT_EXPLORER_BONUS,
|
|
74
|
+
lifeformExplorerBonus = 0,
|
|
75
|
+
lifeformResourceBonus = 0,
|
|
76
|
+
}) {
|
|
77
|
+
if (!(economySpeed > 0)) {
|
|
78
|
+
throw new Error(`economySpeed must be greater than 0, received ${economySpeed}`);
|
|
79
|
+
}
|
|
80
|
+
assertBonus('explorerBonus', explorerBonus);
|
|
81
|
+
assertBonus('lifeformExplorerBonus', lifeformExplorerBonus);
|
|
82
|
+
assertBonus('lifeformResourceBonus', lifeformResourceBonus);
|
|
83
|
+
|
|
84
|
+
const classFactor = explorer
|
|
85
|
+
? (1 + explorerBonus * (1 + lifeformExplorerBonus)) * economySpeed
|
|
86
|
+
: 1;
|
|
87
|
+
|
|
88
|
+
return getExpeditionFindBase(topScore) * classFactor * (pathfinder ? 2 : 1) * (1 + lifeformResourceBonus);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
*
|
|
93
|
+
* Return the cargo capacity of one ship, with its bonuses
|
|
94
|
+
*
|
|
95
|
+
* `ship.cargo * (1 + hyperspaceLevel * hyperspaceMultiplier / 100 + bonus)`:
|
|
96
|
+
* every bonus is a share of the base cargo, so they add up.
|
|
97
|
+
* @param {import('../types.js').DestroyableEntry} ship An entry of models/destroyable.js
|
|
98
|
+
* @param {object} [options] The bonuses
|
|
99
|
+
* @param {number} [options.hyperspaceLevel] The Hyperspace Technology level
|
|
100
|
+
* @param {number} [options.hyperspaceMultiplier] The % of base cargo per level, 5 by default
|
|
101
|
+
* @param {number} [options.bonus] Any other bonus as a fraction: class (Collector 0.25 on cargo ships), lifeform…
|
|
102
|
+
* @return {number} The capacity
|
|
103
|
+
*/
|
|
104
|
+
function getCargoCapacity(ship, {
|
|
105
|
+
hyperspaceLevel = 0,
|
|
106
|
+
hyperspaceMultiplier = DEFAULT_HYPERSPACE_MULTIPLIER,
|
|
107
|
+
bonus = 0,
|
|
108
|
+
} = {}) {
|
|
109
|
+
if (!Number.isInteger(hyperspaceLevel) || hyperspaceLevel < 0) {
|
|
110
|
+
throw new Error(`hyperspaceLevel must be an integer >= 0, received ${hyperspaceLevel}`);
|
|
111
|
+
}
|
|
112
|
+
assertBonus('hyperspaceMultiplier', hyperspaceMultiplier);
|
|
113
|
+
assertBonus('bonus', bonus);
|
|
114
|
+
|
|
115
|
+
return ship.cargo * (1 + (hyperspaceLevel * hyperspaceMultiplier) / 100 + bonus);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export {
|
|
119
|
+
getExpeditionFindBase,
|
|
120
|
+
getExpeditionMaxFind,
|
|
121
|
+
getCargoCapacity,
|
|
122
|
+
EXPEDITION_FIND_TIERS,
|
|
123
|
+
EXPEDITION_TOP_TIER_BASE,
|
|
124
|
+
};
|
package/src/fleets/index.js
CHANGED
|
@@ -2,7 +2,25 @@ 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';
|
|
7
|
+
import {
|
|
8
|
+
getWaveChance,
|
|
9
|
+
getMoonbreakWaves,
|
|
10
|
+
getMoonbreakChance,
|
|
11
|
+
getMoonbreakLosses,
|
|
12
|
+
MIN_MOON_SIZE,
|
|
13
|
+
MAX_MOON_SIZE,
|
|
14
|
+
WAVES_PER_ATTACKER,
|
|
15
|
+
} from './moonbreak.js';
|
|
16
|
+
import {
|
|
17
|
+
getExpeditionFindBase,
|
|
18
|
+
getExpeditionMaxFind,
|
|
19
|
+
getCargoCapacity,
|
|
20
|
+
EXPEDITION_FIND_TIERS,
|
|
21
|
+
EXPEDITION_TOP_TIER_BASE,
|
|
22
|
+
} from './expedition.js';
|
|
23
|
+
import { getMoonLockShips, MOON_DEBRIS_THRESHOLD, MAX_MOON_CHANCE } from './moonLock.js';
|
|
6
24
|
|
|
7
25
|
const Fleets = {
|
|
8
26
|
getDebris,
|
|
@@ -14,6 +32,29 @@ const Fleets = {
|
|
|
14
32
|
getFuelConsumption,
|
|
15
33
|
getTrip,
|
|
16
34
|
simulateCombat,
|
|
35
|
+
simulateCombats,
|
|
36
|
+
getCombatStatistics,
|
|
37
|
+
getWaveChance,
|
|
38
|
+
getMoonbreakWaves,
|
|
39
|
+
getMoonbreakChance,
|
|
40
|
+
getMoonbreakLosses,
|
|
41
|
+
getExpeditionFindBase,
|
|
42
|
+
getExpeditionMaxFind,
|
|
43
|
+
getCargoCapacity,
|
|
44
|
+
getMoonLockShips,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
MIN_MOON_SIZE,
|
|
49
|
+
MAX_MOON_SIZE,
|
|
50
|
+
WAVES_PER_ATTACKER,
|
|
51
|
+
EXPEDITION_FIND_TIERS,
|
|
52
|
+
EXPEDITION_TOP_TIER_BASE,
|
|
53
|
+
MOON_DEBRIS_THRESHOLD,
|
|
54
|
+
MAX_MOON_CHANCE,
|
|
55
|
+
DEFAULT_REPAIR_FACTOR,
|
|
56
|
+
DEFAULT_PLUNDER_RATIO,
|
|
57
|
+
DEFAULT_RUNS,
|
|
17
58
|
};
|
|
18
59
|
|
|
19
60
|
export default Fleets;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** The debris that gives a moon its maximum chance to appear. */
|
|
2
|
+
const MOON_DEBRIS_THRESHOLD = 2000000;
|
|
3
|
+
|
|
4
|
+
/** The maximum moon chance, in %, reached at `MOON_DEBRIS_THRESHOLD`. */
|
|
5
|
+
const MAX_MOON_CHANCE = 20;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* Return how many ships must be destroyed on a position to reach the maximum moon chance
|
|
10
|
+
*
|
|
11
|
+
* Only metal and crystal land in the debris field here, and the universe keeps
|
|
12
|
+
* `debrisFactor` of them.
|
|
13
|
+
* @param {import('../types.js').DestroyableEntry} ship An entry of models/destroyable.js
|
|
14
|
+
* @param {number} debrisFactor The universe debris factor, e.g. 0.3 for 30%
|
|
15
|
+
* @param {number} [threshold] The debris to reach, 2 000 000 by default
|
|
16
|
+
* @return {number} The number of ships
|
|
17
|
+
*/
|
|
18
|
+
function getMoonLockShips(ship, debrisFactor, threshold = MOON_DEBRIS_THRESHOLD) {
|
|
19
|
+
if (!(debrisFactor > 0)) {
|
|
20
|
+
throw new Error(`debrisFactor must be greater than 0, received ${debrisFactor}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const debris = (ship.cost.metal ?? 0) + (ship.cost.crystal ?? 0);
|
|
24
|
+
if (!(debris > 0)) {
|
|
25
|
+
throw new Error('the ship leaves no metal or crystal debris');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return Math.ceil(threshold / (debrisFactor * debris));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export { getMoonLockShips, MOON_DEBRIS_THRESHOLD, MAX_MOON_CHANCE };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/** A moon size, in km, can only be in this range. */
|
|
2
|
+
const MIN_MOON_SIZE = 3464;
|
|
3
|
+
const MAX_MOON_SIZE = 8944;
|
|
4
|
+
|
|
5
|
+
/** Each attacker spreads their Deathstars over this many waves. */
|
|
6
|
+
const WAVES_PER_ATTACKER = 6;
|
|
7
|
+
|
|
8
|
+
function assertMoonSize(moonSize) {
|
|
9
|
+
if (!Number.isFinite(moonSize) || moonSize < MIN_MOON_SIZE || moonSize > MAX_MOON_SIZE) {
|
|
10
|
+
throw new Error(`moonSize must be between ${MIN_MOON_SIZE} and ${MAX_MOON_SIZE}, received ${moonSize}`);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function assertFleets(fleets) {
|
|
15
|
+
if (!Array.isArray(fleets) || fleets.length === 0) {
|
|
16
|
+
throw new Error('fleets must be a non-empty array of Deathstar counts');
|
|
17
|
+
}
|
|
18
|
+
for (const rip of fleets) {
|
|
19
|
+
if (!Number.isInteger(rip) || rip < 0) {
|
|
20
|
+
throw new Error(`each fleet must be an integer >= 0, received ${rip}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
*
|
|
27
|
+
* Return the chance that a single wave of Deathstars destroys the moon
|
|
28
|
+
*
|
|
29
|
+
* `(100 - sqrt(moonSize)) * sqrt(rip) / 100`, capped at 1
|
|
30
|
+
* @param {number} moonSize The moon diameter, in km
|
|
31
|
+
* @param {number} rip The number of Deathstars in the wave
|
|
32
|
+
* @return {number} The chance, between 0 and 1
|
|
33
|
+
*/
|
|
34
|
+
function getWaveChance(moonSize, rip) {
|
|
35
|
+
return Math.min(1, ((100 - Math.sqrt(moonSize)) * Math.sqrt(rip)) / 100);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
*
|
|
40
|
+
* Return the waves one attacker sends, in firing order
|
|
41
|
+
*
|
|
42
|
+
* The Deathstars are spread over 6 waves: when the count is not a multiple of 6,
|
|
43
|
+
* the first waves carry one extra ship. Under 6 Deathstars, the last waves are
|
|
44
|
+
* empty, which neither threaten the moon nor lose a ship.
|
|
45
|
+
* @param {number} rip The attacker's Deathstars
|
|
46
|
+
* @return {number[]} The size of each of the 6 waves
|
|
47
|
+
*/
|
|
48
|
+
function getMoonbreakWaves(rip) {
|
|
49
|
+
const base = Math.floor(rip / WAVES_PER_ATTACKER);
|
|
50
|
+
const remainder = rip % WAVES_PER_ATTACKER;
|
|
51
|
+
|
|
52
|
+
return Array.from({ length: WAVES_PER_ATTACKER }, (_, i) => (i < remainder ? base + 1 : base));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
*
|
|
57
|
+
* Return the chance that a coordinated attack destroys the moon
|
|
58
|
+
*
|
|
59
|
+
* The attackers fire one after the other and the moon is gone as soon as one
|
|
60
|
+
* wave succeeds, so the attack fails only when every wave fails.
|
|
61
|
+
* @param {number} moonSize The moon diameter, in km (3464 to 8944)
|
|
62
|
+
* @param {number[]} fleets The Deathstars of each attacker, in firing order
|
|
63
|
+
* @return {number} The chance, between 0 and 1
|
|
64
|
+
*/
|
|
65
|
+
function getMoonbreakChance(moonSize, fleets) {
|
|
66
|
+
assertMoonSize(moonSize);
|
|
67
|
+
assertFleets(fleets);
|
|
68
|
+
|
|
69
|
+
const failure = fleets
|
|
70
|
+
.flatMap(getMoonbreakWaves)
|
|
71
|
+
.reduce((acc, rip) => acc * (1 - getWaveChance(moonSize, rip)), 1);
|
|
72
|
+
|
|
73
|
+
return 1 - failure;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
*
|
|
78
|
+
* Return the expected Deathstar losses of a coordinated attack
|
|
79
|
+
*
|
|
80
|
+
* Each Deathstar of a wave is destroyed with probability `sqrt(moonSize) / 200`,
|
|
81
|
+
* and a wave only costs ships if every wave before it failed. The variance sums
|
|
82
|
+
* the per-wave binomial variances (the waves are treated as independent), and
|
|
83
|
+
* the spread is summarised as a gaussian around the mean: 1σ / 2σ / 3σ for the
|
|
84
|
+
* 68 / 95 / 99 % bands, clamped between 0 and the whole fleet.
|
|
85
|
+
* @param {number} moonSize The moon diameter, in km (3464 to 8944)
|
|
86
|
+
* @param {number[]} fleets The Deathstars of each attacker, in firing order
|
|
87
|
+
* @return {{
|
|
88
|
+
* mean: number, sigma: number,
|
|
89
|
+
* bands: { confidence: number, min: number, max: number }[],
|
|
90
|
+
* }} The losses, unrounded
|
|
91
|
+
*/
|
|
92
|
+
function getMoonbreakLosses(moonSize, fleets) {
|
|
93
|
+
assertMoonSize(moonSize);
|
|
94
|
+
assertFleets(fleets);
|
|
95
|
+
|
|
96
|
+
const destructionRate = Math.sqrt(moonSize) / 200;
|
|
97
|
+
const total = fleets.reduce((acc, rip) => acc + rip, 0);
|
|
98
|
+
|
|
99
|
+
let mean = 0;
|
|
100
|
+
let variance = 0;
|
|
101
|
+
// The chance that every wave so far has failed, so that this one fires.
|
|
102
|
+
let reached = 1;
|
|
103
|
+
|
|
104
|
+
for (const size of fleets.flatMap(getMoonbreakWaves)) {
|
|
105
|
+
const p = destructionRate * reached;
|
|
106
|
+
mean += size * p;
|
|
107
|
+
variance += size * p * (1 - p);
|
|
108
|
+
reached *= 1 - getWaveChance(moonSize, size);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const sigma = Math.sqrt(variance);
|
|
112
|
+
const band = (confidence, sigmas) => ({
|
|
113
|
+
confidence,
|
|
114
|
+
min: Math.max(mean - sigmas * sigma, 0),
|
|
115
|
+
max: Math.min(mean + sigmas * sigma, total),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
mean,
|
|
120
|
+
sigma,
|
|
121
|
+
bands: [band(68, 1), band(95, 2), band(99, 3)],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export {
|
|
126
|
+
getWaveChance,
|
|
127
|
+
getMoonbreakWaves,
|
|
128
|
+
getMoonbreakChance,
|
|
129
|
+
getMoonbreakLosses,
|
|
130
|
+
MIN_MOON_SIZE,
|
|
131
|
+
MAX_MOON_SIZE,
|
|
132
|
+
WAVES_PER_ATTACKER,
|
|
133
|
+
};
|
package/types/fleets/combat.d.ts
CHANGED
|
@@ -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
|
|
13
|
-
* may explode
|
|
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;
|
|
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
|
-
* @
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* }
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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":"
|
|
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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The maximum metal an expedition can find, tiered by the score of the
|
|
3
|
+
* universe's top player. `below` is an exclusive upper bound; above the last
|
|
4
|
+
* tier every universe shares `EXPEDITION_TOP_TIER_BASE`.
|
|
5
|
+
*/
|
|
6
|
+
declare const EXPEDITION_FIND_TIERS: readonly (Readonly<{
|
|
7
|
+
below: 10000;
|
|
8
|
+
base: 40000;
|
|
9
|
+
}> | Readonly<{
|
|
10
|
+
below: 100000;
|
|
11
|
+
base: 500000;
|
|
12
|
+
}> | Readonly<{
|
|
13
|
+
below: 1000000;
|
|
14
|
+
base: 1200000;
|
|
15
|
+
}> | Readonly<{
|
|
16
|
+
below: 5000000;
|
|
17
|
+
base: 1800000;
|
|
18
|
+
}> | Readonly<{
|
|
19
|
+
below: 25000000;
|
|
20
|
+
base: 2400000;
|
|
21
|
+
}> | Readonly<{
|
|
22
|
+
below: 50000000;
|
|
23
|
+
base: 3000000;
|
|
24
|
+
}> | Readonly<{
|
|
25
|
+
below: 75000000;
|
|
26
|
+
base: 3600000;
|
|
27
|
+
}> | Readonly<{
|
|
28
|
+
below: 100000000;
|
|
29
|
+
base: 4200000;
|
|
30
|
+
}>)[];
|
|
31
|
+
declare const EXPEDITION_TOP_TIER_BASE = 5000000;
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
* Return the expedition find base of a universe, before any multiplier
|
|
35
|
+
* @param {number} topScore The points of the universe's top player
|
|
36
|
+
* @return {number} The base, in metal
|
|
37
|
+
*/
|
|
38
|
+
declare function getExpeditionFindBase(topScore: number): number;
|
|
39
|
+
/**
|
|
40
|
+
*
|
|
41
|
+
* Return the largest metal find a single expedition can bring back
|
|
42
|
+
*
|
|
43
|
+
* `base * classFactor * (pathfinder ? 2 : 1) * (1 + lifeformResourceBonus)`
|
|
44
|
+
*
|
|
45
|
+
* - a Discoverer gets `classFactor = (1 + explorerBonus * (1 + lifeformExplorerBonus)) * economySpeed`:
|
|
46
|
+
* the lifeform class enhancement amplifies the class bonus itself, and only
|
|
47
|
+
* the Discoverer's finds scale with the economy speed;
|
|
48
|
+
* - any other class gets `classFactor = 1`.
|
|
49
|
+
*
|
|
50
|
+
* Crystal and deuterium finds are smaller than metal finds. Every bonus is a
|
|
51
|
+
* fraction: 0.2 for the 20 % the in-game lifeform bonus page shows.
|
|
52
|
+
* @param {object} options The universe and the player's bonuses
|
|
53
|
+
* @param {number} options.topScore The points of the universe's top player
|
|
54
|
+
* @param {number} options.economySpeed The universe economy speed
|
|
55
|
+
* @param {boolean} [options.explorer] Whether the player is a Discoverer
|
|
56
|
+
* @param {boolean} [options.pathfinder] Whether the fleet holds a Pathfinder
|
|
57
|
+
* @param {number} [options.explorerBonus] The Discoverer class bonus, 0.5 by default
|
|
58
|
+
* @param {number} [options.lifeformExplorerBonus] The lifeform Discoverer enhancement
|
|
59
|
+
* @param {number} [options.lifeformResourceBonus] The lifeform expedition resource bonus
|
|
60
|
+
* @return {number} The maximum find, in metal
|
|
61
|
+
*/
|
|
62
|
+
declare function getExpeditionMaxFind({ topScore, economySpeed, explorer, pathfinder, explorerBonus, lifeformExplorerBonus, lifeformResourceBonus, }: {
|
|
63
|
+
topScore: number;
|
|
64
|
+
economySpeed: number;
|
|
65
|
+
explorer?: boolean;
|
|
66
|
+
pathfinder?: boolean;
|
|
67
|
+
explorerBonus?: number;
|
|
68
|
+
lifeformExplorerBonus?: number;
|
|
69
|
+
lifeformResourceBonus?: number;
|
|
70
|
+
}): number;
|
|
71
|
+
/**
|
|
72
|
+
*
|
|
73
|
+
* Return the cargo capacity of one ship, with its bonuses
|
|
74
|
+
*
|
|
75
|
+
* `ship.cargo * (1 + hyperspaceLevel * hyperspaceMultiplier / 100 + bonus)`:
|
|
76
|
+
* every bonus is a share of the base cargo, so they add up.
|
|
77
|
+
* @param {import('../types.js').DestroyableEntry} ship An entry of models/destroyable.js
|
|
78
|
+
* @param {object} [options] The bonuses
|
|
79
|
+
* @param {number} [options.hyperspaceLevel] The Hyperspace Technology level
|
|
80
|
+
* @param {number} [options.hyperspaceMultiplier] The % of base cargo per level, 5 by default
|
|
81
|
+
* @param {number} [options.bonus] Any other bonus as a fraction: class (Collector 0.25 on cargo ships), lifeform…
|
|
82
|
+
* @return {number} The capacity
|
|
83
|
+
*/
|
|
84
|
+
declare function getCargoCapacity(ship: import('../types.js').DestroyableEntry, { hyperspaceLevel, hyperspaceMultiplier, bonus, }?: {
|
|
85
|
+
hyperspaceLevel?: number;
|
|
86
|
+
hyperspaceMultiplier?: number;
|
|
87
|
+
bonus?: number;
|
|
88
|
+
}): number;
|
|
89
|
+
export { getExpeditionFindBase, getExpeditionMaxFind, getCargoCapacity, EXPEDITION_FIND_TIERS, EXPEDITION_TOP_TIER_BASE, };
|
|
90
|
+
//# sourceMappingURL=expedition.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expedition.d.ts","sourceRoot":"","sources":["../../src/fleets/expedition.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;KASzB,CAAC;AACH,QAAA,MAAM,wBAAwB,UAAU,CAAC;AAczC;;;;;GAKG;AACH,iBAAS,qBAAqB,CAAC,QAAQ,EAH5B,MAG4B,GAF3B,MAAM,CASjB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,iBAAS,oBAAoB,CAAC,EAC5B,QAAQ,EACR,YAAY,EACZ,QAAgB,EAChB,UAAkB,EAClB,aAAsC,EACtC,qBAAyB,EACzB,qBAAyB,GAC1B,EAjBE;IAAwB,QAAQ,EAAxB,MAAM,CACd;IAAwB,YAAY,EAA5B,MAAM,CACd;IAA0B,QAAQ,AAAlC,CACA,EADQ,OAAO,CACf;IAA0B,UAAU,AAApC,CACA,EADQ,OAAO,CACf;IAAyB,aAAa,AAAtC,CACA,EADQ,MAAM,CACd;IAAyB,qBAAqB,AAA9C,CACA,EADQ,MAAM,CACd;IAAyB,qBAAqB,AAA9C,CACA,EADQ,MAAM,CACd;CAUF,GAVW,MAAM,CAuBjB;AAED;;;;;;;;;;;;GAYG;AACH,iBAAS,gBAAgB,CAAC,IAAI,EAPnB,OAAO,aAAa,EAAE,gBAOH,EAAE,EAC9B,eAAmB,EACnB,oBAAoD,EACpD,KAAS,GACV,AAVE,CAIA,EAHA;IAAyB,eAAe,AAAxC,CACA,EADQ,MAAM,CACd;IAAyB,oBAAoB,AAA7C,CACA,EADQ,MAAM,CACd;IAAyB,KAAK,AAA9B,CACA,EADQ,MAAM,CACd;CAMG,GANM,MAAM,CAcjB;AAED,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,wBAAwB,GACzB,CAAC"}
|