@shipload/sdk 1.0.0-next.51 → 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.
Files changed (58) hide show
  1. package/lib/shipload.d.ts +470 -157
  2. package/lib/shipload.js +2309 -492
  3. package/lib/shipload.js.map +1 -1
  4. package/lib/shipload.m.js +2279 -491
  5. package/lib/shipload.m.js.map +1 -1
  6. package/lib/testing.d.ts +123 -8
  7. package/lib/testing.js +450 -19
  8. package/lib/testing.js.map +1 -1
  9. package/lib/testing.m.js +451 -20
  10. package/lib/testing.m.js.map +1 -1
  11. package/package.json +1 -1
  12. package/src/capabilities/crafting.ts +10 -1
  13. package/src/capabilities/gathering.ts +1 -1
  14. package/src/capabilities/hauling.ts +0 -5
  15. package/src/capabilities/modules.ts +6 -0
  16. package/src/capabilities/movement.ts +1 -1
  17. package/src/contracts/server.ts +293 -13
  18. package/src/data/capabilities.ts +11 -2
  19. package/src/data/entities.json +235 -20
  20. package/src/data/item-ids.ts +10 -0
  21. package/src/data/items.json +61 -0
  22. package/src/data/kind-registry.json +53 -1
  23. package/src/data/kind-registry.ts +5 -0
  24. package/src/data/metadata.ts +74 -5
  25. package/src/data/recipes-runtime.ts +1 -0
  26. package/src/data/recipes.json +887 -118
  27. package/src/derivation/capabilities.test.ts +81 -4
  28. package/src/derivation/capabilities.ts +162 -57
  29. package/src/derivation/crafting.ts +2 -0
  30. package/src/derivation/recipe-usage.test.ts +10 -7
  31. package/src/derivation/rollups.ts +16 -0
  32. package/src/derivation/stat-scaling.ts +12 -0
  33. package/src/entities/makers.ts +10 -1
  34. package/src/index-module.ts +29 -3
  35. package/src/managers/actions.ts +77 -46
  36. package/src/managers/construction-types.ts +2 -2
  37. package/src/managers/construction.ts +15 -15
  38. package/src/managers/plot.ts +1 -1
  39. package/src/nft/buildImmutableData.ts +55 -9
  40. package/src/nft/description.ts +99 -36
  41. package/src/planner/planner.test.ts +50 -41
  42. package/src/resolution/resolve-item.test.ts +1 -1
  43. package/src/resolution/resolve-item.ts +26 -11
  44. package/src/scheduling/availability.ts +7 -6
  45. package/src/scheduling/cancel.test.ts +40 -6
  46. package/src/scheduling/cancel.ts +21 -29
  47. package/src/scheduling/jobs.ts +71 -0
  48. package/src/scheduling/lanes.ts +29 -0
  49. package/src/scheduling/projection.ts +42 -25
  50. package/src/scheduling/task-cargo.ts +1 -1
  51. package/src/testing/projection-parity.ts +2 -2
  52. package/src/travel/reach.ts +4 -6
  53. package/src/travel/route-planner.ts +60 -49
  54. package/src/travel/route-simulator.ts +3 -7
  55. package/src/travel/travel.ts +14 -5
  56. package/src/types/capabilities.ts +9 -3
  57. package/src/types.ts +5 -3
  58. 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
