@shipload/sdk 1.0.0-next.50 → 1.0.0-next.52
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/lib/shipload.d.ts +544 -149
- package/lib/shipload.js +2424 -448
- package/lib/shipload.js.map +1 -1
- package/lib/shipload.m.js +2390 -447
- package/lib/shipload.m.js.map +1 -1
- package/lib/testing.d.ts +131 -7
- package/lib/testing.js +469 -11
- package/lib/testing.js.map +1 -1
- package/lib/testing.m.js +470 -12
- package/lib/testing.m.js.map +1 -1
- package/package.json +1 -1
- package/src/capabilities/crafting.ts +10 -1
- package/src/capabilities/gathering.ts +1 -1
- package/src/capabilities/hauling.ts +0 -5
- package/src/capabilities/modules.ts +6 -0
- package/src/capabilities/movement.ts +1 -1
- package/src/contracts/server.ts +315 -8
- package/src/data/capabilities.ts +11 -2
- package/src/data/entities.json +235 -20
- package/src/data/item-ids.ts +10 -0
- package/src/data/items.json +61 -0
- package/src/data/kind-registry.json +53 -1
- package/src/data/kind-registry.ts +5 -0
- package/src/data/metadata.ts +74 -5
- package/src/data/recipes-runtime.ts +1 -0
- package/src/data/recipes.json +887 -118
- package/src/derivation/capabilities.test.ts +81 -4
- package/src/derivation/capabilities.ts +162 -57
- package/src/derivation/crafting.ts +2 -0
- package/src/derivation/recipe-usage.test.ts +10 -7
- package/src/derivation/rollups.ts +16 -0
- package/src/derivation/stat-scaling.ts +12 -0
- package/src/entities/makers.ts +10 -1
- package/src/index-module.ts +33 -3
- package/src/managers/actions.ts +89 -44
- package/src/managers/construction-types.ts +2 -2
- package/src/managers/construction.ts +15 -15
- package/src/managers/plot.ts +1 -1
- package/src/nft/buildImmutableData.ts +55 -9
- package/src/nft/description.ts +99 -36
- package/src/planner/planner.test.ts +50 -41
- package/src/resolution/resolve-item.test.ts +1 -1
- package/src/resolution/resolve-item.ts +26 -11
- package/src/scheduling/availability.ts +7 -6
- package/src/scheduling/cancel.test.ts +40 -6
- package/src/scheduling/cancel.ts +21 -29
- package/src/scheduling/jobs.ts +71 -0
- package/src/scheduling/lanes.ts +29 -0
- package/src/scheduling/projection.ts +42 -25
- package/src/scheduling/task-cargo.ts +1 -1
- package/src/testing/projection-parity.ts +2 -2
- package/src/travel/reach.ts +4 -6
- package/src/travel/route-planner.ts +60 -49
- package/src/travel/route-simulator.ts +170 -0
- package/src/travel/travel.ts +40 -5
- package/src/types/capabilities.ts +9 -3
- package/src/types.ts +8 -3
- package/src/managers/flatten-gather-plan.test.ts +0 -80
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {distanceBetweenPoints, findNearbyPlanets} from './travel'
|
|
2
|
-
import {hasSystem} from '../utils/system'
|
|
3
1
|
import {nearbyWormholes, wormholeAt} from '../derivation/wormhole'
|
|
4
|
-
import {PRECISION} from '../types'
|
|
5
2
|
import {Checksum256, type Checksum256Type} from '@wharfkit/antelope'
|
|
6
3
|
|
|
7
4
|
export interface Coord {
|
|
@@ -26,6 +23,19 @@ export interface RoutePlan {
|
|
|
26
23
|
totalDistance: number
|
|
27
24
|
}
|
|
28
25
|
|
|
26
|
+
export interface RouteLegInput {
|
|
27
|
+
from: Coord
|
|
28
|
+
to: Coord
|
|
29
|
+
distance: number
|
|
30
|
+
isDestination: boolean
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Returns the elapsed-time search cost for a leg, or null when the leg is not contract-feasible.
|
|
35
|
+
*/
|
|
36
|
+
export type RouteLegCost = (leg: RouteLegInput) => number | null
|
|
37
|
+
export type RouteHeuristicCost = (from: Coord, dest: Coord) => number
|
|
38
|
+
|
|
29
39
|
export type RouteFailureReason = 'empty-destination' | 'no-path' | 'max-legs'
|
|
30
40
|
|
|
31
41
|
export interface RouteFailure {
|
|
@@ -46,6 +56,8 @@ export interface PlanRouteParams {
|
|
|
46
56
|
corridorSlack?: number
|
|
47
57
|
nodeBudget?: number
|
|
48
58
|
maxLegs?: number
|
|
59
|
+
legCost?: RouteLegCost
|
|
60
|
+
heuristicCost?: RouteHeuristicCost
|
|
49
61
|
}
|
|
50
62
|
|
|
51
63
|
const key = (c: Coord): string => `${c.x},${c.y}`
|
|
@@ -65,12 +77,25 @@ export function planRoute(params: PlanRouteParams): RouteResult {
|
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
const straightLine = dist(origin, dest)
|
|
68
|
-
|
|
80
|
+
if (straightLine <= perLegReach) {
|
|
81
|
+
const directCost = params.legCost?.({
|
|
82
|
+
from: origin,
|
|
83
|
+
to: dest,
|
|
84
|
+
distance: straightLine,
|
|
85
|
+
isDestination: true,
|
|
86
|
+
})
|
|
87
|
+
if (directCost !== null) {
|
|
88
|
+
return {ok: true, waypoints: [dest], legs: 1, totalDistance: straightLine}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const heuristic = (c: Coord): number =>
|
|
92
|
+
params.heuristicCost?.(c, dest) ??
|
|
93
|
+
(params.legCost ? 0 : Math.ceil(dist(c, dest) / perLegReach))
|
|
69
94
|
|
|
70
95
|
const gScore = new Map<string, number>([[key(origin), 0]])
|
|
71
96
|
const cameFrom = new Map<string, Coord>()
|
|
72
|
-
const frontier: {coord: Coord;
|
|
73
|
-
{coord: origin,
|
|
97
|
+
const frontier: {coord: Coord; legs: number; cost: number; f: number; remaining: number}[] = [
|
|
98
|
+
{coord: origin, legs: 0, cost: 0, f: heuristic(origin), remaining: straightLine},
|
|
74
99
|
]
|
|
75
100
|
|
|
76
101
|
let furthest = origin
|
|
@@ -88,6 +113,7 @@ export function planRoute(params: PlanRouteParams): RouteResult {
|
|
|
88
113
|
}
|
|
89
114
|
}
|
|
90
115
|
const current = frontier.splice(bestIdx, 1)[0]
|
|
116
|
+
if (current.cost !== gScore.get(key(current.coord))) continue
|
|
91
117
|
|
|
92
118
|
if (sameCoord(current.coord, dest)) {
|
|
93
119
|
return reconstruct(cameFrom, origin, dest)
|
|
@@ -105,20 +131,31 @@ export function planRoute(params: PlanRouteParams): RouteResult {
|
|
|
105
131
|
dist(origin, n.coord) + dist(n.coord, dest) <= straightLine + corridorSlack
|
|
106
132
|
if (!inCorridor) continue
|
|
107
133
|
|
|
108
|
-
const
|
|
109
|
-
if (
|
|
134
|
+
const tentativeLegs = current.legs + 1
|
|
135
|
+
if (tentativeLegs > maxLegs) {
|
|
110
136
|
cappedByMaxLegs = true
|
|
111
137
|
continue
|
|
112
138
|
}
|
|
139
|
+
const evaluatedCost = params.legCost?.({
|
|
140
|
+
from: current.coord,
|
|
141
|
+
to: n.coord,
|
|
142
|
+
distance: n.dist,
|
|
143
|
+
isDestination: sameCoord(n.coord, dest),
|
|
144
|
+
})
|
|
145
|
+
if (evaluatedCost === null) continue
|
|
146
|
+
const legCost = evaluatedCost ?? 1
|
|
147
|
+
if (!(legCost >= 0) || !Number.isFinite(legCost)) continue
|
|
113
148
|
const nk = key(n.coord)
|
|
114
|
-
|
|
115
|
-
|
|
149
|
+
const nextCost = current.cost + legCost
|
|
150
|
+
if (nextCost < (gScore.get(nk) ?? Infinity)) {
|
|
151
|
+
gScore.set(nk, nextCost)
|
|
116
152
|
cameFrom.set(nk, current.coord)
|
|
117
153
|
const remaining = dist(n.coord, dest)
|
|
118
154
|
frontier.push({
|
|
119
155
|
coord: n.coord,
|
|
120
|
-
|
|
121
|
-
|
|
156
|
+
legs: tentativeLegs,
|
|
157
|
+
cost: nextCost,
|
|
158
|
+
f: nextCost + heuristic(n.coord),
|
|
122
159
|
remaining,
|
|
123
160
|
})
|
|
124
161
|
}
|
|
@@ -180,50 +217,24 @@ export interface ScanProvider {
|
|
|
180
217
|
): {x: number; y: number; locType: number}[]
|
|
181
218
|
}
|
|
182
219
|
|
|
183
|
-
|
|
184
|
-
const graphCache = new Map<string, SystemGraph
|
|
185
|
-
|
|
186
|
-
// Inject a fast (e.g. wasm) location-type backend; null restores the pure-JS path. Clears the graph cache.
|
|
187
|
-
export function setScanProvider(provider: ScanProvider | null): void {
|
|
188
|
-
scanProvider = provider
|
|
189
|
-
graphCache.clear()
|
|
190
|
-
}
|
|
220
|
+
// Keyed by provider identity, so pass a stable one (the `@shipload/sdk/scan` namespace).
|
|
221
|
+
const graphCache = new WeakMap<ScanProvider, Map<string, SystemGraph>>()
|
|
191
222
|
|
|
192
|
-
export function sdkSystemGraph(seed: Checksum256Type): SystemGraph {
|
|
223
|
+
export function sdkSystemGraph(seed: Checksum256Type, scan: ScanProvider): SystemGraph {
|
|
193
224
|
const s = Checksum256.from(seed)
|
|
194
225
|
const seedHex = s.toString()
|
|
195
|
-
|
|
226
|
+
let bySeed = graphCache.get(scan)
|
|
227
|
+
if (!bySeed) {
|
|
228
|
+
bySeed = new Map()
|
|
229
|
+
graphCache.set(scan, bySeed)
|
|
230
|
+
}
|
|
231
|
+
const cached = bySeed.get(seedHex)
|
|
196
232
|
if (cached) return cached
|
|
197
|
-
const graph =
|
|
198
|
-
|
|
233
|
+
const graph = wasmSystemGraph(s, seedHex, scan)
|
|
234
|
+
bySeed.set(seedHex, graph)
|
|
199
235
|
return graph
|
|
200
236
|
}
|
|
201
237
|
|
|
202
|
-
// Travelable nodes mirror the contract's is_travelable: systems plus wormhole mouths.
|
|
203
|
-
function jsSystemGraph(s: Checksum256): SystemGraph {
|
|
204
|
-
return {
|
|
205
|
-
hasSystem: (c) => hasSystem(s, {x: c.x, y: c.y}) || wormholeAt(s, c.x, c.y) !== null,
|
|
206
|
-
nearby: (c, reachTiles) => {
|
|
207
|
-
const seen = new Set<string>([`${c.x},${c.y}`])
|
|
208
|
-
const out: Neighbor[] = []
|
|
209
|
-
for (const d of findNearbyPlanets(s, {x: c.x, y: c.y}, reachTiles * PRECISION)) {
|
|
210
|
-
const coord = {x: Number(d.destination.x), y: Number(d.destination.y)}
|
|
211
|
-
const k = `${coord.x},${coord.y}`
|
|
212
|
-
if (seen.has(k)) continue
|
|
213
|
-
seen.add(k)
|
|
214
|
-
out.push({coord, dist: Number(d.distance) / PRECISION})
|
|
215
|
-
}
|
|
216
|
-
for (const coord of nearbyWormholes(s, c.x, c.y, reachTiles)) {
|
|
217
|
-
const k = `${coord.x},${coord.y}`
|
|
218
|
-
if (seen.has(k)) continue
|
|
219
|
-
seen.add(k)
|
|
220
|
-
out.push({coord, dist: Math.hypot(coord.x - c.x, coord.y - c.y)})
|
|
221
|
-
}
|
|
222
|
-
return out
|
|
223
|
-
},
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
238
|
const SCAN_BUCKET = 48
|
|
228
239
|
|
|
229
240
|
function wasmSystemGraph(s: Checksum256, seedHex: string, scan: ScanProvider): SystemGraph {
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type {UInt64Type} from '@wharfkit/antelope'
|
|
2
|
+
import {PRECISION, TRAVEL_MAX_DURATION} from '../types'
|
|
3
|
+
import {
|
|
4
|
+
calc_energyusage,
|
|
5
|
+
calc_group_flighttime,
|
|
6
|
+
calc_rechargetime,
|
|
7
|
+
distanceBetweenPoints,
|
|
8
|
+
} from './travel'
|
|
9
|
+
|
|
10
|
+
export interface RouteMoverInput {
|
|
11
|
+
ref: {entityType: string; entityId: UInt64Type}
|
|
12
|
+
hasMovement: boolean
|
|
13
|
+
engines?: {thrust: number; drain: number}
|
|
14
|
+
generator?: {capacity: number; recharge: number}
|
|
15
|
+
hauler?: {capacity: number; efficiency: number}
|
|
16
|
+
mass: number
|
|
17
|
+
energy: number
|
|
18
|
+
priorMobilityEnd: number
|
|
19
|
+
narrowBarrierEnd: number
|
|
20
|
+
/** Max end (seconds-from-now) over all this entity's lanes incl. worker/craft; gates recharges like the contract's all_lanes_end. Default 0. */
|
|
21
|
+
allLanesEnd?: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface RouteLegSim {
|
|
25
|
+
from: {x: number; y: number}
|
|
26
|
+
to: {x: number; y: number}
|
|
27
|
+
distanceCells: number
|
|
28
|
+
energyCostByMover: Record<string, number>
|
|
29
|
+
rechargeBefore: boolean
|
|
30
|
+
rechargeSeconds: number
|
|
31
|
+
flightSeconds: number
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface RouteSim {
|
|
35
|
+
legs: RouteLegSim[]
|
|
36
|
+
totalSeconds: number
|
|
37
|
+
reachable: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function simulateRoute(
|
|
41
|
+
movers: RouteMoverInput[],
|
|
42
|
+
waypoints: {x: number; y: number}[],
|
|
43
|
+
origin: {x: number; y: number},
|
|
44
|
+
recharge: boolean
|
|
45
|
+
): RouteSim {
|
|
46
|
+
const totalThrust = movers
|
|
47
|
+
.filter((m) => m.hasMovement && m.engines)
|
|
48
|
+
.reduce((sum, m) => sum + m.engines!.thrust, 0)
|
|
49
|
+
|
|
50
|
+
const totalMass = movers.reduce((sum, m) => sum + m.mass, 0)
|
|
51
|
+
|
|
52
|
+
const haulCount = movers.filter((m) => !m.hasMovement).length
|
|
53
|
+
|
|
54
|
+
const pooledHaulCap = movers
|
|
55
|
+
.filter((m) => m.hasMovement && m.hauler)
|
|
56
|
+
.reduce((sum, m) => sum + m.hauler!.capacity, 0)
|
|
57
|
+
|
|
58
|
+
const weightedHaulEffNum = movers
|
|
59
|
+
.filter((m) => m.hasMovement && m.hauler)
|
|
60
|
+
.reduce((sum, m) => sum + m.hauler!.efficiency * m.hauler!.capacity, 0)
|
|
61
|
+
|
|
62
|
+
const energyByMover: Map<string, number> = new Map(
|
|
63
|
+
movers.map((m) => [String(m.ref.entityId), m.energy])
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
let reachable = true
|
|
67
|
+
const legs: RouteLegSim[] = []
|
|
68
|
+
|
|
69
|
+
const mobilityBarrier = movers
|
|
70
|
+
.filter((m) => m.hasMovement)
|
|
71
|
+
.reduce((mx, m) => Math.max(mx, m.priorMobilityEnd, m.narrowBarrierEnd), 0)
|
|
72
|
+
|
|
73
|
+
const rechargeFloor = movers
|
|
74
|
+
.filter((m) => m.hasMovement && m.generator)
|
|
75
|
+
.reduce((mx, m) => Math.max(mx, m.allLanesEnd ?? 0), 0)
|
|
76
|
+
|
|
77
|
+
let clock = 0
|
|
78
|
+
|
|
79
|
+
let from = origin
|
|
80
|
+
for (const to of waypoints) {
|
|
81
|
+
const distance = distanceBetweenPoints(from.x, from.y, to.x, to.y)
|
|
82
|
+
const distanceNum = Number(distance)
|
|
83
|
+
const distanceCells = distanceNum / PRECISION
|
|
84
|
+
|
|
85
|
+
const energyCostByMover: Record<string, number> = {}
|
|
86
|
+
|
|
87
|
+
for (const m of movers) {
|
|
88
|
+
if (!m.hasMovement || !m.engines) continue
|
|
89
|
+
const key = String(m.ref.entityId)
|
|
90
|
+
energyCostByMover[key] = Number(calc_energyusage(distance, m.engines.drain))
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let rechargeBefore = false
|
|
94
|
+
let rechargeSeconds = 0
|
|
95
|
+
|
|
96
|
+
if (recharge) {
|
|
97
|
+
let maxDur = 0
|
|
98
|
+
for (const m of movers) {
|
|
99
|
+
if (!m.hasMovement || !m.generator) continue
|
|
100
|
+
const key = String(m.ref.entityId)
|
|
101
|
+
const curEnergy = energyByMover.get(key) ?? 0
|
|
102
|
+
const dur = Number(
|
|
103
|
+
calc_rechargetime(m.generator.capacity, curEnergy, m.generator.recharge)
|
|
104
|
+
)
|
|
105
|
+
if (dur > maxDur) maxDur = dur
|
|
106
|
+
}
|
|
107
|
+
rechargeSeconds = maxDur
|
|
108
|
+
rechargeBefore = rechargeSeconds > 0
|
|
109
|
+
|
|
110
|
+
if (rechargeBefore) {
|
|
111
|
+
for (const m of movers) {
|
|
112
|
+
if (!m.hasMovement || !m.generator) continue
|
|
113
|
+
const key = String(m.ref.entityId)
|
|
114
|
+
energyByMover.set(key, m.generator.capacity)
|
|
115
|
+
}
|
|
116
|
+
clock = Math.max(clock, rechargeFloor) + rechargeSeconds
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for (const m of movers) {
|
|
121
|
+
if (!m.hasMovement || !m.engines) continue
|
|
122
|
+
const key = String(m.ref.entityId)
|
|
123
|
+
const cost = energyCostByMover[key] ?? 0
|
|
124
|
+
const curEnergy = energyByMover.get(key) ?? 0
|
|
125
|
+
|
|
126
|
+
if (recharge && m.generator) {
|
|
127
|
+
if (cost > m.generator.capacity) reachable = false
|
|
128
|
+
} else {
|
|
129
|
+
if (cost > curEnergy) reachable = false
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const flightSeconds = Number(
|
|
134
|
+
calc_group_flighttime(
|
|
135
|
+
totalThrust,
|
|
136
|
+
haulCount,
|
|
137
|
+
pooledHaulCap,
|
|
138
|
+
weightedHaulEffNum,
|
|
139
|
+
totalMass,
|
|
140
|
+
distance
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if (flightSeconds > TRAVEL_MAX_DURATION) reachable = false
|
|
145
|
+
|
|
146
|
+
for (const m of movers) {
|
|
147
|
+
if (!m.hasMovement || !m.engines) continue
|
|
148
|
+
const key = String(m.ref.entityId)
|
|
149
|
+
const cost = energyCostByMover[key] ?? 0
|
|
150
|
+
const curEnergy = energyByMover.get(key) ?? 0
|
|
151
|
+
energyByMover.set(key, Math.max(0, curEnergy - cost))
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
clock = Math.max(clock, mobilityBarrier) + flightSeconds
|
|
155
|
+
|
|
156
|
+
legs.push({
|
|
157
|
+
from,
|
|
158
|
+
to,
|
|
159
|
+
distanceCells,
|
|
160
|
+
energyCostByMover,
|
|
161
|
+
rechargeBefore,
|
|
162
|
+
rechargeSeconds,
|
|
163
|
+
flightSeconds,
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
from = to
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return {legs, totalSeconds: clock, reachable}
|
|
170
|
+
}
|
package/src/travel/travel.ts
CHANGED
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
type Checksum256,
|
|
15
15
|
Int64,
|
|
16
16
|
type Int64Type,
|
|
17
|
-
UInt16,
|
|
18
17
|
UInt32,
|
|
19
18
|
type UInt32Type,
|
|
20
19
|
UInt64,
|
|
@@ -23,9 +22,11 @@ import {
|
|
|
23
22
|
|
|
24
23
|
import type {ServerContract} from '../contracts'
|
|
25
24
|
import {
|
|
25
|
+
BASE_HAUL_PENALTY_MILLI,
|
|
26
26
|
BASE_ORBITAL_MASS,
|
|
27
27
|
type CargoMassInfo,
|
|
28
28
|
type Distance,
|
|
29
|
+
HAULER_EFFICIENCY_DENOM,
|
|
29
30
|
MAX_ORBITAL_ALTITUDE,
|
|
30
31
|
MIN_ORBITAL_ALTITUDE,
|
|
31
32
|
MIN_TRANSFER_DISTANCE_ORBITAL_VESSEL,
|
|
@@ -187,14 +188,15 @@ export function calc_rechargetime(
|
|
|
187
188
|
const cap = UInt32.from(capacity)
|
|
188
189
|
const eng = UInt32.from(energy)
|
|
189
190
|
if (eng.gte(cap)) return UInt32.zero
|
|
190
|
-
|
|
191
|
+
const ticks = cap.subtracting(eng).dividing(recharge)
|
|
192
|
+
return ticks.equals(UInt32.zero) ? UInt32.from(1) : ticks
|
|
191
193
|
}
|
|
192
194
|
|
|
193
195
|
export function calc_ship_rechargetime(ship: ShipLike): UInt32 {
|
|
194
196
|
if (!ship.generator) return UInt32.from(0)
|
|
195
197
|
return calc_rechargetime(
|
|
196
198
|
ship.generator.capacity,
|
|
197
|
-
ship.energy ??
|
|
199
|
+
ship.energy ?? UInt32.from(0),
|
|
198
200
|
ship.generator.recharge
|
|
199
201
|
)
|
|
200
202
|
}
|
|
@@ -203,6 +205,16 @@ export function calc_flighttime(distance: UInt64Type, acceleration: number): UIn
|
|
|
203
205
|
return UInt32.from(2 * Math.sqrt(Number(distance) / acceleration))
|
|
204
206
|
}
|
|
205
207
|
|
|
208
|
+
export function calc_travel_flighttime(distance: UInt64Type, acceleration: number): UInt32 {
|
|
209
|
+
const cruiseTransitionDistance = 2 * PRECISION
|
|
210
|
+
if (Number(distance) <= cruiseTransitionDistance) {
|
|
211
|
+
return calc_flighttime(distance, acceleration)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const cruiseVelocity = Math.sqrt(acceleration * cruiseTransitionDistance)
|
|
215
|
+
return UInt32.from(Number(distance) / cruiseVelocity + cruiseVelocity / acceleration)
|
|
216
|
+
}
|
|
217
|
+
|
|
206
218
|
export function calc_transit_duration(ax: number, ay: number, bx: number, by: number): UInt32 {
|
|
207
219
|
const distance = distanceBetweenPoints(ax, ay, bx, by)
|
|
208
220
|
return UInt32.from(Math.floor(distance.toNumber() / (PRECISION * WH.TRANSIT_SPEED)))
|
|
@@ -232,7 +244,7 @@ export function calc_loader_acceleration(ship: ShipLike, mass: UInt64): number {
|
|
|
232
244
|
|
|
233
245
|
export function calc_ship_flighttime(ship: ShipLike, mass: UInt64, distance: UInt64): UInt32 {
|
|
234
246
|
const acceleration = calc_ship_acceleration(ship, mass)
|
|
235
|
-
return
|
|
247
|
+
return calc_travel_flighttime(distance, acceleration)
|
|
236
248
|
}
|
|
237
249
|
|
|
238
250
|
export function calc_ship_acceleration(ship: ShipLike, mass: UInt64): number {
|
|
@@ -263,8 +275,31 @@ export function calc_ship_mass(ship: ShipLike, cargos: CargoMassInfo[]): UInt64
|
|
|
263
275
|
return mass
|
|
264
276
|
}
|
|
265
277
|
|
|
278
|
+
export function calc_group_flighttime(
|
|
279
|
+
totalThrust: number,
|
|
280
|
+
haulCount: number,
|
|
281
|
+
pooledHaulCap: number,
|
|
282
|
+
weightedHaulEffNum: number,
|
|
283
|
+
totalMass: number,
|
|
284
|
+
distance: UInt64Type
|
|
285
|
+
): UInt32 {
|
|
286
|
+
const avgHaulEff = pooledHaulCap > 0 ? Math.trunc(weightedHaulEffNum / pooledHaulCap) : 0
|
|
287
|
+
let effectiveThrust = totalThrust
|
|
288
|
+
if (haulCount > 0) {
|
|
289
|
+
const penaltyMilli =
|
|
290
|
+
1000 +
|
|
291
|
+
Math.trunc(
|
|
292
|
+
(haulCount * BASE_HAUL_PENALTY_MILLI * (HAULER_EFFICIENCY_DENOM - avgHaulEff)) /
|
|
293
|
+
HAULER_EFFICIENCY_DENOM
|
|
294
|
+
)
|
|
295
|
+
effectiveThrust = Math.trunc((totalThrust * 1000) / penaltyMilli)
|
|
296
|
+
}
|
|
297
|
+
const acceleration = calc_acceleration(effectiveThrust, totalMass)
|
|
298
|
+
return calc_travel_flighttime(distance, acceleration)
|
|
299
|
+
}
|
|
300
|
+
|
|
266
301
|
export function calc_energyusage(distance: UInt64Type, drain: UInt32Type): UInt32 {
|
|
267
|
-
return UInt64.from(distance).
|
|
302
|
+
return UInt64.from(distance).multiplying(drain).dividing(PRECISION)
|
|
268
303
|
}
|
|
269
304
|
|
|
270
305
|
export function calculateTransferTime(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {Name,
|
|
1
|
+
import type {Name, UInt32, UInt8} from '@wharfkit/antelope'
|
|
2
2
|
import type {ServerContract} from '../contracts'
|
|
3
3
|
|
|
4
4
|
export interface LoaderStats {
|
|
@@ -18,13 +18,18 @@ export interface CrafterStats {
|
|
|
18
18
|
drain: {toNumber(): number}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export interface BuilderStats {
|
|
22
|
+
speed: {toNumber(): number}
|
|
23
|
+
drain: {toNumber(): number}
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
export interface MovementCapability {
|
|
22
27
|
engines: ServerContract.Types.movement_stats
|
|
23
28
|
generator: ServerContract.Types.energy_stats
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
export interface EnergyCapability {
|
|
27
|
-
energy:
|
|
32
|
+
energy: UInt32
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
export interface StorageCapability {
|
|
@@ -58,6 +63,7 @@ export interface EntityCapabilities {
|
|
|
58
63
|
loaders?: LoaderStats
|
|
59
64
|
gatherer?: GathererStats
|
|
60
65
|
crafter?: CrafterStats
|
|
66
|
+
builder?: BuilderStats
|
|
61
67
|
hauler?: ServerContract.Types.hauler_stats
|
|
62
68
|
launcher?: ServerContract.Types.launcher_stats
|
|
63
69
|
}
|
|
@@ -65,7 +71,7 @@ export interface EntityCapabilities {
|
|
|
65
71
|
export interface EntityState {
|
|
66
72
|
owner: Name
|
|
67
73
|
location: ServerContract.Types.coordinates
|
|
68
|
-
energy?:
|
|
74
|
+
energy?: UInt32
|
|
69
75
|
cargomass: UInt32
|
|
70
76
|
cargo: ServerContract.Types.cargo_item[]
|
|
71
77
|
}
|
package/src/types.ts
CHANGED
|
@@ -18,6 +18,9 @@ export const CONTAINER_Z = 300
|
|
|
18
18
|
|
|
19
19
|
export const TRAVEL_MAX_DURATION = 86400
|
|
20
20
|
|
|
21
|
+
export const BASE_HAUL_PENALTY_MILLI = 300
|
|
22
|
+
export const HAULER_EFFICIENCY_DENOM = 10000
|
|
23
|
+
|
|
21
24
|
export const MIN_ORBITAL_ALTITUDE = 800
|
|
22
25
|
export const MAX_ORBITAL_ALTITUDE = 3000
|
|
23
26
|
|
|
@@ -35,7 +38,7 @@ export interface ClusterSlotType {
|
|
|
35
38
|
export interface ShipLike {
|
|
36
39
|
coordinates: ServerContract.Types.coordinates
|
|
37
40
|
hullmass?: UInt32
|
|
38
|
-
energy?:
|
|
41
|
+
energy?: UInt32
|
|
39
42
|
engines?: ServerContract.Types.movement_stats
|
|
40
43
|
generator?: ServerContract.Types.energy_stats
|
|
41
44
|
loader_lanes?: ServerContract.Types.loader_lane[]
|
|
@@ -57,15 +60,15 @@ export enum TaskType {
|
|
|
57
60
|
GATHER = 5,
|
|
58
61
|
WARP = 6,
|
|
59
62
|
CRAFT = 7,
|
|
60
|
-
DEPLOY = 8,
|
|
61
63
|
TRANSIT = 9,
|
|
62
64
|
UNWRAP = 10,
|
|
63
65
|
UNDEPLOY = 11,
|
|
66
|
+
LAUNCH = 12,
|
|
64
67
|
DEMOLISH = 13,
|
|
65
|
-
CLAIMPLOT = 14,
|
|
66
68
|
BUILDPLOT = 15,
|
|
67
69
|
CHARGE = 16,
|
|
68
70
|
UPGRADE = 17,
|
|
71
|
+
SHUTTLE = 18,
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
export enum HoldKind {
|
|
@@ -73,6 +76,7 @@ export enum HoldKind {
|
|
|
73
76
|
PUSH = 2,
|
|
74
77
|
GATHER = 3,
|
|
75
78
|
BUILD = 4,
|
|
79
|
+
FLIGHT = 5,
|
|
76
80
|
UPGRADE = 6,
|
|
77
81
|
}
|
|
78
82
|
|
|
@@ -135,6 +139,7 @@ export type ModuleType =
|
|
|
135
139
|
| 'loader'
|
|
136
140
|
| 'warp'
|
|
137
141
|
| 'crafter'
|
|
142
|
+
| 'builder'
|
|
138
143
|
| 'launcher'
|
|
139
144
|
| 'storage'
|
|
140
145
|
| 'hauler'
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import {expect, test, describe} from 'bun:test'
|
|
2
|
-
import {makeClient} from '@wharfkit/mock-data'
|
|
3
|
-
import {Serializer} from '@wharfkit/antelope'
|
|
4
|
-
import type {GatherPlan} from '../planner'
|
|
5
|
-
import {ActionsManager} from './actions'
|
|
6
|
-
import {ServerContract} from '../contracts'
|
|
7
|
-
|
|
8
|
-
const client = makeClient('https://jungle4.greymass.com')
|
|
9
|
-
|
|
10
|
-
function makeActions() {
|
|
11
|
-
const realServer = new ServerContract.Contract({client})
|
|
12
|
-
const stubServer = {
|
|
13
|
-
account: realServer.account,
|
|
14
|
-
action: realServer.action.bind(realServer),
|
|
15
|
-
}
|
|
16
|
-
const context = {server: stubServer} as any
|
|
17
|
-
return new ActionsManager(context)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function fakePlan(): GatherPlan {
|
|
21
|
-
return {
|
|
22
|
-
cycles: [
|
|
23
|
-
{
|
|
24
|
-
rechargeBefore: false,
|
|
25
|
-
rechargeSeconds: 0,
|
|
26
|
-
gatherSeconds: 10,
|
|
27
|
-
batchOre: 30,
|
|
28
|
-
limpets: [
|
|
29
|
-
{slot: 0, quantity: 20, durationSeconds: 10},
|
|
30
|
-
{slot: 1, quantity: 10, durationSeconds: 8},
|
|
31
|
-
],
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
rechargeBefore: true,
|
|
35
|
-
rechargeSeconds: 5,
|
|
36
|
-
gatherSeconds: 10,
|
|
37
|
-
batchOre: 30,
|
|
38
|
-
limpets: [{slot: 0, quantity: 30, durationSeconds: 10}],
|
|
39
|
-
},
|
|
40
|
-
],
|
|
41
|
-
cycleCount: 2,
|
|
42
|
-
totalOre: 60,
|
|
43
|
-
totalSeconds: 25,
|
|
44
|
-
cap: 'requested',
|
|
45
|
-
reachingCount: 2,
|
|
46
|
-
totalLimpets: 2,
|
|
47
|
-
warnings: [],
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
describe('flattenGatherPlan', () => {
|
|
52
|
-
test('flattens cycles to [recharge?, gather x limpets] in order with slots', () => {
|
|
53
|
-
const actions = makeActions().flattenGatherPlan(fakePlan(), {
|
|
54
|
-
sourceId: 1,
|
|
55
|
-
destinationId: 1,
|
|
56
|
-
stratum: 100,
|
|
57
|
-
})
|
|
58
|
-
expect(actions.map((a) => a.name.toString())).toEqual([
|
|
59
|
-
'gather',
|
|
60
|
-
'gather',
|
|
61
|
-
'recharge',
|
|
62
|
-
'gather',
|
|
63
|
-
])
|
|
64
|
-
expect(actions.filter((a) => a.name.toString() === 'recharge').length).toBe(1)
|
|
65
|
-
const gatherSlots = actions
|
|
66
|
-
.filter((a) => a.name.toString() === 'gather')
|
|
67
|
-
.map((a) => {
|
|
68
|
-
const decoded = Serializer.decode({data: a.data, type: ServerContract.Types.gather})
|
|
69
|
-
return Number(decoded.slot)
|
|
70
|
-
})
|
|
71
|
-
expect(gatherSlots).toEqual([0, 1, 0])
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test('empty plan produces no actions', () => {
|
|
75
|
-
const empty = {...fakePlan(), cycles: [], cycleCount: 0, totalOre: 0}
|
|
76
|
-
expect(
|
|
77
|
-
makeActions().flattenGatherPlan(empty, {sourceId: 1, destinationId: 1, stratum: 100})
|
|
78
|
-
).toHaveLength(0)
|
|
79
|
-
})
|
|
80
|
-
})
|