summaryrefslogtreecommitdiff
path: root/nodejs/automation/contracting.ts
blob: 96f9106ed3fe1eac20b09c555f19483d0756f3b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { Contract } from '../lib/types.ts';
import { Ship } from '../lib/ships.ts';
import * as mining from './mining.js';
import * as selling from './selling.js';
import * as dbContracts from '../database/contracts.ts';
import * as contracts from '../lib/contracts.ts';
import * as libSystems from '../lib/systems.ts';
import * as systems from '../lib/systems.ts';
import * as utils from '../lib/utils.ts';

export async function run(ship: Ship): Promise<void> {
	while(true) { // we use the fact that there can only be at most one active contract at a time
		const contracts = dbContracts.getContracts().filter(c => !c.fulfilled);
		let contract: Contract;
		if (contracts.length === 0) {
			contract = await ship.negotiate();
		} else {
			contract = contracts[0];
		}
		await runOne(contract, ship);
		await ship.negotiate();
	}
}

async function runOne(contract: Contract, ship: Ship): Promise<void> {
	await contracts.accept(contract);
	switch(contract.type) {
		case 'PROCUREMENT':
			if (contract.terms.deliver[0].tradeSymbol.match(/_ORE$/)) {
				await runOreProcurement(contract, ship);
			} else {
				await runTradeProcurement(contract, ship);
			}
			break;
		default:
			throw `Handling of contract type ${contract.type} is not implemented yet`;
	}
}

async function runOreProcurement(contract: Contract, ship: Ship): Promise<void> {
	const wantedCargo = contract.terms.deliver[0].tradeSymbol;
	const deliveryPoint = contract.terms.deliver[0].destinationSymbol;
	const asteroids = await systems.type(ship.nav.systemSymbol, 'ENGINEERED_ASTEROID');
	const asteroidSymbol = asteroids[0].symbol;
	while (!contract.fulfilled) {
		const goodCargo = ship.cargo.inventory.filter(i => i.symbol === wantedCargo)[0]
		// what we do depends on where we are
		switch (ship.nav.waypointSymbol) {
			case asteroidSymbol:
				await mining.mineUntilFullFor(contract, ship, asteroidSymbol);
				await ship.navigate(deliveryPoint);
				break;
			case deliveryPoint:
				if (goodCargo !== undefined) { // we could be here if a client restart happens right after selling before we navigate away
					contract = await contracts.deliver(contract, ship);
					if (contract.fulfilled) return;
				}
				await ship.navigate(asteroidSymbol);
				break;
			default:
				await selling.sell(ship, wantedCargo);
				await ship.navigate(asteroidSymbol);
		}
	}
}

async function runTradeProcurement(contract: Contract, ship: Ship): Promise<void> {
	const deliver = contract.terms.deliver[0];
	const deliveryPoint = deliver.destinationSymbol;
	const wantedCargo = deliver.tradeSymbol;
	while (!contract.fulfilled) {
		const goodCargo = ship.cargo.inventory.filter(i => i.symbol === wantedCargo)[0]
		// make sure we are not carrying useless stuff
		await selling.sell(ship, wantedCargo);
		// go buy what we need
		const rawMarkets = await libSystems.trait(ship.nav.systemSymbol, 'MARKETPLACE');
		// sorted by distance from where we are
		const markets = rawMarkets.map(function (m) { return {
			data: m,
			distance: (m.x - ship.nav.route.destination.x) ** 2 + (m.y - ship.nav.route.destination.y) ** 2,
		}});
		markets.sort(function(a, b) {
			if (a.distance < b.distance) {
				return -1;
			} else if (a.distance > b.distance) {
				return 1;
			}
			return 0;
		});
		// check from the closest one that exports what we need
		let buyingPoint: string = "";
		outer: for (let i = 0; i < markets.length; i++) {
			const waypointSymbol = markets[i].data.symbol;
			const market = await libSystems.market(waypointSymbol);
			for (let j = 0; j < market.exports.length; j++) {
				if (market.exports[j].symbol === wantedCargo) {
					buyingPoint = market.symbol;
					break outer;
				}
			}
		}
		// if we did not find an exporting market we look for an exchange
		if (buyingPoint === "") {
			outer: for (let i = 0; i < markets.length; i++) {
				const waypointSymbol = markets[i].data.symbol;
				const market = await libSystems.market(waypointSymbol);
				for (let j = 0; j < market.exchange.length; j++) {
					if (market.exports[j].symbol === wantedCargo) {
						buyingPoint = market.symbol;
						break outer;
					}
				}
			}
		}
		if (buyingPoint === "") {
			throw `runTradeProcurement failed, no market exports or exchanges ${wantedCargo}`;
		}
		// go buy what we need
		await ship.navigate(buyingPoint);
		const units = Math.min(
			deliver.unitsRequired - deliver.unitsFulfilled,
			ship.cargo.capacity - ship.cargo.units,
		);
		await ship.purchase(wantedCargo, units);
		// then make a delivery
		await ship.navigate(deliveryPoint);
		contract = await contracts.deliver(contract, ship);
		if (contract.fulfilled) return;
	}
	console.log("runTradeProcurement not implemented");
	throw "not implemented";
}