- const heuristic = (c: Coord): number => Math.ceil(dist(c, dest) / perLegReach)
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; g: number; f: number; remaining: number}[] = [
73
- {coord: origin, g: 0, f: heuristic(origin), remaining: straightLine},
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 tentativeG = current.g + 1
109
- if (tentativeG > maxLegs) {
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
- if (tentativeG < (gScore.get(nk) ?? Infinity)) {
115
- gScore.set(nk, tentativeG)
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
- g: tentativeG,
121
- f: tentativeG + Math.ceil(remaining / perLegReach),
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
- let scanProvider: ScanProvider | null = null
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
- const cached = graphCache.get(seedHex)
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 = scanProvider ? wasmSystemGraph(s, seedHex, scanProvider) : jsSystemGraph(s)
198
- graphCache.set(seedHex, graph)
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 {
@@ -12,7 +12,7 @@ export interface RouteMoverInput {
12
12
  hasMovement: boolean
13
13
  engines?: {thrust: number; drain: number}
14
14
  generator?: {capacity: number; recharge: number}
15
- hauler?: {capacity: number; drain: number; efficiency: number}
15
+ hauler?: {capacity: number; efficiency: number}
16
16
  mass: number
17
17
  energy: number
18
18
  priorMobilityEnd: number
@@ -80,18 +80,14 @@ export function simulateRoute(
80
80
  for (const to of waypoints) {
81
81
  const distance = distanceBetweenPoints(from.x, from.y, to.x, to.y)
82
82
  const distanceNum = Number(distance)
83
- const distanceCells = Math.trunc(distanceNum / PRECISION)
83
+ const distanceCells = distanceNum / PRECISION
84
84
 
85
85
  const energyCostByMover: Record<string, number> = {}
86
86
 
87
87
  for (const m of movers) {
88
88
  if (!m.hasMovement || !m.engines) continue
89
89
  const key = String(m.ref.entityId)
90
- let cost = Number(calc_energyusage(distance, m.engines.drain))
91
- if (m.hauler) {
92
- cost += Math.trunc(distanceCells) * m.hauler.drain * haulCount
93
- }
94
- energyCostByMover[key] = cost
90
+ energyCostByMover[key] = Number(calc_energyusage(distance, m.engines.drain))
95
91
  }
96
92
 
97
93
  let rechargeBefore = false
@@ -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,
@@ -197,7 +196,7 @@ export function calc_ship_rechargetime(ship: ShipLike): UInt32 {
197
196
  if (!ship.generator) return UInt32.from(0)
198
197
  return calc_rechargetime(
199
198
  ship.generator.capacity,
200
- ship.energy ?? UInt16.from(0),
199
+ ship.energy ?? UInt32.from(0),
201
200
  ship.generator.recharge
202
201
  )
203
202
  }
@@ -206,6 +205,16 @@ export function calc_flighttime(distance: UInt64Type, acceleration: number): UIn
206
205
  return UInt32.from(2 * Math.sqrt(Number(distance) / acceleration))
207
206
  }
208
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
+
209
218
  export function calc_transit_duration(ax: number, ay: number, bx: number, by: number): UInt32 {
210
219
  const distance = distanceBetweenPoints(ax, ay, bx, by)
211
220
  return UInt32.from(Math.floor(distance.toNumber() / (PRECISION * WH.TRANSIT_SPEED)))
@@ -235,7 +244,7 @@ export function calc_loader_acceleration(ship: ShipLike, mass: UInt64): number {
235
244
 
236
245
  export function calc_ship_flighttime(ship: ShipLike, mass: UInt64, distance: UInt64): UInt32 {
237
246
  const acceleration = calc_ship_acceleration(ship, mass)
238
- return calc_flighttime(distance, acceleration)
247
+ return calc_travel_flighttime(distance, acceleration)
239
248
  }
240
249
 
241
250
  export function calc_ship_acceleration(ship: ShipLike, mass: UInt64): number {
@@ -286,11 +295,11 @@ export function calc_group_flighttime(
286
295
  effectiveThrust = Math.trunc((totalThrust * 1000) / penaltyMilli)
287
296
  }
288
297
  const acceleration = calc_acceleration(effectiveThrust, totalMass)
289
- return calc_flighttime(distance, acceleration)
298
+ return calc_travel_flighttime(distance, acceleration)
290
299
  }
291
300
 
292
301
  export function calc_energyusage(distance: UInt64Type, drain: UInt32Type): UInt32 {
293
- return UInt64.from(distance).dividing(PRECISION).multiplying(drain)
302
+ return UInt64.from(distance).multiplying(drain).dividing(PRECISION)
294
303
  }
295
304
 
296
305
  export function calculateTransferTime(
@@ -1,4 +1,4 @@
1
- import type {Name, UInt16, UInt32, UInt8} from '@wharfkit/antelope'
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: UInt16
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?: UInt16
74
+ energy?: UInt32
69
75
  cargomass: UInt32
70
76
  cargo: ServerContract.Types.cargo_item[]
71
77
  }
package/src/types.ts CHANGED
@@ -38,7 +38,7 @@ export interface ClusterSlotType {
38
38
  export interface ShipLike {
39
39
  coordinates: ServerContract.Types.coordinates
40
40
  hullmass?: UInt32
41
- energy?: UInt16
41
+ energy?: UInt32
42
42
  engines?: ServerContract.Types.movement_stats
43
43
  generator?: ServerContract.Types.energy_stats
44
44
  loader_lanes?: ServerContract.Types.loader_lane[]
@@ -60,15 +60,15 @@ export enum TaskType {
60
60
  GATHER = 5,
61
61
  WARP = 6,
62
62
  CRAFT = 7,
63
- DEPLOY = 8,
64
63
  TRANSIT = 9,
65
64
  UNWRAP = 10,
66
65
  UNDEPLOY = 11,
66
+ LAUNCH = 12,
67
67
  DEMOLISH = 13,
68
- CLAIMPLOT = 14,
69
68
  BUILDPLOT = 15,
70
69
  CHARGE = 16,
71
70
  UPGRADE = 17,
71
+ SHUTTLE = 18,
72
72
  }
73
73
 
74
74
  export enum HoldKind {
@@ -76,6 +76,7 @@ export enum HoldKind {
76
76
  PUSH = 2,
77
77
  GATHER = 3,
78
78
  BUILD = 4,
79
+ FLIGHT = 5,
79
80
  UPGRADE = 6,
80
81
  }
81
82
 
@@ -138,6 +139,7 @@ export type ModuleType =
138
139
  | 'loader'
139
140
  | 'warp'
140
141
  | 'crafter'
142
+ | 'builder'
141
143
  | 'launcher'
142
144
  | 'storage'
143
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
- })